Redis源码中hyperloglog结构的实现原理是什么

Redis源码中hyperloglog结构的实现原理:1、密集存储结构;2、稀疏存储结构;3、存储转换;4、计数缓冲;5、对象头。密集存储结构是指,连续 16384 个 6bit 串成的字符串位图,结构比较简单。

Redis源码中hyperloglog结构的实现原理是什么-Worktile社区

1、密集存储结构

Redis内部使用字符串位图来存储HyperLogLog所有桶的计数值,密集存储的结构非常简单,就是连续 16384 个 6bit 串成的字符串位图。

通过桶的编号,计算他的6bit计算值:offset_bytes代表6bit计数值的起始字节位置偏移,而offset_bits表示起始比特位置偏移,例如idx代表桶的编号且位2,则offset_bytes = (idx*6)/8 = 1 ,offset_bits = (idx * 6)%8 = 4,所有第2个字节的第5位是bucket 2的计数值。需要注意的是字节位序是左边是低位,右边是高位,正好与平时我们接触到的相反。

如offset_bits小于2,那么6bit在一个字节内部,对应代码:

val = buffer[offset_bytes] >> offset_bits  # 向右移位

如果offset_bits大于2,那么就会跨越字节边界,需要两个字节拼接起来,对应代码:

# 低位值
low_val = buffer[offset_bytes] >> offset_bits
# 低位个数
low_bits = 8 - offset_bits
# 拼接,保留低6位
val = (high_val << low_bits | low_val) & 0b111111

2、稀疏存储结构

稀疏存储适用于很多计数值都是零的情况。

当多个连续桶的计数值都是零时,Redis使用一个字节来表示多少个桶的计数值都是零即00xxxxxx,前缀俩个零表示接下来的6bit整数值加1就是零值计数器的数量,比如 00010101表示连续 22 个零值计数器;6bit非常多能表示连续64个零值计数器,如果大于64个,采用来个字节表示即01xxxxxx yyyyyyyy,后面的 14bit 可以表示非常多连续 16384 个零值计数器。

如果连续几个桶的计数值非零,那就使用形如 1vvvvvxx 这样的一个字节来表示。中间 5bit 表示计数值,尾部 2bit 表示连续几个桶。它的意思是连续 (xx +1) 个计数值都是 (vvvvv + 1)。比如 10101011 表示连续 4 个计数值都是 11。注意这两个值都需要加 1,因为任意一个是零都意味着这个计数值为零,那就应该使用零计数值的形式来表示。注意计数值最大只能表示到32,而 HyperLogLog 的密集存储单个计数值用 6bit 表示,最大可以表示到 63。当稀疏存储的某个计数值需要调整到大于 32 时,Redis 就会立即转换 HyperLogLog 的存储结构,将稀疏存储转换成密集存储。

Redis 为了方便表达稀疏存储,它将上面三种字节表示形式分别赋予了一条指令:

  • ZERO:len 单个字节表示 00[len-1],连续非常多64个零计数值
  • VAL:value,len 单个字节表示 1[value-1][len-1],连续 len 个值为 value 的计数值
  • XZERO:len 双字节表示 01[len-1],连续非常多16384个零计数值

3、存储转换

当计数值达到一定程度后,稀疏存储将不可逆一次性转换成密集型存储,但要满足两个条件

  • 任意一个计数值从 32 变成 33,因为VAL指令已经无法容纳,它能表示的计数值最大为 32;
  • 稀疏存储占用的总字节数超过 3000 字节,这个阈值可以通过 hll_sparse_max_bytes 参数进行调整。

4、计数缓冲

计数为啥要增加缓冲:HyperLogLog的总计数值时根据16384个桶计数值进行调和平均后再基于修正因子公示计算出来的,它要遍历所有桶以及还要处理浮点数,这计算相对来说比较大,所以Redis使用一个64bit的字段缓冲这个总计数值,该字段较高如果是1,表示该缓冲已过期,如果是0,那么剩下的63bit就是该缓冲值。

HyperLogLog中任意一个桶的计数值发生变化,都会将计数缓冲设为过期。

5、对象头

