「一文搞懂」Redis高级数据结构HyperLogLog
mhr18 2024-12-04 14:03 21 浏览 0 评论
本章内容
简介
HyperLogLog是一种基数估算算法,可以用于在数据量很大的情况下,只需要使用很小的空间就能够近似的统计出所有数据的基数。
在Redis中,HyperLogLog是一种高级数据结构,可以使用该结构来进行基数估算,它的标准误差率为0.81%,这意味着即使在非常大的数据集上,它也可以提供非常准确的结果。
应用场景
HyperLogLog的主要应用场景:
- 独立访客统计:通过收集用户IP或其他标识符,将其添加到HyperLogLog中,统计得到一个独立访客数量的估算值。
- 活跃用户数统计:通过将用户标识符(如:用户ID)添加到HyperLogLog中,统计得到活跃用户数的近似值。
- 布隆过滤器:通过与布隆过滤器结合使用,实现数据的高效检索,其中布隆过滤器用于判断元素是否存在于集合中,HyperLogLog用于估算集合中不重复元素的数量。
- 统计分析:通过将数据特征或标识符添加到HyperLogLog中,统计得到不同特征对应的数量(如:兴趣爱好、商品种类等)。
常用命令
pfadd命令
- 格式:pfadd key element [element …]。
- 功能:向HyperLogLog中添加元素。
- 返回值:HyperLogLog中元素发生变更时返回1;否则返回0。
- 时间复杂度:O(1)。
pfcount命令
- 格式:pfcount key [key …]。
- 功能:统计基数。
- 返回值:
- 单个 key:返回HyperLogLog中存储的基数统计结果,如果key不存在则返回0。
- 多个 key:返回多个key对应的HyperLogLog合并结果。内部处理:创建一个临时HyperLogLog合并多个key对应的HyperLogLog。
- 时间复杂度:O(n),n为key的个数。
pfmerge命令
- 格式:pfmerge destkey sourcekey[sourcekey ...]。
- 功能:合并多个HyperLogLog。
- 返回值:OK。
- 时间复杂度:O(n),n为HyperLogLog的个数。
实现原理
HyperLogLog算法的理论基础与伯努利试验有关。
伯努利实验
伯努利试验(Bernoulli experiment)是在同样的条件下重复地、相互独立地进行的一种随机试验,其特点是该随机试验只有两种可能结果:发生或者不发生。假设该项试验独立重复地进行了n次,那么就称这一系列重复独立的随机试验为n重伯努利试验,或称为伯努利概型。
伯努利实验过程
一个硬币存在正反两面,每抛出一次,正面朝上的概率为50%(即:1/2),连续抛硬币直到出现正面朝上,记录第一次出现正面朝上的位置为k。
假如连续抛出3次硬币才出现正面朝上,那么k=3,k概率为:k = 1/2*1/2*1/2(即:(1/2)^k)。
这种找出硬币正面朝上的行为可以看作是伯努利过程,连续经历n次伯努利过程,可以找到n次正面朝上的k对应的位置(k1、k2、…...、kn)。
如图所示:
通过概率统计发现,n与k之间存在一定的联系,经历n次伯努利过程,找出k中的最大值k_max,n与k的关系为:n ≈ 2^k_max。
经历5次伯努利过程找出正面朝上的k:
- 第一次抛掷2次出现正面朝上:k=2,n=1。
- 第二次抛掷5次出现正面朝上:k=5,n=2。
- 第三次抛掷6次出现正面朝上:k=6,n=3。
- 第四次抛掷10次出现正面朝上:k=10,n=4。
- 第五次抛掷3次出现正面朝上:k=3,n=5。
按以上n与k的关系:显然2^10不等于4 ,出现很大误差。因此,需要通过优化算法来减小误差。
估值优化
估值优化方式:
- 增加伯努利过程轮数,取平均值。假设以x次伯努利试验为一轮,得到每轮找出的k_max,经历m轮伯努利试验后,平均值为:k_average = (k_max_1 + k_max_1 …... + k_max_m)/m。
- 增加修正因子,修正因子是一个不固定的值,会根据实际情况来进行值的调整。这种方式涉及很多数学理论知识,感兴趣的同学可自行查阅相关资料。
以上增加伯努利过程轮数,取平均值的方式就是LogLog算法的实现,其计算公式:
其中:
- DVLL:伯努利试验次数。
- constant:修正因子。
- m:伯努利试验轮数。
- 头上带一横杠的R:平均值。
HyperLogLog与LogLog的区别是HyperLogLog使用了调和平均数(即:倒数平均数),调和平均数相比平均数的优点是能降低极大值对平均值的影响,其计算公式:
HyperLogLog实现原理
HyperLogLog处理流程大致分为三步:计算bit串、分组、对应分组。
1)计算bit串
如图所示:
图中,任一key经过hash后得到一个64bit的bit串,其中:
- 低14bit作为分组编号,共2^14 = 16384个分组。
- 剩余50bit作为基数估计,从低位到高位(即:从右至左)找出第一个1的位置,记为k,与当前分组的记录的k_current进行比较,如果k大于k_current,则更新;反之,不做处理。
2)分组
在Redis中,通过采用分组(分轮)策略来提高估值的准确率,将一个以bit为单位、长度为L的大数组S,平均分为m组(即:m轮),每组占有相同的比特个数,设为p。得出以下关系:
- L = S.length
- L = m * p
- 以K为单位,S(内存占用)= L / 8 / 1024。
在Redis中,HyperLogLog设置为:m=16834,P=6,L=16834 * 6。占用内存 = 16834 * 6 / 8 / 1024 = 12K。
每组用6bit存储k_max的原因:6bit可以存储的最大值为63(即:6bit全部为1),目前计算机一般为64位或32位操作系统,因此6位最节省内存,又能满足需求。
如图所示:
3)对应分组
假设某个key经过hash后得到的bit串为0011001...010101000 00000000000110。其中:
- 低14bit(00000000000110)转换为10进制得到的值为6,即:分组编号为6,对应第6个分组。
- 剩余50bit(0011001...010101000)中,第一个1的位置为4(即:k_max=4),与当前分组编号比较:4<6,不做处理,即:该key对应第6个分组。
统计访问页面的独立用户数
假设要统计访问某个页面的独立用户数,每个用户存在一个唯一的用户ID,将用户ID通过hash散列到不同的分组中,每个分组对应一个k_max,最后将k_max代入公式中得到估算值。估算公式:
内存优化
如果每个key都占用12KB的存储空间,对于那些需求远小于12KB的key,会造成存储空间的浪费。因此,在Redis中,为了节省内存空间,在HyperLogLog内部会采用不同的编码方式进行存储:
- 稀疏编码(默认):应对数据量小的场景。
- 密集编码:应对数据量大的场景。
HyperLogLog底层采用SDS结构存储数据,为了区分普通字符串,在HyperLogLog头部使用了固定魔法字符串:HYLL。
满足以下任一条件时,稀疏编码会转换为密集编码:
- HyperLogLog中任一分组的计数值(即:k_max)大于32。
- HyperLogLog中存储内容大小超过参数hll-sparse-max-bytes(默认为3000字节)设定的值。
使用示例
客户端示例:
127.0.0.1:6379> pfadd page user1 # 添加key
(integer) 1
127.0.0.1:6379> pfcount page # 估算结果
(integer) 1
127.0.0.1:6379> pfadd page user2 user3 user4
(integer) 1
127.0.0.1:6379> pfcount page
(integer) 4
127.0.0.1:6379> pfadd page user2 user3 user1 # 添加已经存在的key
(integer) 0
127.0.0.1:6379> pfcount page
(integer) 4
127.0.0.1:6379>
代码实例:
/**
* HyperLogLog基数统计
* @return 估算结果
*/
@Override
public long hyperLogLog(){
for (int i = 0; i < 100000; i++) {
redisUtil.pfAdd("page","user" + i);
}
long result = redisUtil.pfCount("page");
log.info("用户总数:" + 100000 + ",统计结果 : " + result);
return result;
}
返回结果:
用户总数:100000,统计结果 : 99785
【阅读推荐】
更多精彩内容,如:
- Redis系列
- 数据结构与算法系列
- Nacos系列
- MySQL系列
- JVM系列
- Kafka系列
请移步【南秋同学】个人主页进行查阅。内容持续更新中......
【作者简介】
一枚热爱技术和生活的老贝比,专注于Java领域,关注【南秋同学】带你一起学习成长~
- 上一篇:Redis 高级特性之 HyperLoglog
- 下一篇:redis高级数据类型
相关推荐
- SpringBoot 各种分页查询方式详解(全网最全)
-
一、分页查询基础概念与原理1.1什么是分页查询分页查询是指将大量数据分割成多个小块(页)进行展示的技术,它是现代Web应用中必不可少的功能。想象一下你去图书馆找书,如果所有书都堆在一张桌子上,你很难...
- 《战场兄弟》全事件攻略 一般事件合同事件红装及隐藏职业攻略
-
《战场兄弟》全事件攻略,一般事件合同事件红装及隐藏职业攻略。《战场兄弟》事件奖励,事件条件。《战场兄弟》是OverhypeStudios制作发行的一款由xcom和桌游为灵感来源,以中世纪、低魔奇幻为...
- LoadRunner(loadrunner录制不到脚本)
-
一、核心组件与工作流程LoadRunner性能测试工具-并发测试-正版软件下载-使用教程-价格-官方代理商的架构围绕三大核心组件构建,形成完整测试闭环:VirtualUserGenerator(...
- Redis数据类型介绍(redis 数据类型)
-
介绍Redis支持五种数据类型:String(字符串),Hash(哈希),List(列表),Set(集合)及Zset(sortedset:有序集合)。1、字符串类型概述1.1、数据类型Redis支持...
- RMAN备份监控及优化总结(rman备份原理)
-
今天主要介绍一下如何对RMAN备份监控及优化,这里就不讲rman备份的一些原理了,仅供参考。一、监控RMAN备份1、确定备份源与备份设备的最大速度从磁盘读的速度和磁带写的带度、备份的速度不可能超出这两...
- 备份软件调用rman接口备份报错RMAN-06820 ORA-17629 ORA-17627
-
一、报错描述:备份归档报错无法连接主库进行归档,监听问题12541RMAN-06820:WARNING:failedtoarchivecurrentlogatprimarydatab...
- 增量备份修复物理备库gap(增量备份恢复数据库步骤)
-
适用场景:主备不同步,主库归档日志已删除且无备份.解决方案:主库增量备份修复dg备库中的gap.具体步骤:1、停止同步>alterdatabaserecovermanagedstand...
- 一分钟看懂,如何白嫖sql工具(白嫖数据库)
-
如何白嫖sql工具?1分钟看懂。今天分享一个免费的sql工具,毕竟现在比较火的NavicatDbeaverDatagrip都需要付费才能使用完整功能。幸亏今天有了这款SQLynx,它不仅支持国内外...
- 「开源资讯」数据管理与可视化分析平台,DataGear 1.6.1 发布
-
前言数据齿轮(DataGear)是一款数据库管理系统,使用Java语言开发,采用浏览器/服务器架构,以数据管理为核心功能,支持多种数据库。它的数据模型并不是原始的数据库表,而是融合了数据库表及表间关系...
- 您还在手工打造增删改查代码么,该神器带你脱离苦海
-
作为Java开发程序,日常开发中,都会使用Spring框架,完成日常的功能开发;在相关业务系统中,难免存在各种增删改查的接口需求开发。通常来说,实现增删改查有如下几个方式:纯手工打造,编写各种Cont...
- Linux基础知识(linux基础知识点及答案)
-
系统目录结构/bin:命令和应用程序。/boot:这里存放的是启动Linux时使用的一些核心文件,包括一些连接文件以及镜像文件。/dev:dev是Device(设备)的缩写,该目录...
- PL/SQL 杂谈(二)(pl/sql developer使用)
-
承接(一)部分。我们从结构和功能这两个方面展示PL/SQL的关键要素。可以看看PL/SQL的优雅的代码。写出一个好的代码,就和文科生写出一篇优秀的作文一样,那么赏心悦目。1、与SQL的集成PL/S...
- 电商ERP系统哪个好用?(电商erp哪个好一点)
-
电商ERP系统哪个好用?做电商的,谁还没被ERP折腾过?有老板说:“我们早就上了ERP,订单、库存、财务全搞定,系统用得飞起。”也有运营吐槽:“系统是上了,可库存老不准,订单漏单错单天天有,财务对账还...
- 汽车检测线系统实例,看集中控制与PLC分布控制
-
PLC可编程控制器,上个世纪70年代初,为取代早期继电器控制线路,开始采取存储指令方式,完成顺序控制而设计的。开始仅有逻辑运算、计时、计数等简单功能。随着微处理的发展,PLC可编程能力日益提高,已经能...
- 苹果五件套成公司年会奖品主角,几大小技巧教你玩转苹果新品
-
钱江晚报·小时新闻记者张云山随着春节的临近,各家大公司的年会又将陆续上演。上周,各大游戏公司的年会大奖,苹果五件套又成了标配。在上海的游戏公司中,莉莉丝奖品列表拉得相当长,从特等奖到九等奖还包含了特...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- oracle位图索引 (63)
- oracle批量插入数据 (62)
- oracle事务隔离级别 (53)
- oracle 空为0 (50)
- oracle主从同步 (55)
- oracle 乐观锁 (51)
- redis 命令 (78)
- php redis (88)
- redis 存储 (66)
- redis 锁 (69)
- 启动 redis (66)
- redis 时间 (56)
- redis 删除 (67)
- redis内存 (57)
- redis并发 (52)
- redis 主从 (69)
- redis 订阅 (51)
- redis 登录 (54)
- redis 面试 (58)
- 阿里 redis (59)
- redis 搭建 (53)
- redis的缓存 (55)
- lua redis (58)
- redis 连接池 (61)
- redis 限流 (51)