在面試軟件開發工程師時,常常會遇到海量數據排序和去重的面試題,特別是大數據崗位。html
例1:給定a、b兩個文件,各存放50億個url,每一個url各佔64字節,內存限制是4G,找出a、b文件共同的url?面試
針對上述問題,咱們分治算法的思想。算法
遍歷文件a,對每一個url求取hash(url)%1000,而後根據所取得的值將url分別存儲到1000個小文件(記爲a0,a1,...,a999,每一個小文件約300M),爲何是1000?主要根據內存大小和要分治的文件大小來計算,咱們就大體能夠把320G大小分爲1000份,每份大約300M。數組
遍歷文件b,採起和a相同的方式將url分別存儲到1000個小文件(記爲b0,b1,...,b999)。性能優化
文件a的hash映射和文件b的hash映射函數要保持一致,這樣的話相同的url就會保存在對應的小文件中。好比,若是a中有一個url記錄data1被hash到了a99文件中,那麼若是b中也有相同url,則必定被hash到了b99中。微信
因此如今問題轉換成了:找出1000對小文件中每一對相同的url(不對應的小文件不可能有相同的url)數據結構
由於每一個小文件大約300M,因此咱們再能夠採用上面解答中的想法。架構
常見的高效解答思路有:堆排序法、分治策略和BitMap(位圖法)。函數
堆排序是4種平均時間複雜度爲nlogn的排序方法之一,其優勢在於當求M個數中的前n個最大數,和最小數的時候性能極好。因此當從海量數據中要找出前m個最大值或最小值,而對其餘值沒有要求時,使用堆排序法效果很好。微服務
從1億個整數裏找出100個最大的數
這裏採用堆排序將空間複雜度講得很低,要排序1億個數,但一次性只需讀取100個數字,或者設置其餘基數,不須要一次性讀完全部數據,下降對內存要求。
整體思想:分而治之。經過分治將大數據變成小數據進行處理,再合併。
首先區份內部排序和外部排序:
步驟:
其次要注意待排序數據的特色。若是待排序數據具備某些特色,每每可以有更加有效的方法解決。 同時,這種思想也更加貼近大數據應用的思惟方式。
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的概念和應用。
將整數映射到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的內存進行操做。