Redis 高级数据结构 HyperLogLog

news2024/11/27 18:22:53

介绍

    HyperLogLog(Hyper[ˈhaɪpə(r)])并不是一种新的数据结构(实际类型为字符串类型),而是一种基数算法,通过HyperLogLog可以
利用极小的内存空间完成独立总数的统计,数据集可以是IP、Email、ID等。
   如果你负责开发维护一个大型的网站,有一天产品经理要网站每个网页每天的 UV 数据,然后让你来开发这个统计模块,你会如何实现?
   如果统计 PV 那非常好办,给每个网页一个独立的 Redis 计数器就可以了,这个计数器的 key 后缀加上当天的日期。这样来一个请求,
incrby 一次,最终就可以统计出所有的 PV 数据。
   但是 UV 不一样,它要去重,同一个用户一天之内的多次访问请求只能计数一次。这就要求每一个网页请求都需要带上用户的 ID,无论
是登陆用户还是未登陆用户都需要一个唯一 ID 来标识。
   一个简单的方案,那就是为每一个页面一个独立的 set 集合来存储所有当天访问过此页面的用户 ID。当一个请求过来时,我们使用
sadd 将用户 ID 塞进去就可以了。通过 scard 可以取出这个集合的大小,这个数字就是这个页面的 UV 数据。
  但是,如果你的页面访问量非常大,比如一个爆款页面几千万的 UV,你需要一个很大的 set集合来统计,这就非常浪费空间。如果这样的
页面很多,那所需要的存储空间是惊人的。为这样一个去重功能就耗费这样多的存储空间,值得么?其实需要的数据又不需要太精确,105w 
和 106w 这两个数字对于老板们来说并没有多大区别,So,有没有更好的解决方案呢?
  这就是HyperLogLog的用武之地,Redis 提供了 HyperLogLog 数据结构就是用来解决这种统计问题的。HyperLogLog 提供不精确的
去重计数方案,虽然不精确但是也不是非常不精确,Redis官方给出标准误差是0.81%,这样的精确度已经可以满足上面的UV 统计需求了。

百万级用户访问网站
image.png

操作命令

HyperLogLog提供了3个命令: pfadd、pfcount、pfmerge。

1、pfadd key element [element …] pfadd用于向HyperLogLog 添加元素,如果添加成功返回1

127.0.0.1:6379> pfadd u-6-1 u1 u2 u3
(integer) 1

2、pfcount pfcount key [key …] pfcount用于计算一个或多个HyperLogLog的独立总数

127.0.0.1:6379> pfcount u-6-1
(integer) 3
 如果此时向插入一些用户,用户并且有重复
127.0.0.1:6379> pfadd u-6-1 u1 u2 u3 u4 u5
(integer) 1
127.0.0.1:6379> pfcount u-6-1
(integer) 5
127.0.0.1:6379> pfadd u-6-2 u6 u7 u8 u9 u10
(integer) 1
127.0.0.1:6379> pfcount u-6-2
(integer) 5
127.0.0.1:6379> PFMERGE  u-6 u-6-1 u-6-2
OK
127.0.0.1:6379> pfcount u-6
(integer) 10

3、pfmerge destkey sourcekey [sourcekey … ] pfmerge可以求出多个HyperLogLog的并集并赋值给destkey

127.0.0.1:6379> pfcount u-6-1
(integer) 3
 如果此时向插入一些用户,用户并且有重复
127.0.0.1:6379> pfadd u-6-1 u1 u2 u3 u4 u5
(integer) 1
127.0.0.1:6379> pfcount u-6-1
(integer) 5
127.0.0.1:6379> pfadd u-6-2 u6 u7 u8 u9 u10
(integer) 1
127.0.0.1:6379> pfcount u-6-2
(integer) 5
127.0.0.1:6379> PFMERGE  u-6 u-6-1 u-6-2
OK
127.0.0.1:6379> pfcount u-6
(integer) 10

原理概述
1、基本原理
HyperLogLog基于概率论中伯努利试验并结合了极大似然估算方法,并做了分桶优化
实际上目前还没有发现更好的在大数据场景中准确计算基数的高效算法,因此在不追求绝对准确的情况下,使用概率算法算是一个不错的解决方案。概率算法不直接存储数据集合本身,通过一定的概率统计方法预估值,这种方法可以大大节省内存,同时保证误差控制在一定范围内。目前用于基数计数的 概率算法包括:

