大数据计算:如何仅用1.5KB内存为十亿对象计数
AddThis(前身為Clearspring)的數(shù)據(jù)分析副總監(jiān)Matt Abrams在High Scalability上發(fā)表了一篇文章,介紹了他們公司如何應(yīng)對(duì)大數(shù)據(jù)。在這篇文章中,AddThis僅僅用了1.5KB內(nèi)存的內(nèi)存就計(jì)算了十億個(gè)不同的對(duì)象,Matt Abrams主要向我們?cè)斀饬怂麄児驹谔幚磉^(guò)程中使用的方法。
以下為文章全文:
在AddThis,我們喜歡統(tǒng)計(jì)數(shù)據(jù)。對(duì)一組中不同元素(也稱為“基數(shù)”)的數(shù)量進(jìn)行計(jì)數(shù),當(dāng)其數(shù)量很大時(shí),這是一個(gè)挑戰(zhàn)。
為了更好地理解確定的大型成套基數(shù)的挑戰(zhàn),讓我們想象一下,在你的日志中有一個(gè)16個(gè)字符的ID,并且你想計(jì)算不同ID的數(shù)量。這里有一個(gè)例子:
4f67bfc603106cb2
16個(gè)字符需要用128位來(lái)表示。6萬(wàn)5千個(gè)ID將需要1MB的空間。我們每天收到30多億個(gè)事件,每個(gè)事件都有一個(gè)ID。這些ID需要3840億位或45GB的存儲(chǔ)。而這僅僅是ID字段需要的空間!為了得到我們?nèi)粘;顒?dòng)中的ID基數(shù),我們可以采取一個(gè)簡(jiǎn)單的方法。最簡(jiǎn)單的想法是使用內(nèi)存中的哈希集合,其中包含在輸入文件中看到的獨(dú)特的ID列表。即使我們假設(shè)3條記錄中有1個(gè)是唯一的,哈希集合仍需要119GB的RAM,不包括Java需要在內(nèi)存中存儲(chǔ)對(duì)象的開(kāi)銷。你需要一臺(tái)配備幾百GB內(nèi)存的機(jī)器來(lái)計(jì)算不同的元素,并且這只是計(jì)算一天的獨(dú)特ID的成本。如果我們想要數(shù)周或數(shù)月的數(shù)據(jù),這個(gè)問(wèn)題只會(huì)變得更加困難。我們身邊當(dāng)然不會(huì)有一臺(tái)配備幾百GB內(nèi)存的空閑機(jī)器,所以我們需要一個(gè)更好的解決方案。
解決這一問(wèn)題的常見(jiàn)辦法是使用位圖。位圖可以用來(lái)快速、準(zhǔn)確地獲取一個(gè)給定的輸入的基數(shù)。位圖的基本思路是使用哈希函數(shù)映射數(shù)據(jù)集到一個(gè)位字段,在位字段里輸入的獨(dú)特元素映射到該字段中的一個(gè)位。這產(chǎn)生零碰撞,并減少需要計(jì)算每個(gè)獨(dú)特元素到1位的空間。雖然位圖大大減少了對(duì)空間的要求,但當(dāng)基數(shù)是非常高或你對(duì)非常多的不同的組進(jìn)行計(jì)數(shù)時(shí),它們?nèi)匀挥袉?wèn)題。例如,如果我們想要使用位圖計(jì)數(shù)十億,你將需要十億位,或需要每個(gè)約120 MB的計(jì)數(shù)器。稀疏的位圖可以被壓縮,以獲得更多的空間效率,但也并不總是有幫助的。
幸運(yùn)的是,基數(shù)估計(jì)是一個(gè)熱門的研究領(lǐng)域。我們已經(jīng)利用這項(xiàng)研究提供了一個(gè)開(kāi)源實(shí)現(xiàn)的基數(shù)估計(jì)、集員資格檢測(cè)和top-k算法。
為了了解算法占用的空間與精確度之間的關(guān)系,我們用三種不同的計(jì)算方法在所有莎士比亞的作品計(jì)數(shù)了不同單詞的數(shù)量(90萬(wàn))。請(qǐng)注意,我們的輸入數(shù)據(jù)集有額外的數(shù)據(jù),所以基數(shù)高于這個(gè)問(wèn)題答案的標(biāo)準(zhǔn)參考。我們使用的三種技術(shù)是:Java HashSet、Linear Probabilistic Counter以及一個(gè)Hyper LogLog Counter。這里是結(jié)果:
該表顯示,我們只使用512字節(jié)的空間就可以進(jìn)行計(jì)數(shù),并且誤差在3%以內(nèi)。相比之下,HashMap的計(jì)數(shù)準(zhǔn)確度最高,但需要近10MB的空間,你可以很容易地看到為什么基數(shù)估計(jì)量是有用的。在實(shí)際應(yīng)用中精度并不是很重要的,這是事實(shí),在大多數(shù)網(wǎng)絡(luò)規(guī)模和網(wǎng)絡(luò)計(jì)算的情況下,用概率計(jì)數(shù)器可能會(huì)節(jié)省巨大的空間,這是真的。
線性概率計(jì)數(shù)器
線性概率計(jì)數(shù)器是高效的空間,并且允許實(shí)現(xiàn)者指定所需的精度水平。該算法在注重空間效率時(shí)是很有用的,但你需要能夠控制你結(jié)果里的誤差。該算法運(yùn)行需要兩步:第一步,在內(nèi)存中分配一個(gè)初始化為都為0的位圖,隨后哈希函數(shù)被應(yīng)用于輸入數(shù)據(jù)中的每個(gè)條目,哈希函數(shù)的結(jié)果將映射條目到位圖的一個(gè)位上,該位設(shè)置為1;第二步算法對(duì)空位的數(shù)量進(jìn)行計(jì)算,并使用這個(gè)數(shù)字輸入到下面的公式來(lái)獲得估計(jì)。
n=-m ln Vn
在方程中,m是位圖的大小,n是空位和映射的大小的比率。需要重點(diǎn)注意的是原始位圖的大小,可以遠(yuǎn)小于預(yù)期的最大基數(shù)。小多少取決于你能忍受多少錯(cuò)誤的結(jié)果。因?yàn)槲粓D的大小、m,小于不同元素的總數(shù),將會(huì)有碰撞。這些碰撞都可以節(jié)省空間,但同時(shí)也造成了錯(cuò)誤的估計(jì)。所以通過(guò)控制原始映射的大小,我們可以估算碰撞的次數(shù),因此我們將在最終結(jié)果中看到誤差量。
Hyper LogLog
顧名思義,Hyper LogLog計(jì)數(shù)器的特點(diǎn)是,你僅需要使用loglog(Nmax)+一個(gè)常數(shù)那么多位就可以對(duì)Nmax進(jìn)行計(jì)數(shù)。如線性計(jì)數(shù)器的Hyper LogLog計(jì)數(shù)器使設(shè)計(jì)人員能夠指定所需的精度公差,在Hyper LogLog的情況下,這是通過(guò)定義所需的相對(duì)標(biāo)準(zhǔn)偏差和你期望要計(jì)數(shù)的最大基數(shù)。大部分計(jì)數(shù)通過(guò)一個(gè)輸入數(shù)據(jù)流、M,并應(yīng)用一個(gè)哈希函數(shù)設(shè)置h(M)來(lái)工作。這將產(chǎn)生一個(gè)S = h(M) of {0,1}^∞字符串的可觀測(cè)結(jié)果。通過(guò)分割成m子字符串的哈希輸入流,并為每個(gè)子數(shù)據(jù)保持m的觀測(cè)值擴(kuò)展了Hyper LogLog。使用額外的觀測(cè)值的平均值,產(chǎn)生一個(gè)計(jì)數(shù)器,其精度提高為m的大小增長(zhǎng),只需要一個(gè)在輸入集的每個(gè)元素上恒定要執(zhí)行的操作數(shù)目。其結(jié)果是,這個(gè)計(jì)數(shù)器可以僅使用1.5 kb的空間計(jì)算精度為2%的十億個(gè)截然不同的項(xiàng)。與執(zhí)行 HashSet所需的120 兆字節(jié)進(jìn)行比較,這種算法的效率變得很明顯。
合并分布式計(jì)數(shù)器
我們已經(jīng)證明了使用上面描述的計(jì)數(shù)器我們可以估算大集合的基數(shù)。但是,如果你的原始輸入數(shù)據(jù)集不適合于單臺(tái)機(jī)器,你能做些什么?這正是我們?cè)贏ddThis所面臨的問(wèn)題。我們的數(shù)據(jù)分散在數(shù)百臺(tái)服務(wù)器上,并且每個(gè)服務(wù)器只包含整個(gè)數(shù)據(jù)集子集的一部分。這就是事實(shí),我們可以合并一組分布式計(jì)數(shù)器的內(nèi)容,這是至關(guān)重要的。這個(gè)想法有點(diǎn)令人費(fèi)解,但如果你花費(fèi)一些時(shí)間去思考這個(gè)概念,就會(huì)發(fā)現(xiàn)其與基本的基數(shù)估計(jì)值相比并沒(méi)有太大的不同。因?yàn)橛?jì)數(shù)器代表映射中的位作為基數(shù),我們可以采取兩個(gè)兼容計(jì)數(shù)器并將其位合并到單一的地圖。這個(gè)算法已經(jīng)處理碰撞,所以我們可以得到一個(gè)基數(shù)估計(jì)所需的精密,即使我們從來(lái)沒(méi)有把所有的輸入數(shù)據(jù)到一臺(tái)機(jī)器。這是非常有用的,節(jié)省了我們?cè)诰W(wǎng)絡(luò)中移動(dòng)數(shù)據(jù)的大量時(shí)間和精力。
總結(jié)
希望這篇文章能幫助你更好地理解這個(gè)概念和概率計(jì)數(shù)器的應(yīng)用。如果估計(jì)大集合的基數(shù)是一個(gè)問(wèn)題,而你又碰巧使用一個(gè)基于JVM的語(yǔ)言,那么你應(yīng)該使用stream-lib項(xiàng)目——它提供了其他幾個(gè)流處理工具以及上文所述的算法的實(shí)現(xiàn)。總結(jié)
以上是生活随笔為你收集整理的大数据计算:如何仅用1.5KB内存为十亿对象计数的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 微信小程序实现点击不同view标签,移动
- 下一篇: 微信小程序实现时间戳转为时间格式