HyperLogLog原理與在Redis中的使用

Redis-HyperLogLog

基于HyperLogLog算法,使用極小的空間完成巨量運算

Redis 中HyperLogLog 基本使用

常用命令

  1. PFADD key element [element …]: 將任意數量的元素添加到指定的 HyperLogLog 里面。
  2. PFCOUNT key [key …]: 計算hyperloglog的獨立總數
  3. prmerge destkey sourcekey [sourcekey…]: 合并多個hyperloglog

python 操作Redis HyperLogLog

from MyRedis.RedisTool import RedisToolclass RedisHLL:def __init__(self):self._conn = RedisTool.redis_connection("127.0.0.1", 8100, "redis")def hll_test(self):self._conn.pfadd('test', "junebao", "python", "redis", "hyperloglog", "java")count = self._conn.pfcount("test")print(count)if __name__ == '__main__':RedisHLL().hll_test()  # 5

HyperLogLog 算法原理

特點:

  • 能使用極少的內存來統計巨量的數據,在Redis中的HyperLogLog只需要12k內存就能統計 2642^{64}264
  • 計數存在一定的誤差,但誤差率整體較低,標準誤差為 0.81%
  • 可以設置輔助計算因子減小誤差

LogLog 簡介

HyperLogLog 其實是 LogLog 算法的改進版,Loglog源于著名的伯努利實驗

這個實驗是這樣的:隨機拋一枚硬幣,那么正面朝上和反面朝上的概率都應該是 50% ,那么如果一直重復拋硬幣,直到出現正面朝上,就記作1次伯努利實驗。

對于單個一次伯努利實驗,拋硬幣的次數是不確定的,有可能第一次就正面朝上,那這1次就被記為1次伯努利實驗,也有可能拋了10次才出現正面朝上,那這10次才會被記作1次伯努利實驗。

假設做了n次伯努利實驗,第一次實驗拋了 k1k_1k1? 次硬幣, 第二次拋了 k2k_2k2? 次硬幣,那么第 n 次實驗就拋了 knk_nkn? 次硬幣。在 [k1?kn][k_1 -k_n][k1??kn?] 之間,就必然存在一個最大值 kmaxk_{max}kmax? , kmaxk_{max}kmax?的意義就是在這一組伯努利實驗中,出現正面朝上需要的最多的拋擲次數。結合極大似然估計方法得到伯努利實驗的次數 nnn 和這個最大值 kmaxk_{max}kmax? 存在關系: n=2kmaxn = 2^{k_{max}}n=2kmax?

例如:實驗0和1表示硬幣的正反,一輪做五次實驗,某輪伯努利實驗的結果為

# 第一次
001
# 第二次
01
# 第三次
1
# 第四次
0001
# 第五次
001

那么這一輪伯努利實驗的 kmax=4k_{max}=4kmax?=4 ,按照上面的公式應該得到 5=245=2^45=24,這個誤差顯然太過巨大,我們可以增加某一輪實驗的次數,用python模擬一下

import randomclass BernoulliExp:def __init__(self, freq: int):self.freq = freqself.option = [0, 1]def run(self):k_max = 0for i in range(self.freq):num = 0while True:num += 1result = random.choice(self.option)if result == 1:break# print(f"第{i}次伯努利實驗,拋了{num}次硬幣")if num > k_max:k_max = numreturn k_maxif __name__ == '__main__':be = BernoulliExp(5000)k_max = be.run()print(f"k_max={k_max}")

通過測試,當每一輪進行5000次伯努利實驗時,進行五輪,kmaxk_{max}kmax?分別為 12, 12, 14, 11, 15,誤差仍舊很大,所以我們可以進行多輪伯努利實驗,求kmaxk_{max}kmax?的平均值,用python模擬一下