举个例子来理解HyperLogLog
规则如下: 抛硬币的游戏,每次抛的硬币可能正面,可能反面,每回合一直抛,直到每当抛到正面回合结束,抛到正面最长的回合用到了7次,你来猜一猜,我用到了多少个回合做到的?
image.png
进行了n次实验,比如上图:

第一次试验: 抛了3次才出现正面,此时 k=3,n=1

第二次试验: 抛了2次才出现正面,此时 k=2,n=2

第三次试验: 抛了4次才出现正面,此时 k=4,n=3

…………

第n 次试验:抛了7次才出现正面,此时我们估算,k=7

k是每回合抛到1所用的次数,我们已知的是最大的k值,可以用kmax表示。由于每次抛硬币的结果只有0和1两种情况,因此,能够推测出kmax在任意回合出现的概率 ,并由kmax结合极大似然估算的方法推测出n的次数n =2^(k_max) 。概率学把这种问题叫做伯努利实验。这种本身就是概率的问题,所以这种预估方法存在较大误差,为了改善误差情况,HLL中引入分桶平均的概念。

同样举抛硬币的例子,如果只有一组抛硬币实验,显然根据公式推导得到的实验次数的估计误差较大;如果100个组同时进行抛硬币实验,受运气影响的概率就很低了,每组分别进行多次抛硬币实验,并上报各自实验过程中抛到正面的抛掷次数的最大值,就能根据100组的平均值预估整体的实验次数了。

分桶平均的基本原理是将统计数据划分为m个桶,每个桶分别统计各自的kmax,并能得到各自的基数预估值,最终对这些基数预估值求平均得到整体的基数估计值。LLC中使用几何平均数预估整体的基数值,但是当统计数据量较小时误差较大;HLL在LLC基础上做了改进,采用调和平均数过滤掉不健康的统计值

什么叫调和平均数呢?举个例子

求平均工资:A的是1000/月,B的30000/月。采用平均数的方式就是:
(1000 + 30000) / 2 = 15500

采用调和平均数的方式就是:
2/(1/1000 + 1/30000) ≈ 1935.484

可见调和平均数比平均数的好处就是不容易受到大的数值的影响,比平均数的效果是要更好的。

结合Redis的实现理解原理

现在我们和前面的业务场景进行挂钩:统计网页每天的 UV 数据。

1.转为比特串

通过hash函数,将数据转为二进制的比特串,例如输入5,便转为:101。为什么要这样转化呢?

是因为要和抛硬币对应上,比特串中,0 代表了反面,1 代表了正面,如果一个数据最终被转化了 10010000,那么从右往左,从低位往高位看,我们可以认为,首次出现 1 的时候,就是正面。

那么基于上面的估算结论,我们可以通过多次抛硬币实验的最大抛到正面的次数来预估总共进行了多少次实验,同样也就可以根据存入数据中,转化后的出现了 1 的最大的位置 k_max 来估算存入了多少数据。

2.分桶

分桶就是分多少轮。抽象到计算机存储中去,就是存储的是一个以单位是比特(bit),长度为 L 的大数组 S ,将 S 平均分为 m 组,注意这个 m 组,就是对应多少轮,然后每组所占有的比特个数是平均的,设为 P。容易得出下面的关系:

比如有4个桶的话,那么可以截取低2位作为分桶的依据。

比如

10010000 进入0号桶

10010001 进入1号桶

10010010 进入2号桶

10010011 进入3号桶

Redis 中的 HyperLogLog 实现

pfadd

127.0.0.1:6379> pfadd u-6-1 u1
(integer) 1

当我们执行这个操作时,u1 这个字符串就会被转化成64个bit的二进制比特串。

0010…0001 64位

然后在Redis中要分到16384个桶中(为什么是这么多桶:第一降低误判,第二,用到了14位二进制:2的14次方=16384)

怎么分?根据得到的比特串的后14位来做判断即可。

image.png

根据上述的规则,我们知道这个数据要分到 1号桶,同时从右往左 (低位到高位)计算第1个出现的1的位置,这里是第4位,那么就往这个1号桶插入4的数据(转成二进制)

