由散列表到BitMap的概念與應用(三):面試中的海量數據處理

一道面試題

在面試軟件開發工程師時,常常會遇到海量數據排序和去重的面試題,特別是大數據崗位。html

例1:給定a、b兩個文件,各存放50億個url,每一個url各佔64字節,內存限制是4G,找出a、b文件共同的url?面試

  • 首先咱們最常想到的方法是讀取文件a,創建哈希表,而後再讀取文件b,遍歷文件b中每一個url,對於每一個遍歷,咱們都執行查找hash表的操做,若hash表中搜索到了,則說明兩文件共有,存入一個集合。
  • 但上述方法有一個明顯問題,加載一個文件的數據須要50億*64bytes = 320G遠遠大於4G內存,況且咱們還須要分配哈希表數據結構所使用的空間,因此不可能一次性把文件中全部數據構建一個總體的hash表。

如何解答

針對上述問題,咱們分治算法的思想。算法

step1

遍歷文件a,對每一個url求取hash(url)%1000,而後根據所取得的值將url分別存儲到1000個小文件(記爲a0,a1,...,a999,每一個小文件約300M),爲何是1000?主要根據內存大小和要分治的文件大小來計算,咱們就大體能夠把320G大小分爲1000份,每份大約300M。數組

step2

遍歷文件b,採起和a相同的方式將url分別存儲到1000個小文件(記爲b0,b1,...,b999)。性能優化

文件a的hash映射和文件b的hash映射函數要保持一致,這樣的話相同的url就會保存在對應的小文件中。好比,若是a中有一個url記錄data1被hash到了a99文件中,那麼若是b中也有相同url,則必定被hash到了b99中。微信

因此如今問題轉換成了:找出1000對小文件中每一對相同的url(不對應的小文件不可能有相同的url)數據結構

step3

由於每一個小文件大約300M,因此咱們再能夠採用上面解答中的想法。架構

解題思路

常見的高效解答思路有:堆排序法、分治策略和BitMap(位圖法)。函數

堆排序法

堆排序是4種平均時間複雜度爲nlogn的排序方法之一,其優勢在於當求M個數中的前n個最大數,和最小數的時候性能極好。因此當從海量數據中要找出前m個最大值或最小值,而對其餘值沒有要求時,使用堆排序法效果很好。微服務

來源於https://www.cnblogs.com/gaochundong/p/comparison_sorting_algorithms.html

從1億個整數裏找出100個最大的數

  • 讀取前100個數字,創建最大值堆。
  • 依次讀取餘下的數,與最大值堆做比較,維持最大值堆。能夠每次讀取的數量爲一個磁盤頁面,將每一個頁面的數據依次進堆比較,這樣節省IO時間。
  • 將堆進行排序,便可獲得100個有序最大值。

這裏採用堆排序將空間複雜度講得很低,要排序1億個數,但一次性只需讀取100個數字,或者設置其餘基數,不須要一次性讀完全部數據,下降對內存要求。

分治策略

整體思想:分而治之。經過分治將大數據變成小數據進行處理,再合併。

首先區份內部排序和外部排序:

  • 內部排序:內部排序是指待排序序列能夠所有裝入內存的排序過程,適用於規模較小的元素序列。
  • 外部排序:外部排序是指大文件的排序,即待排序的記錄存儲在外存儲器上,待排序的文件沒法一次裝入內存,須要在內存和外部存儲器之間進行屢次數據交換,才能達到排序整個文件的目的。

步驟:

  • 從大數據中抽取樣本,將須要排序的數據切分爲多個樣本數大體相等的區間
  • 將大數據文件切分爲多個小數據文件,這裏要考慮IO次數和硬件資源問題,例如可將小數據文件數設定爲1G(要預留內存給執行時的程序使用)
  • 使用最優的算法對小數據文件的數據進行排序,將排序結果按照步驟1劃分的區間進行存儲
  • 對各個數據區間內的排序結果文件進行處理,最終每一個區間獲得一個排序結果的文件
  • 將各個區間的排序結果合併

其次要注意待排序數據的特色。若是待排序數據具備某些特色,每每可以有更加有效的方法解決。 同時,這種思想也更加貼近大數據應用的思惟方式。

BitMap(位圖法)

32位機器上,一個整形,好比int a; 在內存中佔32bit位,能夠用對應的32bit位對應十進制的0-31個數,BitMap算法利用這種思想處理大量數據的排序與查詢.

