1. 程式人生 > >【UV統計】海量資料統計的前世今生

【UV統計】海量資料統計的前世今生

> [轉載請註明出處](https://blog.csdn.net/ERKELIU/article/details/111124848) # 背景 > 在網際網路公司中,每個專案都需要資料統計、分析,便於專案組利用詳細資料研究專案的整體情況,進行下一步的調整。在資料統計中,UV統計是最常見的,也是最普遍的。有的場景要求實時性很高,有點場景要求準確性很高,有的場景比較在意計算過程中的記憶體。不同的場景使用不同的演算法,下面我們從0到1簡單介紹下UV統計領域。 # 什麼是UV統計 假設我們的場景是商家這邊上架一系列水果,然後需要統計出一共上架幾種水果。具體如下所示: 針對這個問題,我們想到的最簡單的方式就是利用STL中的**set**處理。 # SET 上架一個水果的時候,也同時在set中插入。最後需要統計的時候,直接計算set中一共有幾個水果即可。具體如下所示: 這種方式準確率是絕對準確的,但是這種方式耗費的記憶體是很大的。 假設每個水果需要 K 位元組,那麼如果有 M 個水果,一共需要 K * M 位元組。那麼我們能不能縮小這裡的記憶體呢? 稍微損失一點準確率換取記憶體?具體見下面**HashMap**的方式 # HASHMAP 這種演算法在上架一個水果的時候,只需要在特定的位置置1即可,而不需要儲存這個位置上究竟是何種水果。然後在統計的時候,只需要統計hashmap裡面有多少個1即可。具體如下所示: 具體如下所示:
那麼如果有M個水果,這裡其實只需要 M / 8 位元組,相比set的方式記憶體直接縮小到1/8。當然Hash肯定會有衝突的,所以這裡肯定有一定準確率的損失。 但是如果涉及到海量資料的UV統計,這裡的記憶體還是很大的。 能否用上統計學進一步縮小記憶體呢?具體見下面的Linear Count的方式。 # Linear Count 這種演算法在上架一個水果的時候,完全跟hashmap一致,在相應位置置1。 然後在統計的時候,利用統計學的方式,根據hashmap中零的個數給出一個估算值。具體如下所示: 假設M為雜湊桶長度,那麼每次上架水果,每個桶被選中的概率為: $$\frac{1}{M}$$ 然後在上架N個元素後,某個桶為0的概率為: $$(1-\frac{1}{M}) ^N$$ 所以在上架n個元素後,雜湊桶中零的個數期望為: $$ZeroNum=\sum_{i=1}^M (1-\frac{1}{M}) ^N = M (1-\frac{1}{M}) ^N= M ((1+\frac{1}{-M})^{-M})^{-\frac{N}{M}}) \approx Me^{- \frac{N}{M}}$$ 所以最終: $$ N = UV = -M ln(\frac{ZeroNum}{M}) $$ 所以Linear Count演算法中,只需統計下hashmap中零的個數,然後代入上式即可。 這種演算法在N很小的時候,準確率是很高的,但是N很大的時候,它的準確率急劇下降。 針對海量資料的情況,LogLog Count的演算法更加魯棒 # LogLog Count 這種演算法跟上面幾種都不同,上架水果的時候,在相應桶裡面記錄的是二進位制數後面最長的連續零個數。然後統計的時候,利用統計學的方式,根據儲存中最長連續後綴零個數,得出一個估計值。具體如下所示:
它的原理如下: 這裡如果只使用一個桶來估計的話,它的誤差是很大,需要用分桶平均的方式來減少它的誤差。 ## 分桶平均 既然這裡利用了分桶來減少誤差,那麼這裡統計的時候就必須合起來,這裡有4種方式: * 算術平均:$$UV=\frac{\sum_{j=1}^mUV_j} { m}$$ * 幾何平均:$$UV=\sqrt[m]{UV_1...UVm}$$ * 調和平均:$$UV=\frac{m}{\sum_{j=1}^mUV_j^{-1}}$$ * 中位數:$$UV=mediam \{UV_1,...,UV_m\}$$ LogLog Count利用的是算術平均的方式,所以最終估計值為: $$UV=2^{\frac{\sum_{j=1}^m{UV_j}}{m}}$$ 這種演算法對於基數大的情況下準確率挺高的,但是基數小的情況下準確率很低。 # HyperLogLog Count 這種演算法跟LogLog Count 類似,有個區別點就是它在求均值的時候利用了調和平均數,而不是算術平均數。這裡最終估計值為: $$UV=mm(\sum_{j=1}^m{2^{-M_j}})^{-1}$$ 然後它還引入了分段誤差修正。 ## 誤差修正
具體可以看我github上的程式碼:[HyperLogLog](https://github.com/lockeliu/hyperloglog) # 總結 | | 準確率 | 記憶體 | 耗時| |--|--| -- | -- | | Set | 絕對準確 | K * M| O(Mlog(M))| | HashMap | 很高 | M/8| O(M)| | Linear Count | 基數小高,基數大低 | M/8 | O(M/8)| | LogLog Count | 基數小低,基數大高 | | | | HyperLogLog Count |