【Redis数据结构】intset 整数集合

news/2025/2/21 11:01:47

1. intset 简介

整数集合(intset) 是 redis 当中 set 集合类型的底层实现方式之一,即当存放的元素全都是整数类型且元素数量不多的时候,redis就会使用 intset 作为 set 的底层实现

如上图所示,当我们向 s1 当中插入 [1, 2, 3] 三个元素后,使用OBJECT ENCODING s1可以观察到底层结构使用的是intset整数集合

2. intset 底层结构

redis 的源码包src/intset.h中就有如下图所示结构体声明:

其中各个字段的含义如下:

  • contents:本质上是一个字节数组,用于存放存储的整数值(从小到大有序排列且不重复)
  • length:集合中包含的元素个数
  • encoding:contents 数组中存储的整数元素的类型(可以为 int16_t、int32_t、int64_t)
    • encoding字段的值为INTSET_ENC_INT16时,表示数组每一项都是 int16_t 类型大小
    • encoding字段的值为INTSET_ENC_INT32时,表示数组每一项都是 int32_t 类型大小
    • encoding字段的值为INTSET_ENC_INT64时,表示数组每一项都是 int64_t 类型大小

如上图所示:该 intset 结构存储了四个整数:[-32768, 1, 2, 8],因此 length 的值为 4 ,并且每个元素的大小都不超过 int16_t 的范围,所以 encoding 的值为 INTSET_ENC_INT16,此时 contents 数组占用的存储空间就是 sizeof(int16_t) * length = 64 bit

💡 温馨提示:有些小伙伴可能会疑惑为什么 contents 数组的类型统一是 int8_t,那不是存储不了 int64_t 类型的值吗?可以这样来理解:一个 int64_t 类型本质就是 8 个字节,因此完全可以使用多个最小粒度的 int_8 类型表示,将来取值的时候也只需要根据 encoding 类型获取相应长度的数组元素

3. intset 升级

3.1 升级流程

当我们向一个 intset 类型的集合当中插入新元素的时候,如果新元素的类型大小超过了当前的encoding表示的元素类型大小,此时就需要进行升级(upgrade)操作,其流程如下:

  1. 首先计算新数组需要开辟的内存大小:(length + 1) * 新元素类型大小
  2. 然后 倒序 地将原数组存储的数值复制到新的索引位置
  3. 将新元素复制到对应的索引位置
  4. 修改结构体当中的 encoding 和 length 字段

如上图所示,当前的 intset 类型存储的数据是[1, 2, 3],可以使用 int16 来表示,但是如果现在要插入新元素:65535,此时该新元素的类型大小为 int32_t,因此需要进行升级操作:

  1. 首先计算升级后的新数组的大小:sizeof(int32_t) * (length + 1) = 128 位
  2. 接下来分别倒序将原来的元素进行复制,比如元素 3 的新索引处应该是 2,起始地址就是 2 * sizeof(int32_t),即存储到 64 - 95 位处,同理元素 2 的新索引处是 1, 起始地址就是 1 * sizeof(int32_t),即存储到 32 - 63 位处,元素 1 存储到 0 - 31 位处
  3. 将新元素移动到对应位置,元素 65535 的索引应该是 3 ,因此复制到 96 - 127 位处
  4. 修改 encoding 为 INTSET_ENC_INT32,length 为 4

💡 注意:为什么需要倒序复制呢?这是为了避免覆盖原先的值,比如如果按照正序复制,那么需要先将元素 1 的值覆盖到 0 - 31位,然后获取原先元素 2 的位置(16 - 31位)发现已经被填充了!

3.2 升级的好处

intset 结构的升级主要有以下两点好处:

  1. 灵活度高:由于 C 语言是一门静态类型语言,因此 int64_t 类型的数组只能存储 int64_t 类型元素,而 intset 采用的升级流程更加灵活(可以运行时切换元素类型)
  2. 节省内存空间:没有固定使用 int64_t 类型存储 int16_t、int32_t 类型的元素,而是采用了运行时动态升级切换的策略,如果存储的元素均为 int16_t 类型,那么只会分配 int16_t 为单位的内存空间