如果有第二个数据来了,按照上述的规则进行计算。

那么问题来了,如果分到桶的数据有重复了(这里比大小,大的替换小的):

规则如下,比大小(比出现位置的大小),比如有个数据是最高位才出现1,那么这个位置算出来就是50,50比4大,则进行替换。1号桶的数据就变成了50(二进制是110010)

所以这里可以看到,每个桶的数据一般情况下6位存储即可。

所以我们这里可以推算一下一个key的HyperLogLog只占据多少的存储。

16384*6 /8/1024=12k。并且这里最多可以存储多少数据,因为是64位吗,所以就是2的64次方的数据,这个存储的数据非常非常大的,一般用户用long来定义,最大值也只有这么多。

pfcount

进行统计的时候,就是把16384桶,把每个桶的值拿出来,比如取出是 n,那么访问次数就是2的n次方。

image.png

然后把每个桶的值做调和平均数,就可以算出一个算法值。

同时,在具体的算法实现上,HLL还有一个分阶段偏差修正算法。我们就不做更深入的了解了。

image.png

const和m都是Redis里面根据数据做的调和平均数。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/615560.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Vue.js 如何进行打包部署

Vue.js 中的打包部署 Vue.js 是一款流行的前端框架,它提供了一种简单、灵活的方式来构建用户界面。在开发完成后,需要对 Vue.js 应用程序进行打包部署,以便在生产环境中使用。本文将介绍 Vue.js 中的打包部署以及如何进行打包部署。 打包部署…

运维小白必学篇之基础篇第十七集:NFS和DHCP实验

NFS和DHCP实验 目录 NFS和DHCP实验 环境配置: 实验题1:实现NFS服务 实验题2:实现DHCP服务 实验作业: 计算机1配置如下:(计算机名为姓名首拼,例hy01,hy02...)基础环…

为什么初学者都先学C语言?

不少高校选择C语言,主要C语言是一种相对底层的语言,学习它可以让学习者更好的理解计算机的基本原理和编程的基础概念,比如变量、函数、指针等。这些基础知识对于理解其他高级语言和解决复杂的编程问题都非常重要。 另外就是C语言对算法和数据…

最小化微服务漏洞

最小化微服务漏洞 目录 本节实战 实战名称💘 案例:设置容器以普通用户运行-2023.5.29(测试成功)💘 案例:避免使用特权容器,选择使用capabilities-2023.5.30(测试成功)💘 案例:只读挂载容器文件…

Redis进阶:主从复制、集群搭建

Redis进阶 1. 主从复制机制1.1 复制原理1.2 配置“一主多从”试验主从复制1)为每个Redis进程提供配置文件2)分别在不同客户端启动Redis服务3)配置服务器成为某主机的下属从机 1.3 不同模式1.3.1 一主二仆1.3.2 薪火相传1.3.3 反客为主 1.4 哨…

在京东做外包的那些日子....

不知不觉已经在京东做了两年外包,最近收到不少朋友私信,说马上面临就业,在找工作的过程中,有不少软件测试的外包公司给我打电话让去面试,究竟要不要去?这篇文章给大家说说我这2年的外包经验,希望…

【Java基础学习打卡01】计算机概述

目录 引言一、计算机是什么?1.计算机vs计算器2.计算机定义 二、计算机发展简史三、计算机分类四、计算机基本工作原理1.冯诺依曼2.冯诺依曼原理 总结 引言 其实我们在学习Java编程之前应该要对计算机有所了解,这里的了解不是说我们日常接触电脑就算是了…

机器视觉初步2:机器视觉基础知识

本节概况 1.机器视觉的定义2.机器视觉系统的工作流程3.机器视觉的组成视场和最大像面 1.机器视觉的定义 什么是机器视觉,其中美国的一种定义为: “机器视觉是研究如何通过光学装置和非接触式传感器自动地接收、处理真实场景的图像,以获得所需…

前端使用tailwindcss 快速实现主题切换方案

使用Tailwind CSS在黑暗模式下为你的网站设计样式。 现在,黑暗模式是许多操作系统的第一流功能,为你的网站设计一个黑暗版本以配合默认设计,变得越来越普遍。 为了使这一点尽可能简单,Tailwind包括一个暗色变体,让你…

