1. 程式人生 > >大資料計算:如何僅用1.5KB記憶體為十億物件計數 - Hyper LogLog 演算法

大資料計算:如何僅用1.5KB記憶體為十億物件計數 - Hyper LogLog 演算法

his is a guest post by Matt Abrams (@abramsm), from Clearspring, discussing how they are able to accurately estimate the cardinality of sets with billions of distinct elements using surprisingly small data structures. Their servers receive well over 100 billion events per month.

Clearspring,我們從事統計資料。統計一組不同元素且數量很大的資料集時,是一個挑戰。

為了更好地理解已經明確基數的大資料集的挑戰,我們假設你的日誌檔案包含16個字元的ID,並且你想統計不同ID的數量.例如:

4f67bfc603106cb2

這 16個字元需要用128位來表示。6萬5千個ID將需要1MB的空間。我們每天收到30多億條事件記錄,每條記錄都有一個ID。這些ID需要3840億位 或45GB的儲存。而這僅僅是ID欄位需要的空間。我們採取一種簡單的方法獲取日常事件記錄中以ID為基數的資料。最簡單的辦法就是使用雜湊集合且存放到 記憶體中,其中雜湊集包含唯一ID的列表(即輸入檔案中可能會有多條記錄的id是相同,但在雜湊集中只存放一條)。即使我們假設只有1/3的條記錄ID是唯 一的(即2/3的記錄ID是重複的),雜湊集仍需要119GB的RAM,這其中不包括Java需要在記憶體中儲存物件的開銷。你需要一臺配備幾百GB記憶體的 機器來計算不同的元素,並且這只是計算一天內日誌事件記錄的唯一ID的記憶體消耗。如果我們想要統計數週或數月的資料,這問題只會變得更加困難。我們身邊當 然不會有一臺配備幾百GB記憶體的空閒機器,所以我們需要一個更好的解決方案。

解決這一問題的常見辦法是使用點陣圖(部落格:海量資料處理演算法—Bit-Map)。 點陣圖可以快速、準確地獲取一個給定輸入的基數。點陣圖的基本思想是使用雜湊函式把資料集對映到一個bit位,每個輸入元素與bit位是一一對應。這樣 Hash將沒有產生碰撞衝突,並減少需要計算每個元素對映到1個bit的空間。雖然Bit-map大大節省了儲存空間,但當統計很高的基數或非常大的不同 的資料集,它們仍然有問題。例如,如果我們想要使用Bit-map計數十億,你將需要Bit-map位,或需要每個約120 MB的計數器。稀疏的點陣圖可以被壓縮,以獲得更多的空間效率,但也並不總是有幫助的。

幸運的是,基數估計

是一個熱門的研究領域。我們已經利用這項研究提供了一個開源實現的基數估計、集合元素檢測和top-k演算法。

基數估計演算法就是使用準確性換取空間。 為了說明這一點,我們用三種不同的計算方法統計所有莎士比亞作品中不同單詞的數量。請注意,我們的輸入資料集增加了額外的資料以致比問題的參考基數更高。 這三種技術是:Java HashSet、Linear Probabilistic Counter以及一個Hyper LogLog Counter。結果如下:

該表顯示,我們統計這些單詞只用了512 bytes,而誤差在3%以內。相比之下,HashMap的計數準確度最高,但需要近10MB的空間,你可以很容易地看到為什麼基數估計是有用的。在實際應用中準確性並不是很重要的,這是事實,在大多數網路規模和網路計算的情況下,用概率計數器會節省巨大的空間。

線性概率計數器

線性概率計數器是高效的使用空間,並且允許實現者指定所需的精度水平。該演算法在注重空間效率時是很有用的,但你需要能夠控制結果的誤差。該演算法分兩步執行:第一步,首先在記憶體中分配一個初始化為都為0的Bit-map,然後使用雜湊函式對輸入資料中的每個條目進行hash計算,雜湊函式運算的結果是將每條記錄(或者是元素)對映到Bit-map的一個Bit位上,該Bit位被置為1;第二步,演算法計算空的bit位數量,並使用這個數輸入到下面的公式來進行估算:

n=-m ln Vn

注意:ln Vn=Loge(Vn) 自然對數

在公式中,m是 Bit-map的大小,Vn是空bit位和map的大小的比率。需要重點注意的是原始Bit-map的大小,可以遠小於預期的最大基數。到底小多少取決於你可以承受誤差的大小。因為Bit-map的大小m小於不同元素的總數將會產生碰撞。雖然碰撞可以節省空間,但同時也造成了估算結果出現誤差。所以通過控制原始map的大小,我們可以估算碰撞的次數,以致我們將在最終結果中看到誤差有多大。

Hyper LogLog

顧名思義,Hyper LogLog計數器就是估算Nmax為基數的資料集僅需使用loglog(Nmax)+O(1) bits就可以。如線性計數器的Hyper LogLog計數器允許設計人員指定所需的精度值,在Hyper LogLog的情況下,這是通過定義所需的相對標準差和預期要計數的最大基數。大部分計數器通過一個輸入資料流M,並應用一個雜湊函式設定h(M)來工作。這將產生一個S = h(M) of {0,1}^∞字 符串的可觀測結果。通過分割雜湊輸入流成m個子字串,並對每個子輸入流保持m的值可觀測 ,這就是相當一個新Hyper LogLog(一個子m就是一個新的Hyper LogLog)。利用額外的觀測值的平均值,產生一個計數器,其精度隨著m的增長而提高,這隻需要對輸入集合中的每個元素執行幾步操作就可以完成。其結果 是,這個計數器可以僅使用1.5 kb的空間計算精度為2%的十億個不同的資料元素。與執行 HashSet所需的120 兆位元組進行比較,這種演算法的效率很明顯。

合併分散式計數器

我們已經證明了使用上面描述的計數器我們可以估算大集合的基數。但是,如果你的原始輸入資料集不適合於單臺機器,將怎麼做呢?這正是我們在Clearspring所 面臨的問題。我們的資料分散在數百臺伺服器上,並且每個伺服器只包含整個資料集子集的一部分。這事實上我們能合併一組分散式計數器的內容是至關重要的。這 個想法有點令人費解,但如果你花費一些時間去思考這個問題,就會發現其與基本的基數估計值相比並沒有太大的不同。因為這個計數器表示對映中的位作為基數, 我們可以採取兩個相容計數器並將他們bit位合併到單一的map上。這個演算法已經處理碰撞,所以我們可以得到一個基數估計所需的精密,即使我們從來沒有把 所有的輸入資料到一臺機器。這是非常有用的,節省了我們在網路中移動資料的大量時間和精力。

 

Next Steps

希望這篇文章能幫助你更好地理解這個概念和概率計數器的應用。如果估算大集合的基數是一個問題,而你又碰巧使用一個基於JVM的語言,那麼你應該使用stream-lib專案——它提供了其他幾個流處理工具以及上文所述的演算法的實現。