3.3 降级

那么就有小伙伴有疑问了:有升级策略,那有没有降级策略呢?答案是没有!即如果当前 intset 元素值为 [1, 2, 65535] 时,删除 65535 元素,encoding 的值仍然保持为 INTSET_ENC_INT32_t

4. intset 总结

本篇博客内容总结如下:

  • intset 是 redis 内部 set 集合的实现方式之一
  • intset 内部包含三部分:encoding、length、contents,其中保存的元素是从小到大有序排列且无重复的
  • intset 只有升级流程,没有降级流程

5. 参考

📖 参考内容:

  1. 《Redis设计与实现》 黄健宏 著
  2. 黑马程序员 Redis 课程:https://www.bilibili.com/video/BV1cr4y1671t

http://www.niftyadmin.cn/n/5860737.html

相关文章

项目设置内网 IP 访问实现方案

在我们平常的开发工作中,项目开发、测试完成后进行部署上线。比如电商网站、新闻网站、社交网站等,通常对访问不会进行限制。但是像企业内部网站、内部管理系统等,这种系统一般都需要限制访问,比如内网才能访问等。那么一个网站应…

人工智能与自闭症的研究现状及未来趋势

人工智能与自闭症的研究现状及未来趋势 摘要:本研究旨在通过文献计量学方法,分析人工智能领域内关于自闭症研究的现状与未来趋势。研究基于中国知网(CNKI)、万方数据库(WanFang)、维普数据库(V…

【Python】迭代器与生成器详解,附代码(可迭代对象、定义、实现方式、区别、使用场景)

文章目录 1. 可迭代对象1.1 常见的可迭代对象1.2 迭代器和生成器 2. 迭代器2.1 定义2.2 原理2.3 特点2.4 示例2.4.1 for语句进行遍历2.4.2 next() 函数进行遍历2.4.3 自定义迭代器 2.5 内置迭代器 3. 生成器3.1 定义3.2 创建方式3.2.1 生成器表达式3.2.2 生成器函数 3.3 特点 4…

C# 背景 透明 抗锯齿 (效果完美)

主要是通过 P/Invoke 技术调用 Windows API 函数 gdi32.dll/user32.dll,同时定义了一些结构体来配合这些 API 函数的使用,常用于处理图形绘制、窗口显示等操作。 运行查看效果 局部放大,抗锯齿效果很不错,尾巴毛毛清晰可见。 using System; u…

java项目之学术成果管理系统源码(ssm+前端+mysql)

项目简介 学术成果管理系统实现了以下功能: 宠物医院信息管理系统的主要使用者分为管理员:个人中心、用户管理、医生管理、医学知识管理、科室信息管理、医生信息管理、预约挂号管理、医嘱信息管理、药品信息管理、订单信息管理、留言板管理、系统管理…

PWM(脉宽调制)技术详解:从基础到应用实践示例

PWM(脉宽调制)技术详解:从基础到应用实践示例 目录 PWM(脉宽调制)技术详解:从基础到应用实践示例学前思考:一、PWM概述二、PWM的基本原理三、PWM的应用场景四、PWM的硬件配置与使用五、PWM的编程…

ZLG嵌入式笔记 | RTC时钟偶发性延时或超时该怎么办?

嵌入式系统运行时,RTC 时钟受多种因素干扰致延时或超时,影响系统时间同步与功能稳定。本文将提出从硬件适配到软件算法优化的综合性方案,以解决此问题,保障 RTC 时钟的精确性与可靠性。 引起延时和超时的主要原因是计时系统使用的…

Java 面试笔记 - Java基础

1 、JDK、JRE 和 JVM 是 Java 开发与运行环境中的三个核心组件,它们之间的关系和区别如下: 1. JDK (Java Development Kit) 定义:JDK 是 Java 开发工具包,包含了开发 Java 应用程序所需的所有工具和库。包含内容: 编…