海量資料處理(4) 外排序演算法

2021-10-02 05:05:09 字數 2826 閱讀 4917

外排序演算法是指能夠處理極大量資料的排序演算法。通常來說,外排序處理的資料不能一次裝入記憶體,只能放在硬碟上。通常採用排序–歸併的策略,將原本的大檔案,拆分為若干個小檔案,小檔案可以讀入記憶體中進行排序,然後使用歸併操作。

因此,外排序通常分為兩個基本步驟:

根據記憶體的大小,盡可能多的分批次的將資料 load 到記憶體中,並使用系統自帶的記憶體排序函式(或者自己寫個快排),將其排好序,並輸出到乙個個小檔案中。比如乙個檔案有1t,記憶體有1g(自己設定jvm引數),那麼我們就這個大檔案中的內容按照 1g 的大小,分批次的匯入記憶體,排序之後輸出得到 1024 個 1g 的小檔案。

小檔案都排完序後,就使用k路歸併演算法合併排序好的檔案。k路歸併使用的是來完成的。我們將 k 個檔案中的第乙個元素加入到堆裡,假設資料是從小到大排序的話,那麼這個堆是乙個最小堆(min heap)。每次從堆中選出最小的元素,輸出到目標結果檔案中,然後如果這個元素來自第 x 個檔案,則從第 x 個檔案中繼續讀入乙個新的數進來放到堆裡,並重複上述操作,直到所有元素都被輸出到目標結果檔案中。

其實這個思路就是和合併k個排序鍊錶/有序陣列是一樣的,leetcode有這個題的,當時就是用堆來做。

leetcode 23.合併k個排序鍊錶

這邊乙個問題是在歸併的過程中,乙個個從檔案中讀入資料,乙個個輸出到目標檔案中操作很慢,如何優化?如果我們每個檔案唯讀入1個元素並放入堆裡的話,總共只用到了 1024 個元素,這很小,沒有充分的利用好記憶體。另外,單個讀入和單個輸出的方式也不是磁碟的高效使用方式。因此我們可以為輸入和輸出都分別加入乙個buffer。假如乙個元素有10個位元組大小的話,1024 個元素一共 10k,1g的記憶體可以支援約 100k 組這樣的資料,那麼我們就為每個檔案設定乙個 100k 大小的 buffer,每次需要從某個檔案中讀資料,都將這個 buffer 裝滿。當然 buffer 中的資料都用完的時候,再批量的從檔案中讀入。輸出同理,設定乙個 buffer 來避免單個輸出帶來的效率緩慢。

總之就是加乙個buffer來改善效能,具體設定多大得看實際情況啦。

問題的具體描述如下:給定a、b兩個檔案,各存放50億個urls,每個 url 各佔 64 位元組,記憶體限制是 4g,讓你找出a、b檔案共同的 urls?

遇到問題首先還是要看有沒有條件需要澄清,這裡主要是乙個問題:這兩個檔案各自是否已經沒有重複?

對於這個問題,通常面試官會先讓你假設沒有重複,然後再來看有重複的情況怎麼處理。

檔案拆分可以叫sharding,也可以叫partition,這是能想到的最簡單的方法,肯定就是要把檔案從拆分,思路**於kafka、es。50億,每個 urls 64 位元組,也就是 320g 大小的檔案。很顯然我們不能直接全部 load 到記憶體中去處理。這種記憶體不夠的問題,通常我們的解決方法都可以是使用 hash function 來將大檔案拆分為若干個小檔案。比如按照hashfunc(url) % 200進行拆分的話,可以拆分成為,200 個小檔案(就是如果 hashfunc(url) % 200 = 1 就把這個 url 放到 1 號檔案裡)每個小檔案理想狀況下,大小約是 1.6 g,完全可以 load 到記憶體裡。

