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][k1kn] 之间,就必然存在一个最大值 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=constantm2R

其中 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=1nxi1n

所以 HyperLogLog 的公式就可以写为

DVHLL=const∗m∗m∑j=1m12RjDV_{HLL} = const * m * \frac{m}{\sum_{j=1} ^ m \frac{1}{2^{R_j}}}DVHLL=constmj=1m2Rj1m

在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=log2m

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.mzph.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属性引用的…