import randomclass BernoulliExp:def __init__(self, freq: int, rounds: int, num: int):"""Args:freq: int,每輪進行多少次實驗rounds: k_max 對多少輪實驗求平均num: 進行多少次這樣的實驗(求誤差)"""self.freq = freqself.option = [0, 1]self.rounds = roundsself.number_of_trials = numdef _run_one_round(self):k_max = 0for i in range(self.freq):num = 0while True:num += 1result = random.choice(self.option)if result == 1:break# print(f"第{i}次伯努利實驗,拋了{num}次硬幣")if num > k_max:k_max = numreturn k_maxdef get_k_max(self):sum_k_max = 0for i in range(self.rounds):sum_k_max += self._run_one_round()return sum_k_max / self.roundsdef deviation(self):dev = 0for i in range(self.number_of_trials):k_max = self.get_k_max()print(f"第{i}次:k_max = {k_max}")dev += (2 ** k_max) - self.freqreturn dev/self.number_of_trialsif __name__ == '__main__':be = BernoulliExp(6, 16384, 5)dev = be.deviation()print(f"誤差:{dev}")
第0次:k_max = 4.03546142578125
第1次:k_max = 4.034423828125
第2次:k_max = 4.05010986328125
第3次:k_max = 4.02423095703125
第4次:k_max = 4.045654296875
誤差:10.427087015403654

這時誤差依舊非常大,但我們發現 kmaxk_{max}kmax?卻浮動在4.038上下,這就說明nnnkmaxk_{max}kmax? 之間的關系確實存在,但公式前面還應該有一個常數項,原公式應該是 n=α?2kmaxn = \alpha · 2^{k_{max}}n=α?2kmax?

通過簡單計算,把 α\alphaα設為 0.36520.36520.3652:

第0次:k_max = 4.055908203125
第1次:k_max = 4.0262451171875
第2次:k_max = 4.03045654296875
第3次:k_max = 4.04534912109375
第4次:k_max = 4.048095703125
誤差:0.01269833279264585

這里0.3652是用n=6n=6n=6計算出來的,但當n取其他值時,這個因子也能基本將相對誤差控制在0.1以內。

上面的公式,便是LogLog的估算公式

DVLL=constant?m?2R ̄DV_{LL} = constant * m * 2 ^ {\overline{R}}DVLL?=constant?m?2R

其中 DVLLDV_{LL}DVLL?就是n,constant就是調和因子, m是實驗輪數,R ̄\overline{R}Rkmaxk_{max}kmax?的平均值。


而 HyperLogLog和LogLog的區別就是使用調和平均數計算kmaxk_{max}kmax?,這樣如果計算的數值相差較大,調和平均數可以較好的反應平均水平,調和平均數的計算方式為:

Hn=n∑i=1n1xiH_n = \frac{n}{\sum_{i=1} ^ n \frac{1}{x_i}}Hn?=i=1n?xi?1?n?

所以 HyperLogLog 的公式就可以寫為

DVHLL=const?m?m∑j=1m12RjDV_{HLL} = const * m * \frac{m}{\sum_{j=1} ^ m \frac{1}{2^{R_j}}}DVHLL?=const?m?j=1m?2Rj?1?m?

在Redis中的實現方法

如果我們我們可以通過kmaxk_{max}kmax?來估計nnn,那同樣的,對于一個比特串,我們就可以按照這個原理估算出里面1的個數,例如在

統計一個頁面每日的點擊量(同一用戶不重復計算)

要實現這個功能,最簡單的辦法就是維持一個set,每當有用新戶訪問頁面,就把ID加入集合(重復訪問的用戶也不會重復加),點擊量就是集合的長度,但這樣做最大的問題就是會浪費很多空間,如果一個用戶ID占8字節,加入有一千萬用戶,那就得消耗幾十G的空間,但Redis只用了12k就完成了相同的功能。

首先,他把自己的12k劃分為 16834 個 6bit 大小的 “桶”,這樣每個桶所能表示的最大數字為 1111(2)=63{1111}_{(2)} = 631111(2)?=63, 在存入時,把用戶ID作為Value傳入,這個value會被轉換為一個64bit的比特串,前14位用來選擇這個比特串從右往左看,第一次出現1的下標要儲存的桶號。

例如一個value經過Hash轉換后的比特串為

[0000 0000 0000 1100 01]01 0010 1010 1011 0110 1010 0111 0101 0110 1110 0110 0100

這個比特串前14位是 110001(2){110001}_{(2)}110001(2)?,轉換成10進制也就是49,而它從右往左看,第3位是1,所以3會被放到49桶中(首先要看49桶中原來的值是不是小于3,如果比3小,就用3替換原來的,否則不變,【因為桶中存的是kmaxk_{max}kmax?】), kmaxk_{max}kmax?在這里最大也只能是64,用6bit肯定夠用。