POWERBUILDER基础学习提纲

Chengg0769 2012年 版权信息保留: www.mis2erp.com http://blog.csdn.net/chengg0769 http://www.haojiaocheng.cc 这当时是给列给一位因伤休养的朋友。他有一段难捱的时间,想学习学习。这样复出之后也不至于工作无望或者浪费这段时间。 在SQL2000基础…

什么是API接口测试

什么是 API ? API 是“应用程序编程接口”的缩写,是一种允许不同应用程序之间相互通信和交换数据的接口。就好像在餐厅点餐一样,你只需要告诉服务员你想要的食物,而不需要了解厨房中的具体操作,服务员会把你的订单传递…

【PyQt5】(02)Qt Designer:快速设计GUI界面的利器

系列文章目录 【PyQt5】(01)PyQt的详细介绍 文章目录 系列文章目录前言一、安装Qt Designer二、Qt Designer的基本用法2.1 新建窗体2.2 添加部件2.3 编辑属性2.4 布局管理2.5 设置样式2.6 保存文件 三、将UI文件转化为Python代码并显示3.1 使用uic将UI文…

请问一下java在线编程的网站有哪一些?

前言 下面为大家整合了一些Java在线编程的的网站,个人认为都是挺好用的,整合不易,希望大家能顺手留下点赞和收藏! 废话少说,我们直接进入正题: 1、菜鸟工具 菜鸟工具的这个在线编程工具页面很为简洁&…

linux查看/修改某个进程运行的CPU核

1.ps -eF #查看fwd进程运行在哪个cpu核上 [rootCENTOS57 rpm]# ps -eF | grep fwd 2.top命令 (1)top (2)按f键可以选择下面配置选项 P Last Used Cpu (SMP) (3)Esc 退回到top界面可以看到多了一列进程所在cpu信息 3.pidstat命令 查看进程使用cpu情况,如果绑定了多…

CH32V3xx RT-Thread下的ethernet调试及问题记录

目录 1、CH32V3xx 以太网模块简介2、TCP Client 程序2.1 WCHNET库2.1.1 添加WCHNET2.1.2 以太网配置2.2 TCP Client代码2.3 测试结果3、调试过程中的一些问题1、CH32V3xx 以太网模块简介 CH32V3xx MCU的以太网收发器是微控制器的一个重要高速高速通讯外设,集成了千兆的MAC(媒体…

堆积如山:探索数据结构中的堆

前言 欢迎来到小K的数据结构专栏的第十一小节,本节将为大家带来堆的详解并带来堆题目的讲解(✨当然也为大家准备了完整的源码 )~希望你看完之后,能对你有所帮助,不足请指正!共同学习交流 🐾 目录…

排查一次nacos动态配置不生效

一、问题描述 新需求需要使用到nacos动态配置,但是开发完成之后联调过程中发现动态配置没有生效。 二、问题排查 首先在本地测试,发现启动服务后修改nacos配置确实不生效,在查看启动日志时发现服务启动时打印了下面这样的日志。这里是在配…

Blender3.2使用python脚本命令的三种方式, 以及源码示例

本文环境系统OS(Win10) 方式一,在Script(脚本)功能里面的Console(控制台输入python代码)。 如下图: 对应的三句代码是: print("Hi, blender.")import bpybpy.ops.mesh.primitive_cube_add(enter_editmodeFalse, alignWORLD, location(0,0, …

ModBus通信详解

Modbus协议包括ASCII、RTU、TCP等,并没有规定物理层。此协议定义了控制器能够认识和使用的消息结构,而不管它们是经过何种网络进行通信的。标准的Modicon控制器使用RS232C实现串行的Modbus。Modbus的ASCII、RTU协议规定了消息、数据的结构、命令和就答的…

python通过远程连接mysql数据库进行操作

文章目录 前言一、开启远程访问权限1、开启远程访问端口2、命令行登录mysql 二、Navicat 建库1、新建连接2、建库建表 三、python对数据库进行远程操作三、总结四、参考资料 前言 环境: 1、mysql 5.7 2、pycharm 22.3.1 Professional 3、navicat 一、开启远程访问权…