大資料計算:如何僅用1.5KB記憶體為十億對象計數

來源:互聯網
上載者:User

標籤:

大資料計算:如何僅用1.5KB記憶體為十億對象計數 

                     Big Data Counting: How To Count A Billion Distinct Objects Using Only 1.5K

This 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 MB進行比較,這種演算法的效率很明顯。

合并分布式計數器

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

 

Next Steps

 

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

本文來自:High Scalability

 

本人結束語:自認英語不好,翻譯可能會有出入。但我看了csdn的翻譯,我懷疑那是技術員人翻譯的嗎? 一些翻譯直接是工具翻譯過來。

若深入瞭解Hyper LogLog:請看http://algo.inria.fr/flajolet/Publications/FlFuGaMe07.pdf和http://www.ic.unicamp.br/~celio/peer2peer/math/bitmap-algorithms/durand03loglog.pdf

 

At Clearspring we like to count things. Counting the number of distinct elements (the cardinality) of a set is challenge when the cardinality of the set is large.

To better understand the challenge of determining the cardinality of large sets let‘s imagine that you have a 16 character ID and you‘d like to count the number of distinct IDs that you‘ve seen in your logs. Here is an example:

4f67bfc603106cb2

These 16 characters represent 128 bits. 65K IDs would require 1 megabyte of space. We receive over 3 billion events per day, and each event has an ID. Those IDs require 384,000,000,000 bits or 45 gigabytes of storage. And that is just the space that the ID field requires! To get the cardinality of IDs in our daily events we could take a simplistic approach. The most straightforward idea is to use an in memory hash set that contains the unique list of IDs seen in the input files. Even if we assume that only 1 in 3 records are unique the hash set would still take 119 gigs of RAM, not including the overhead Java requires to store objects in memory. You would need a machine with several hundred gigs of memory to count distinct elements this way and that is only to count a single day‘s worth of unique IDs. The problem only gets more difficult if we want to count weeks or months of data. We certainly don‘t have a single machine with several hundred gigs of free memory sitting around so we needed a better solution.

One common approach to this problem is the use of bitmaps. Bitmaps can be used to quickly and accurately get the cardinality of a given input. The basic idea with a bitmap is mapping the input dataset to a bit field using a hash function where each input element uniquely maps to one of the bits in the field. This produces zero collisions, and reduces the space required to count each unique element to 1 bit. While bitmaps drastically reduce the space requirements from the naive set implementation described above they are still problematic when the cardinality is very high and/or you have a very large number of different sets to count. For example, if we want to count to one billion using a bitmap you will need one billion bits, or roughly 120 megabytes for each counter. Sparse bitmaps can be compressed in order to gain space efficiency, but that is not always helpful.

Luckily, cardinality estimation is a popular area of research. We‘ve leveraged this research to provide a open source implementation of cardinality estimators, set membership detection, and top-k algorithms.

Cardinality estimation algorithms trade space for accuracy. To illustrate this point we counted the number of distinct words in all of Shakespeare‘s works using three different counting techniques. Note that our input dataset has extra data in it so the cardinality is higher than the standard reference answer to this question. The three techniques we used were Java HashSet, Linear Probabilistic Counter, and a Hyper LogLog Counter. Here are the results:

Counter

Bytes Used

Count

Error

HashSet

10447016

67801

0%

Linear

3384

67080

1%

HyperLogLog

512

70002

3%

 

The table shows that we can count the words with a 3% error rate using only 512 bytes of space. Compare that to a perfect count using a HashMap that requires nearly 10 megabytes of space and you can easily see why cardinality estimators are useful. In applications where accuracy is not paramount, which is true for most web scale and network counting scenarios, using a probabilistic counter can result in tremendous space savings.

Linear Probabilistic Counter

The Linear Probabilistic Counter is space efficient and allows the implementer to specify the desired level of accuracy. This algorithm is useful when space efficiency is important but you need to be able to control the error in your results. This algorithm works in a two-step process. The first step assigns a bitmap in memory initialized to all zeros. A hash function is then applied to the each entry in the input data. The result of the hash function maps the entry to a bit in the bitmap, and that bit is set to 1. The second step the algorithm counts the number of empty bits and uses that number as input to the following equation to get the estimate.

n=-m ln Vn

In the equation m is the size of the bitmap and Vn is the ratio of empty bits over the size of the map. The important thing to note is that the size of the original bitmap can be much smaller than the expected max cardinality. How much smaller depends on how much error you can tolerate in the result. Because the size of the bitmap, m, is smaller than the total number of distinct elements, there will be collisions. These collisions are required to be space-efficient but also result in the error found in the estimation. So by controlling the size of the original map we can estimate the number of collisions and therefore the amount of error we will see in the end result.

Hyper LogLog

The Hyper LogLog Counter‘s name is self-descriptive. The name comes from the fact that you can estimate the cardinality of a set with cardinality Nmax using just loglog(Nmax) + O(1) bits. Like the Linear Counter the Hyper LogLog counter allows the designer to specify the desired accuracy tolerances. In Hyper LogLog‘s case this is done by defining the desired relative standard deviation and the max cardinality you expect to count. Most counters work by taking an input data stream, M, and applying a hash function to that set, h(M). This yields an observable result of S = h(M) of {0,1}^∞ strings. Hyper LogLog extends this concept by splitting the hashed input stream into m substrings and then maintains m observables for each of the substreams. Taking the average of the additional observables yields a counter whose accuracy improves as m grows in size but only requires a constant number of operations to be performed on each element of the input set. The result is that, according to the authors of thispaper, this counter can count one billion distinct items with an accuracy of 2% using only 1.5 kilobytes of space. Compare that to the 120 megabytes required by the HashSet implementation and the efficiency of this algorithm becomes obvious.

Merging Distributed Counters

We‘ve shown that using the counters described above we can estimate the cardinality of large sets. However, what can you do if your raw input dataset does not fit on single machine? This is exactly the problem we face at Clearspring. Our data is spread out over hundreds of servers and each server contains only a partial subset of the the total dataset. This is where the fact that we can merge the contents of a set of distributed counters is crucial. The idea is a little mind-bending but if you take a moment to think about it the concept is not that much different than basic cardinality estimation. Because the counters represent the cardinality as set of bits in a map we can take two compatible counters and merge their bits into a single map. The algorithms already handle collisions so we can still get a cardinality estimation with the desired precision even though we never brought all of the input data to a single machine. This is terribly useful and saves us a lot of time and effort moving data around our network.

Next Steps

Hopefully this post has helped you better understand the concept and application of probabilistic counters. If estimating the cardinality of large sets is a problem and you happen to use a JVM based language then you should check out the stream-lib project — it provides implementations of the algorithms described above as well as several other stream-processing utilities.

大資料計算:如何僅用1.5KB記憶體為十億對象計數

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.