這樣不管有多少用戶訪問網站,存儲的只有這12k的數據,訪問量越多,kmaxk_{max}kmax? 越大,然后根據HyperLogLog公式,就可以較精確的估計出訪問量。(一個桶可以看作一輪伯努利實驗)

修正因子

constant 并不是一個固定的值,他會根據實際情況而被分支設置,如: P=log?2mP = \log_2 mP=log2?m

m 是分桶數

switch (p) {case 4:constant = 0.673 * m * m;case 5:constant = 0.697 * m * m;case 6:constant = 0.709 * m * m;default:constant = (0.7213 / (1 + 1.079 / m)) * m * m;
}

參考

https://www.cnblogs.com/linguanh/p/10460421.html#commentform

https://chenxiao.blog.csdn.net/article/details/104195908

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/457498.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/457498.shtml
英文地址,請注明出處:http://en.pswp.cn/news/457498.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

iOS開發UI篇—xib的簡單使用

一、簡單介紹 xib和storyboard的比較,一個輕量級一個重量級。 共同點: 都用來描述軟件界面 都用Interface Builder工具來編輯 不同點: Xib是輕量級的,用來描述局部的UI界面 Storyboard是重量級的,用來描述整個軟件的多個界面&…

【云棲計算之旅】線下沙龍第2期精彩預告:Docker在云平臺上的最佳實踐

Docker是一個開源的應用容器引擎,提供了一種在安全、可重復的環境中自動部署軟件的方式,允許開發者將他們的應用和依賴包打包到一個可移植的容器中,然后發布到任何流行的Linux機器上,也可以實現虛擬化。容器完全使用沙箱機制&…

小程序mpvue圖片繪制水印_開發筆記:使用 mpvue 開發斗圖小程序

之前用過 wepy 框架寫了個小程序 GitHub - yshkk/shanbay-mina: 基于 wepy 框架的 “扇貝閱讀” 微信小程序 ,感覺寫法上類似 vue,但不那么徹底。現在美團點評發布的 mpvue 支持開發者可以用 vue 的語法開發微信小程序,正好有強需求需要一個斗…

mysql int類型的長度值

整數類型的存儲和范圍(來自mysql手冊) 類型字節最小值最大值(帶符號的/無符號的)(帶符號的/無符號的)TINYINT1-1281270255SMALLINT2-3276832767065535MEDIUMINT3-83886088388607016777215INT4-2147483648214748364704294967295BIGINT8-92233720368547758089223372036854775807…

龍王我當定了(一個在QQ刷龍王的腳本)

自從學了python,龍王再也沒丟過,就是經常被打, QQ 和 TIM 都可以,發送時要把聊天窗口打開。 # 如果import報錯,那可以pip下載這幾個模塊試一試 import win32gui import win32con import win32clipboard as w import random from…

時序數據合并場景加速分析和實現 - 復合索引,窗口分組查詢加速,變態遞歸加速...

時序數據合并場景加速分析和實現 - 復合索引,窗口分組查詢加速,變態遞歸加速 作者 digoal 日期 2016-11-28 標簽 PostgreSQL , 數據合并 , 時序數據 , 復合索引 , 窗口查詢 背景 在很多場景中,都會有數據合并的需求。 例如記錄了表的變更明細…

navicat for mysql 數據庫備份與還原

一, 首先設置, 備份保存路徑 工具 -> 選項 點開 其他 -> 日志文件保存路徑 二. 開始備份 備份分兩種, 一種是以sql保存, 一種是保存為備份 SQL保存 右鍵點擊你要備份的數據庫, -> 轉儲SQL文件 選擇位置和文件名 開始轉儲 導入 建議 刪除所有表 或 重新建數據庫 同導出…

DES的原理及python實現

DES加密算法原理及實現 DES是一種對稱加密算法【即發送者與接收者持有相同的密鑰】,它的基本原理是將要加密的數據劃分為n個64位的塊,然后使用一個56位的密鑰逐個加密每一個64位的塊,得到n個64位的密文塊,最后將密文塊拼接起來得…

