日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

HyperLogLog原理与在Redis中的使用

發(fā)布時(shí)間:2023/12/20 数据库 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 HyperLogLog原理与在Redis中的使用 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

Redis-HyperLogLog

基于HyperLogLog算法,使用極小的空間完成巨量運(yùn)算

Redis 中HyperLogLog 基本使用

常用命令

  • PFADD key element [element …]: 將任意數(shù)量的元素添加到指定的 HyperLogLog 里面。
  • PFCOUNT key [key …]: 計(jì)算hyperloglog的獨(dú)立總數(shù)
  • prmerge destkey sourcekey [sourcekey…]: 合并多個(gè)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 算法原理

    特點(diǎn):

    • 能使用極少的內(nèi)存來統(tǒng)計(jì)巨量的數(shù)據(jù),在Redis中的HyperLogLog只需要12k內(nèi)存就能統(tǒng)計(jì) 2642^{64}264
    • 計(jì)數(shù)存在一定的誤差,但誤差率整體較低,標(biāo)準(zhǔn)誤差為 0.81%
    • 可以設(shè)置輔助計(jì)算因子減小誤差

    LogLog 簡(jiǎn)介

    HyperLogLog 其實(shí)是 LogLog 算法的改進(jìn)版,Loglog源于著名的伯努利實(shí)驗(yàn)。

    這個(gè)實(shí)驗(yàn)是這樣的:隨機(jī)拋一枚硬幣,那么正面朝上和反面朝上的概率都應(yīng)該是 50% ,那么如果一直重復(fù)拋硬幣,直到出現(xiàn)正面朝上,就記作1次伯努利實(shí)驗(yàn)。

    對(duì)于單個(gè)一次伯努利實(shí)驗(yàn),拋硬幣的次數(shù)是不確定的,有可能第一次就正面朝上,那這1次就被記為1次伯努利實(shí)驗(yàn),也有可能拋了10次才出現(xiàn)正面朝上,那這10次才會(huì)被記作1次伯努利實(shí)驗(yàn)。

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

    例如:實(shí)驗(yàn)0和1表示硬幣的正反,一輪做五次實(shí)驗(yàn),某輪伯努利實(shí)驗(yàn)的結(jié)果為

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

    那么這一輪伯努利實(shí)驗(yàn)的 kmax=4k_{max}=4kmax?=4 ,按照上面的公式應(yīng)該得到 5=245=2^45=24,這個(gè)誤差顯然太過巨大,我們可以增加某一輪實(shí)驗(yàn)的次數(shù),用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}次伯努利實(shí)驗(yàn),拋了{(lán)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}")

    通過測(cè)試,當(dāng)每一輪進(jìn)行5000次伯努利實(shí)驗(yàn)時(shí),進(jìn)行五輪,kmaxk_{max}kmax?分別為 12, 12, 14, 11, 15,誤差仍舊很大,所以我們可以進(jìn)行多輪伯努利實(shí)驗(yàn),求kmaxk_{max}kmax?的平均值,用python模擬一下

    import randomclass BernoulliExp:def __init__(self, freq: int, rounds: int, num: int):"""Args:freq: int,每輪進(jìn)行多少次實(shí)驗(yàn)rounds: k_max 對(duì)多少輪實(shí)驗(yàn)求平均num: 進(jìn)行多少次這樣的實(shí)驗(yàn)(求誤差)"""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}次伯努利實(shí)驗(yàn),拋了{(lán)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

    這時(shí)誤差依舊非常大,但我們發(fā)現(xiàn) kmaxk_{max}kmax?卻浮動(dòng)在4.038上下,這就說明nnnkmaxk_{max}kmax? 之間的關(guān)系確實(shí)存在,但公式前面還應(yīng)該有一個(gè)常數(shù)項(xiàng),原公式應(yīng)該是 n=α?2kmaxn = \alpha · 2^{k_{max}}n=α?2kmax?

    通過簡(jiǎn)單計(jì)算,把 α\alphaα設(shè)為 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計(jì)算出來的,但當(dāng)n取其他值時(shí),這個(gè)因子也能基本將相對(duì)誤差控制在0.1以內(nèi)。

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

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

    其中 DVLLDV_{LL}DVLL?就是n,constant就是調(diào)和因子, m是實(shí)驗(yàn)輪數(shù),R ̄\overline{R}Rkmaxk_{max}kmax?的平均值。


    而 HyperLogLog和LogLog的區(qū)別就是使用調(diào)和平均數(shù)計(jì)算kmaxk_{max}kmax?,這樣如果計(jì)算的數(shù)值相差較大,調(diào)和平均數(shù)可以較好的反應(yīng)平均水平,調(diào)和平均數(shù)的計(jì)算方式為:

    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中的實(shí)現(xiàn)方法

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

    統(tǒng)計(jì)一個(gè)頁面每日的點(diǎn)擊量(同一用戶不重復(fù)計(jì)算)

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

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

    例如一個(gè)value經(jīng)過Hash轉(zhuǎn)換后的比特串為

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

    這個(gè)比特串前14位是 110001(2){110001}_{(2)}110001(2)?,轉(zhuǎn)換成10進(jìn)制也就是49,而它從右往左看,第3位是1,所以3會(huì)被放到49桶中(首先要看49桶中原來的值是不是小于3,如果比3小,就用3替換原來的,否則不變,【因?yàn)橥爸写娴氖?span id="ozvdkddzhkzd" class="katex--inline">kmaxk_{max}kmax?】), kmaxk_{max}kmax?在這里最大也只能是64,用6bit肯定夠用。

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

    修正因子

    constant 并不是一個(gè)固定的值,他會(huì)根據(jù)實(shí)際情況而被分支設(shè)置,如: P=log?2mP = \log_2 mP=log2?m

    m 是分桶數(shù)

    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

    總結(jié)

    以上是生活随笔為你收集整理的HyperLogLog原理与在Redis中的使用的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。