如何高效計算DAU

普通程式設計師發表於2018-10-30

專案中一直有計算DAU這類的需求,業務開發者往往埋個點,其他是事情就交給資料團隊了。

如果自己要做一個這樣的計數器怎麼做呢?一個樸素的想法是透過hashmap實現,時間複雜度是O(1)。這個方法在計數物件較少的情況下還是不錯的,但是如果計數物件很多(比如計算獨立訪問IP),意味著hashmap的key非常多,記憶體消耗是非常大。

閱讀開源IM軟體GoBelieve程式碼,看到了下面一個函式 

如何高效計算DAU

這個函式的目的是計算IM的日活使用者量,採用了redis一個命令“PFADD”。趕緊查一下幫助文件,看到下面一段執行記錄

如何高效計算DAU

這個方法用於計算日活DAU太合適不過。仔細檢視Redis文件,HyperLogLog結構下支援三個方法,PFADD,PFCOUNT,PFMERGE。透過這幾個方案,能夠很容易實現計數類的一些需求。

HyperLogLog是什麼?

HyperLogLog是一種基數估計演算法。在理解技術估計演算法之前,我們需要先知道基數計數法的概念(有沒有感覺讀書的時候似曾相識)。

基數計數(cardinality counting)通常用來統計一個集合中不重複的元素個數,例如統計某個網站的UV,或者使用者搜尋網站的關鍵詞數量。資料分析、網路監控及資料庫最佳化等領域都會涉及到基數計數的需求。 要實現基數計數,最簡單的做法是記錄集合中所有不重複的元素集合Su,當新來一個元素xi,若Su中不包含元素xi,則將xi加入Su,否則不加入,計數值就是Su的元素數量。這種做法存在兩個問題:

1、當統計的資料量變大時,相應的儲存記憶體也會線性增長(文章開始用hashmap技術的辦法就有這個問題)

2、當集合Su變大,判斷其是否包含新加入元素xi的成本變大

大資料量背景下,要實現基數計數,首先需要確定儲存統計資料的方案,以及如何根據儲存的資料計算基數值;另外還有一些場景下需要融合多個獨立統計的基數值,例如對一個網站分別統計了三天的UV,現在需要知道這三天的UV總量是多少,怎麼融合多個統計值。

除了hashmap,另一個容易被想到的辦法是點陣圖BitMap。點陣圖可以快速、準確地獲取一個給定輸入的基數。點陣圖的基本思想是使用雜湊函式把資料集對映到一個bit位,每個輸入元素與bit位是一一對應。這樣Hash將沒有產生碰撞衝突,並減少需要計算每個元素對映到1個bit的空間。點陣圖大大節省了空間,但是當統計很高的基數或非常大的不同的資料集,它的空間開銷依然較大,同時可能帶來稀疏點陣圖等問題。

技術估計演算法(HyperLogLog是其中一種)就是來解決海量資料技術難題的!基數估計演算法使用準確性換取空間

為了說明這一點,這裡引用《Big Data Counting: How To Count A Billion Distinct Objects Using Only 1.5K》文章的實驗資料。

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

如何高效計算DAU

該表顯示,Hyper LogLog Counter統計這些單詞只用了512 bytes,而誤差在3%以內。相比之下,HashMap的計數準確度最高,但需要近10MB的空間,基數估計非常有用!在實際應用中,某些統計的準確性並不是很重要。在大多數網路規模和網路計算的情況下,用機率計數器會節省巨大的空間。

HyperLogLog的演算法原理可以搜尋《HyperLogLog the analysis of a near-optimal cardinality estimation algorithm》這篇文章。下邊截個演算法具體實現過程 

如何高效計算DAU

演算法看不懂沒關係(很多做AI的人也不清楚反向傳播演算法),重要的是要知道怎麼正確使用Redis中實現的HyperLogLog演算法。

redis中實現的HyperLogLog,只需要12K記憶體,在標準誤差0.81%的前提下,能夠統計2^64個資料

所以不要擔心統計資料太大,redis記憶體不夠用,放心使用就好。 

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31556438/viewspace-2218085/,如需轉載,請註明出處,否則將追究法律責任。

相關文章