python按身高體重排隊_LeetCode-python 406.根據身高重建隊列

題目鏈接難度:中等 類型: 數組假設有打亂順序的一群人站成一個隊列。 每個人由一個整數對(h, k)表示,其中h是這個人的身高,k是排在這個人前面且身高大于或等于h的人數。 編寫一個算法來重建這個隊列。注意:總人數…

遠程連接mysql數據庫,1130問題

遠程或使用非127.0.0.1和localhost地址連接時,出現代號為1130問題, ERROR 1130: Host 192.168.2.159 is not allowed to connect to this MySQL server 猜想這是沒有授權,將mysql數據庫中user表中host列的localhost改為%,重新啟動…

華為手機充滿有提醒嗎_2020手機充電速度排名:最快21分鐘充滿,華為第15名

5G手機扎堆出現,中國5G基站數量也是不斷增多,中國移動曾經表態,2020年底將會在全國地級市覆蓋5G網絡,全民5G時代終于到來!從目前國內手機出貨量數據來看,5G手機占比已經達到了六成以上,國產5G手…

關于移動手機端富文本編輯器qeditor圖片上傳改造

日前項目需要在移動端增加富文本編輯,上網找了下,大多數都是針對pc版的,不太兼容手機,當然由于手機屏幕小等原因也限制富文本編輯器的眾多強大功能,所以要找的編輯器功能必須是精簡的。 找了好久,發現qedit…

【python】生成器

生成器 直接總結 創建生成器的方法 生成器表達式:(i for i in [1, 2])yield: 函數中出現yield這個函數就是生成器,函數(生成器)執行到yield時會返回yield后面的值,并暫停,知道下次被喚醒后會從暫停處接著…

python redis 性能測試臺_Redis性能測試

Redis 性能測試Redis 性能測試是通過同時執行多個命令實現的。Redis性能測試主要是通過src文件夾下的redis-benchmark來實現(Linux系統下)語法redis 性能測試的基本命令如下:redis-benchmark [option] [option value]實例以下實例同時執行 10000 個請求來檢測性能&a…

Java IO 系統

Java IO系統 File類 用來處理文件目錄,既可以代表一個特定文件的名稱,也可以代表一組文件的名稱,如果代表的是一個文件組,可以調用File.list()方法返回一個字符數組。 list()不傳遞任何參數時返回該目錄下所有文件或文件名的字…

Linux Crontab 任務管理工具命令以及示例

Crontab 是 Linux 平臺下的一款用于循環執行例行任務的工具,Linux 系統由 cron (crond) 這個系統服務來控制任務 , Linux系統本來就有很多的計劃任務需要啟動 , 所以這個系統服務是默認開機啟動的 。 Linux 為使用者提供的計劃任務的命令就是 Crontab Crontab 是 Linux 下用來周…

Linux 網絡編程詳解一(IP套接字結構體、網絡字節序,地址轉換函數)

IPv4套接字地址結構 struct sockaddr_in {uint8_t sinlen;(4個字節)sa_family_t sin_family;(4個字節)in_port_t sin_port;(2個字節)struct in_addr sin_addr;(4個字節)char sin_zer…

地籍cad的lisp程序大集合_AutoCAD-LISP程序100例

{:soso_e179:}AutoCAD-LISP程序100例.JPG (143.82 KB, 下載次數: 28)2011-10-18 14:42 上傳有說明很好!頂如果您使用 AutoCAD,下面的內容對您一定有幫助。在某些方面能大大提高您的工作效率。下面的程序均以源程序方式給出,您可以使用、參考、修改它。bg…

javascript中數組的22種方法

前面的話數組總共有22種方法,本文將其分為對象繼承方法、數組轉換方法、棧和隊列方法、數組排序方法、數組拼接方法、創建子數組方法、數組刪改方法、數組位置方法、數組歸并方法和數組迭代方法共10類來進行詳細介紹對象繼承方法數組是一種特殊的對象,繼…

javascript/jquery高度寬度詳情解說分析

為什么80%的碼農都做不了架構師?>>> 一、window對象表示瀏覽器中打開的窗口 二、window對象可以省略 一、document對象是window對象的一部分 二、瀏覽器的HTML文檔成為Document對象 window.location和document.location window對象的location屬性引用的…