对象头要存一些附加的字段,例如总计数缓冲、存储类型等:

struct hllhdr {
    char magic[4];      /* 魔术字符串"HYLL" */
    uint8_t encoding;   /* 存储类型 HLL_DENSE or HLL_SPARSE. */
    uint8_t notused[3]; /* 保留三个字节未来可能会使用 */
    uint8_t card[8];    /* 总计数缓存 */
    uint8_t registers[]; /* 所有桶的计数器 */
};

HyperLogLog整体的内部结构就是对象头加上16384个桶的计数值位图。

不能使用HyperLogLog指令来操作普通字符串,因为内部要检查对象头魔术字符串是否位HYLL。

如果字符串以 “HYLL\x00” 或者 “HYLL\x01” 开头,那么就可以使用 HyperLogLog 的指令:

127.0.0.1:6379> set codehole "HYLL\x01whatmagicthing"
OK
127.0.0.1:6379> get codehole
"HYLL\x01whatmagicthing"
127.0.0.1:6379> pfadd codehole python java golang
(integer) 1

这是因为 HyperLogLog 在执行指令前需要对内容进行格式检查,这个检查就是查看对象头的 magic 魔术字符串是否是 “HYLL” 以及 encoding 字段是否是 HLL_SPARSE=0 或者 HLL_DENSE=1 来判断当前的字符串是否是 HyperLogLog 计数器。如果是密集存储,还需要判断字符串的长度是否恰好等于密集计数器存储的长度:

int isHLLObjectOrReply(client *c, robj *o) {
    ...
    /* Magic should be "HYLL". */
    if (hdr->magic[0] != 'H' || hdr->magic[1] != 'Y' ||
        hdr->magic[2] != 'L' || hdr->magic[3] != 'L') goto invalid;
    if (hdr->encoding > HLL_MAX_ENCODING) goto invalid;
    if (hdr->encoding == HLL_DENSE &&
        stringObjectLen(o) != HLL_DENSE_SIZE) goto invalid;
    return C_OK;
invalid:
    addReplySds(c,
        sdsnew("-WRONGTYPE Key is not a valid "
               "HyperLogLog string value.\r\n"));
    return C_ERR;
}

HyperLogLog 和 字符串的关系就好比 Geo 和 zset 的关系。你也可以使用任意 zset 的指令来访问 Geo 数据结构,因为 Geo 内部存储就是使用了一个纯粹的 zset来记录元素的地理位置。

延伸阅读

HyperLogLog算法

HyperLogLog算法是一种非常巧妙的近似统计大量去重元素数量的算法,它内部维护了16384个桶来记录各自桶的元素数量,当一个元素过来,它会散列到其中一个桶。当元素到来时,通过 hash 算法将这个元素分派到其中的一个小集合存储,同样的元素总是会散列到同样的小集合。这样总的计数就是所有小集合大小的总和。使用这种方式精确计数除了可以增加元素外,还可以减少元素。

一个HyperLogLog实际占用的空间大约是 13684 * 6bit / 8 = 12k 字节。但是在计数比较小的时候,大多数桶的计数值都是零。如果 12k 字节里面太多的字节都是零,那么这个空间是可以适当节约一下的。Redis 在计数值比较小的情况下采用了稀疏存储,稀疏存储的空间占用远远小于 12k 字节。相对于稀疏存储的就是密集存储,密集存储会恒定占用 12k 字节。

文章标题:Redis源码中hyperloglog结构的实现原理是什么,发布者:Z, ZLW,转载请注明出处:https://worktile.com/kb/p/34716

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
Z, ZLWZ, ZLW站长
上一篇 2023年1月8日 上午5:30
下一篇 2023年1月8日 上午6:18