這種方法的好處在於,因為我們的目標是要去重,那麼那些a和b中重複的 urls,會被hashfunc(url) % 200對映到同乙個檔案中。這樣在這個小檔案中,來自 a 和 b 的 urls 在理想狀況下一共 3.2g,可以全部匯入記憶體進入重複判斷篩選出有重複的 urls。

但如果 hashfunc(url) % 200 的結果比較集中,就有可能會造成不同的 urls 在同乙個檔案中扎堆的情況,這種情況下,有一些檔案的大小可能會超過 4g。對於這種情況,處理的辦法是進行二次拆分,把這些仍然比較大的小檔案,用乙個新的 hashfunc 進行拆分:hashfunc』(url) % x。這裡再拆成多少個檔案,可以根據檔案的實際大小來定。如果二次拆分之後還是存在很大的檔案,就進行三次拆分。直到每個小檔案都小於 4g。

既然是記憶體空間太少的問題,有乙個主要用於記憶體過少的情況的資料結構:bloomfilter。我們可以使用乙個 4g 的 bloom filter,它大概包含 320 億 個 bit(1g = 230 * 8 bit)。把 a 檔案的 50億 個 urls 丟入 bf 中,然後查詢 b 檔案的 每個 url 是否在 bf 裡。這種方法的缺點在於,320 億個 bit 的 bf 裡存 50 億個 urls 實在是太滿了(要考慮到bf可能會用4個雜湊函式),錯誤率會很高。因此仍然還需需要方法1中的檔案拆分來分批處理。

將a,b檔案分別拆分為80個小檔案,每個小檔案4g。每個檔案在拆分的時候,每4g的資料在記憶體中做快速排序並將有序的urls輸出到小檔案中。用多路歸併演算法,將這些小檔案進行歸併,在歸併的過程中,即可知道哪些是重複的 urls。只需將重複的 urls 記錄下來即可。

當 a, b 各自有重複的 urls 的時候,比如最壞情況下,a裡的50億個urls 全部一樣。b裡也是。這樣採用方法1這種比較容易想到的 sharding 方法,是不奏效的,因為所有 urls 的 hashcode 都一樣,就算換不同的 hashfunc 也一樣。這種情況下,需要先對兩個檔案進行單獨的去重,方法是每 4g 的資料,放到記憶體中用簡單的雜湊表進行去重。這樣,在最壞情況下,總共 320g 的資料裡,乙個 urls 最多重複 80次,則不會出現太嚴重的扎堆情況了。演算法上唯一需要稍微改動的地方是,由於 a 存在多個重複的 urls,所以當和 b 的 urls 被sharding 到同乙個檔案裡的時候,需要標記一下這個 urls 來自哪個檔案,這樣才能知道是否在a和b中同時出現過。

另外,使用外排序的方法,是無需對兩個檔案進行單獨去重的步驟的。

總結:用外排序,分治思想,採用雜湊函式,外排在歸併的時候看一下有沒有重複,不用提前去重。

海量資料處理 排序問題

乙個檔案中有9億條不重複的9位整數,對這個檔案中數字進行排序 針對這個問題,最容易想到的方法是將所有資料匯入到記憶體中,然後使用常規的排序方法,例如插入排序,快速排序,歸併排序等各種排序方法對資料進行排序,最後將排序好的資料存入檔案.但這些方法在此並不適用,由於資料量巨大,對32位機器而言,很難將這...

對海量資料進行外排序的演算法

需求 一種資料報中包括int,string,long,double 四種型別的陣列,陣列長度均為4096.即4096行,每行有int,string,long,double四個資料 對1000個隨機產生的資料報,按int列進行排序。將int列最小的4096個資料及其對應的其他資料儲存到乙個新的資料報中...

演算法之海量資料處理

1.雜湊 分治 尋找共同的url 給定兩個a和b檔案,各存放50億個url,每個url佔64位元組,記憶體限制4gb,請找出a和b檔案中共同的url。乙個檔案的記憶體 5 000 000 000 64 320gb,每個檔案可以分為100個小檔案,每個檔案大約是3.2gb。1000萬個字串去重 假設每...