其優勢是運算效率高,不準進行比較和移位,且佔用內存少,好比N=10000000;只需佔用內存爲N/8=1250000Byte=1.25M。

解答示例

例2:在特定的場合下:
對10億個不重複的整數進行排序。
找出10億個數字中重複的數字。

如上的題目通常會限制內存。

咱們換一個與上面示例類似的題目進行演示解答過程。

例3:一臺主機,2G內存,40億個不重複的沒排過序的unsigned int的整數的文件,而後再給一個整數,如何快速判斷這個整數是否在那40億個數當中?

咱們能夠有幾種方法解答如上的題目。

遍歷法

若是內存足夠將40億個數所有放到內存中,逐個遍歷,此時時間複雜度爲O(N)。但是如今在內存不足,須要分批讀一部分數據到內存而後在作判斷,加上I/O操做的時間,時間複雜度遠遠大於O(N)。

這時,性能問題主要集中在I/O操做,和遍歷數組上。那麼有沒有下降時間複雜度的方法呢?答案是確定的,若是咱們假定內存是足夠的,只去優化時間,能夠獲得下面的方法。

直接尋址表法

申請一個4G超大數組char a[0~2^32-1],將文件中出現的數字置爲1,沒有出現的置爲0。 例如文件存在一個整數1000022,就將a[100002211]=1。

a 0 1 2 ... 2^32-1
value 1 0 1 0 0

這時時間複雜度爲O(1),但是空間問題尚未解決。分析下咱們的算法,以所需判斷的整數爲數組下標,用0/1來區分整數是否在。一共用了一個字節來做爲標記位,而事實上1Bit就足夠標記了。若是能把這部分空間優化掉,4G/8 < 2G 那麼就能夠解決問題了。看下面的方法。

BitMap

在前面兩篇文章中,咱們講過BitMap的概念和應用。

將整數映射到bit上,例如整數10,10/8=1,10%8=2,那麼就將a[1]的b[2]置爲1。這樣時間複雜度便是O(1),內存也獲得了壓縮。

a 0 1 2 ... 2^32/8-1
bit 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
flag 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

逆向思惟優化

unsinged int只有接近43億(unsigned int最大值爲2^32-1=4294967295,最大不超過43億),咱們還能夠用某種方式存儲沒有出現過的3億個數(使用數組{大小爲3億中最大的數/8 bytes}存儲),若是出如今3億個數裏面,說明不在40億裏面。3億個數存儲空間通常小於40億個。ps:存儲4294967296(不到43億)須要512MB, 而存儲294967296只須要35.16MB。

這裏須要注意的是,BitMap排序須要的時間複雜度和空間複雜度依賴於數據中最大的數字。當數據相似(1,1000,10萬)只有3個數據的時候,用BitMap時間複雜度和空間複雜度至關大,只有當數據比較密集時纔有優點。

總結

在處理海量數據時,咱們會想到這些數據的存儲結構。在全部具備性能優化的數據結構中,你們使用最多的就是hash表,是的,在具備定位查找上具備O(1)的常量時間,多麼的簡潔優美。可是數據量大了,內存就不夠了。固然也可使用相似外排序來解決問題的,因爲要走IO因此時間上又不行。BitMap基於位的映射,用一個Bit位來標記某個元素對應的Value, 而Key便是該元素。因爲採用了Bit爲單位來存儲數據,所以BitMap在存儲空間方面,能夠大大節省。

本文總結了幾種經常使用的海量數據處理方法,咱們能夠根據實際的題意(空間、時間限制)進行靈活應用。瞭解散列表和BitMap能夠參見前面兩篇文章

最後,歡迎購買筆者的新書《Spring Cloud微服務架構進階》

思考

最後,留一個思考題給你們,和上面的解答過程相似,有興趣的能夠在文章下面留言討論。

例4:現有3G的數據量,數據類型爲整型,找出其中重複的數據。
數據:每一個數據不大於20億,且每一個數據最多重複一次。
內存:最多用300M的內存進行操做。

訂閱最新文章,歡迎關注個人公衆號

微信公衆號

參考

  1. 大數據排序算法:外部排序,bitmap算法;大數據去重算法:hash算法,bitmap算法
  2. 大量數據去重:Bitmap和布隆過濾器(Bloom Filter)
相關文章
相關標籤/搜索