相关推荐

  • mysql常用命令

    MySQL是一款流行的开源关系型数据库管理系统,它支持标准的SQL(结构化查询语言)。MySQL的常用命令可以分为:数据定义语言(DDL),包括创建、修改、删除数据库和表等;数据操作语言(DML),包括插入、更新、删除数据等;数据查询语言(DQL),包括查询数据等。这些命令可以帮助用户有效管理和操作…

    2023年5月15日
    19300
  • autowired和resource注解的区别

    @Autowired和@Resource注解的区别主要体现在以下五个方面:1.来源不同;2.注入方式不同;3.默认处理方式不同;4.属性名不同;5.兼容性不同。从整体上来看,这两个注解虽然都用于实现依赖注入,但是各自的侧重点和使用情况有所不同。 1.来源不同 @Autowired是Spring框架的…

    2023年5月18日
    3.3K00
  • java各版本基本语法有没有区别

    java各版本基本语法没有区别。基本语法没有多大区别,java的特点就是语法特性,演变比较慢,但是几乎不会有语法上的breaking changes。比如现在把1.2时代的java代码,拿过来,放到18或者将来21,25等高版本的jdk上一样可以编译通过。 java各版本基本语法没有区别,java的…

    2023年2月12日
    17000
  • VR时代的主流编程语言是什么

    VR时代的主流编程语言有:1、C#;2、 C++语言;3、Java;4、 其他计算机图形学与GPU编程。C#基础语法与算法、面向对象编程、C#是数据结构与高级语法;是做U3D的基础语言。 一、C# C#基础语法与算法、面向对象编程、C#是数据结构与高级语法;是做U3D的基础语言。而Unity 把 C…

    2023年2月9日
    52600
  • java oa系统有哪些模块

    java oa系统有以下模块:一、个人事务模块;二、工作流模块;三、行政事务模块;四、人力资源模块;五、公文档案管理模块;六、EPR管理模块;七、CMS管理模块;八、系统管理模块。个人事务模块有内部邮件、手机短信管理、消息管理、提醒设置、微讯群管理等。 一、个人事务模块 内部邮件、手机短信管理、消息…

    2023年3月31日
    11100
  • jsp非法字符有哪些

    jsp非法字符有:1、< (小于号);2、>(大于号);3、& (和号);4、” (双引号);5、’ (单引号);6、/ (斜杠);7、% (百分号);8、! (感叹号);9、@ (at符号);10、$ (美元符号)等。这些字符在JSP页面中必须进行转义或避免使用。 一、jsp的…

    2023年3月25日
    12700
  • 请问CS结构和BS结构的详细区别

    CS结构和BS结构的区别在于:1、CS、BS架构定义区别;2、CS、BS 对硬件环境的要求区别。CS是客户端-服务器结构。C/S结构主要特点是交互性强、具有安全的存取模式。BS是浏览器-服务器结构,是目前应用系统的发展方向。 1、CS、BS架构定义区别 CS(Client/Server):客户端&#…

    2023年2月7日
    22900
  • 项目流程管理包括哪些内容

    项目流程管理包括:一、项目计划;二、项目执行;三、项目监控;四、项目收尾。项目计划是项目流程管理的关键环节,它是项目管理的基础。项目计划的目的是制定出项目实现的时间表、成本预算、质量标准、风险控制计划等。 一、项目计划 项目计划是项目流程管理的关键环节,它是项目管理的基础。项目计划的目的是制定出项目…

    2023年4月30日
    7100
  • 二层交换机、三次交换机的区别是什么

    二层交换机、三次交换机的区别:1、工作层级不同;2、原理不同;3、功能不同;4、应用不同;5、支持协议不同;6、工作方式不同;7、工作模式不同;8、交换方式不同。工作层级不同是指,二层交换机工作在数据链路层,三层交换机工作在网络层。 一、二层交换机、三次交换机的区别 1、工作层级不同 二层交换机工作…

    2023年3月28日
    50200
  • 微信小程序后台可以用什么数据库

    在小程序开发过程中,后台数据库通常有以下几种常见选择:1、MySQL;2、MongoDB;3、云数据库;4、微信小程序云开发数据库。MySQL是最常见的关系型数据库之一,有着良好的社区支持和丰富的文档。它是一个完全开源的数据库,支持多种数据类型,适合处理结构化数据。 1、MySQL MySQL是最常…

    2023年7月12日
    74500

发表回复

登录后才能评论
站长微信
站长微信
电话联系

400-800-1024

工作日9:30-21:00在线

分享本页
返回顶部