簡介
Redis 在 2.8.9 版本新增了 HyperLogLog 結構
HyperLogLog 是用來做基數統計的演算法,即對集合去重元素的計數
在輸入元素的數量不超過2^64個,計算基數所需的記憶體最多12KB,該結構使用一種近似值演算法,標準誤差0.81%。
計算基數的方法分析
使用一般集合計算
隨著統計元素的增加,記憶體消耗也劇增,資源消耗嚴重,不適合大資料量統計
bitmap
用位陣列來表示各元素是否出現,每個元素對應一位,所需的總記憶體為n bit。能大大減少記憶體佔用且位操作迅速。
如果要統計1億個資料的基數值,大約需要記憶體100000000/8/1024/1024 ≈ 12M,記憶體減少佔用的效果顯著。然而統計一個物件的基數值需要12M,如果統計10000個物件,就需要將近120G,同樣不能廣泛用於大資料場景
但是這個方法是精確計算
概率演算法
目前用於基數計數的概率演算法包括:
- Linear Counting(LC):早期的基數估計演算法,LC在空間複雜度方面並不算優秀,實際上LC的空間複雜度與上文中簡單bitmap方法是一樣的(但是有個常數項級別的降低),都是O(Nmax)
- LogLog Counting(LLC):LogLog Counting相比於LC更加節省記憶體,空間複雜度只有O(log2(log2(Nmax)))
- HyperLogLog Counting(HLL):HyperLogLog Counting是基於LLC的優化和改進,在同樣空間複雜度情況下,能夠比LLC的基數估計誤差更小
演算法白話說明
通俗點說明: 假設我們為一個資料集合生成一個8位的雜湊串,那麼我們得到00000111的概率是很低的,也就是說,我們生成大量連續的0的概率是很低的。生成連續5個0的概率是1/32,那麼我們得到這個串時,可以估算,這個資料集的基數是32
應用場景
適用場景特點
- 非精確統計,HyperLogLog是近似值演算法,有0.81%標準誤差
- 資料量巨大,不大就用不上,大材小用,浪費空間
- 只能統計集合基數(去重)數量,而沒辦法去知道具體的內容,自然也沒辦法返回集合元素
應用場景舉例
- 統計 IP 數
- 統計 UV 數
- 統計使用者每天搜尋不同詞條個數
命令說明
pfadd
PFADD key element [element ...]
時間複雜度: O(1) to add every element
功能說明: 新增元素到計算空間
返回值
- 如果同時指定了key和element,那麼如果執行命令後HyperLogLog的近視基數發生了變化,則返回1,否則返回0
- 如果只指定了key,那麼如果Key存在,返回0,否則建立key,返回1
示例: PFADD hll a b c d e f g
PFCOUNT
PFCOUNT key [key ...]
時間複雜度
- 使用單個鍵呼叫時,O(1)的平均恆定時間非常短。
- 使用多個鍵時,O(N)N是鍵的數目,並且在使用多個鍵呼叫時,常數時間要大得多
功能說明: 計算傳入的所有key對應的元素集合去重基數
返回值: 基數計數近似值(標準誤差約0.81%)
示例: pfcount hll hll2
特別說明
作為呼叫此函式的副作用,HyperLogLog可能會被修改,因為後8個位元組編碼用於快取目的的最新計算基數。因此從技術上講PFCOUNT是寫命令
當用單個鍵呼叫PFCOUNT時,效能也很出色,PFCOUNT使用快取來記住先前計算的基數,這種變化很少改變,因為大多數PFADD操作不會更新任何暫存器
當PFCOUNT呼叫多個key時,HyperLogLogs需要合併計算,這是緩慢的,而且合併的基數不能被快取,所以有多個key使用時PFCOUNT可能需要的時間在一個毫秒級的數量級,不應濫用
PFMERGE
PFMERGE destkey sourcekey [sourcekey ...]
時間複雜度: O(N)合併N個HyperLogLogs,但是具有恆定的時間
功能說明: 將sourcekey 合併到 destkey, 如果destkey不存在,則新建立一個空的HyperLogLog,如果destkey存在,則將其視為sourcekey之一
返回值: 只返回ok
示例: PFMERGE hll3 hll1 hll2