当前位置: 首页 > ai >正文

Redis 的整数集合:像分类收纳盒一样的整数专属存储

目录

一、先懂定位:为什么需要整数集合?(衔接哈希表)

二、整数集合的结构:像 “贴了规格标签的收纳盒”

1. encoding:收纳盒的 “规格标签”(核心:决定格子大小)

2. length:收纳盒的 “物品数量”

3. contents:收纳盒的 “有序格子”

三、核心操作:“升级”—— 从小盒子换大盒子(重点)

举个具体例子:从 int16 升级到 int32

步骤 1:换大收纳盒(扩展空间)

步骤 2:把小珠子换成大珠子(转换元素类型)

步骤 3:放进新的大珠子(添加新元素)

3.1、为什么只能升级,不能降级?(Redis 的权衡)

四、适用场景:什么时候用整数集合?(对比哈希表)

五、总结:整数集合的设计逻辑(衔接 Redis 整体哲学)


如果说 SDS 是 “智能价签”、压缩列表是 “紧凑货架”、字典是 “万能储物柜”,那整数集合(intset) 就是 Redis 为 “少量整数” 定制的分类收纳盒—— 专门用来存纯整数、数量不多的集合(比如SADD nums 10 20 30),核心优势是 “省空间、保有序、无重复”。它和哈希表(hashtable)是集合键的两种底层实现,就像收纳时 “少量小物件用分类盒,大量 / 杂物件用储物柜”。

一、先懂定位:为什么需要整数集合?(衔接哈希表)

之前学的哈希表(hashtable) 虽然能存集合(比如非整数元素SADD fruits "apple"),但有个 “内存浪费” 的问题:每个元素要存键(元素本身)和值(NULL,因为集合只需存存在性),还要处理哈希冲突。比如存 3 个小整数10、20、30,哈希表的指针、哈希槽等开销,比整数本身的空间还大。

而整数集合的思路是:用 “统一规格的数组” 存整数,不存额外指针 / 哈希信息,还保持有序无重复—— 就像用 “带格子的收纳盒” 装小珠子,每个格子只放一颗,按大小排好,没有多余的包装,省空间又好查找。

二、整数集合的结构:像 “贴了规格标签的收纳盒”

整数集合的底层是intset结构体,对应 “分类收纳盒” 的三个核心部分:规格标签、物品数量、收纳格子。我们先看结构定义,再逐个类比拆解:

typedef struct intset {uint32_t encoding;   // 收纳盒的“规格标签”(存什么类型的整数)uint32_t length;     // 收纳盒里的“物品数量”(元素个数)int8_t contents[];   // 收纳盒的“格子数组”(实际存整数的地方)
} intset;

1. encoding:收纳盒的 “规格标签”(核心:决定格子大小)

encoding就像收纳盒上贴的 “适用物品规格”,比如 “仅放直径≤5mm 的小珠子”,它决定了contents数组实际能存什么类型的整数:

  • INTSET_ENC_INT16:“小格子规格”—— 每个格子存int16_t类型(范围:-32768 ~ 32767),比如 10、20、32767。
  • INTSET_ENC_INT32:“中格子规格”—— 每个格子存int32_t类型(范围:-2147483648 ~ 2147483647),比如 32768(超过 int16 最大值)、100000。
  • INTSET_ENC_INT64:“大格子规格”—— 每个格子存int64_t类型(范围极大),比如 2147483648(超过 int32 最大值)。

关键注意:虽然contents声明是int8_t(1 字节小格子),但实际格子大小由encoding决定!就像收纳盒表面写 “小格子”,但贴的规格标签是 “中格子”,实际就能放大珠子 —— 这是 Redis 的 “灵活声明”,为了兼容不同规格。

2. length:收纳盒的 “物品数量”

length直接记录收纳盒里有多少个整数(无重复),比如存了 10、20、30,length=3。和压缩列表的zllen类似,能快速获取元素数量(O (1) 复杂度),不用数格子。

3. contents:收纳盒的 “有序格子”

contents是实际存整数的数组,有两个核心特性:

  • 有序:按从小到大排列,比如存 10、30、20,最终格子里是[10,20,30]—— 方便快速查找(用二分法,O (logN) 复杂度)。
  • 无重复:同一个整数不会出现两次,比如再存 20,Redis 会检查格子里已有,不重复添加 —— 就像收纳盒里不会有两颗一样大的珠子。

三、核心操作:“升级”—— 从小盒子换大盒子(重点)

整数集合最特殊的逻辑是 “自动升级”:当要放的整数 “太大”(超过当前encoding的范围),就必须把整个收纳盒换成更大规格,再统一转换原有元素的 “大小”,最后放进新元素。这就像 “小珠子收纳盒里要放大珠子,必须先换大盒子,把原来的小珠子都换成大珠子,再放大珠子”。

举个具体例子:从 int16 升级到 int32

假设现在有个 “小格子收纳盒”(encoding=INTSET_ENC_INT16),已经放了两个小整数:10、32767(int16 的最大值)。现在要放 32768(超过 int16 的最大值,必须用 int32),升级步骤对应生活场景:

步骤 1:换大收纳盒(扩展空间)
  • 原来的小盒子:每个格子占 2 字节(int16),2 个元素共 4 字节。
  • 新的大盒子:每个格子占 4 字节(int32),需要存 3 个元素(原有 2 个 + 新 1 个),总空间 = 3×4=12 字节。
  • Redis 会申请 12 字节的新空间,代替原来的 4 字节 —— 就像把 2 格的小盒子,换成 3 格的大盒子。
步骤 2:把小珠子换成大珠子(转换元素类型)
  • 不能直接把小珠子(int16 的 10、32767)放进大格子,必须先 “放大” 成大珠子(int32 的 10、32767)。
  • 转换时要保持顺序:原来小盒子里是[10,32767],转换后大盒子的前两格还是[10,32767](只是每个占 4 字节)—— 就像把小珠子装进大托里,位置不变。
步骤 3:放进新的大珠子(添加新元素)
  • 把 32768(int32 类型)放进大盒子的最后一格,最终格子里是[10,32767,32768],保持有序。
  • 最后更新encoding为 INTSET_ENC_INT32,length为 3—— 收纳盒的规格标签和数量标签同步更新。

3.1、为什么只能升级,不能降级?(Redis 的权衡)

升级后就算删了 “大珠子”(比如把 32768 删掉,只剩 10、32767),Redis 也不会把大盒子换回小盒子 —— 这就是 “无降级” 规则,原因很简单:

  1. 麻烦且没必要:降级需要先检查所有元素是否都符合小规格,再转换类型、缩小空间 —— 就像把大珠子换回小珠子,再换小盒子,步骤多,浪费时间。
  2. 效率优先:Redis 认为 “少量内存浪费” 比 “频繁降级的性能损耗” 更划算。比如大盒子里放小珠子,多占的内存不多(每个元素多 2 字节),但省去了降级的麻烦。
  3. 避免风险:如果降级后又要放大珠子,得再次升级,反复操作反而更慢 —— 就像换了小盒子又要换大的,不如一直用大的。

四、适用场景:什么时候用整数集合?(对比哈希表)

Redis 选择整数集合还是哈希表,遵循 “整数优先、少量优先” 的原则,和压缩列表的 “小数据优先” 思路一致:

集合类型底层实现原因(生活类比)
纯整数元素 + 元素少(默认<512 个)整数集合分类收纳盒存少量小珠子,省空间、好排序
非整数元素(如字符串)哈希表万能储物柜存杂物件,不管类型,查找快
纯整数元素 + 元素多哈希表收纳盒格子太多不好翻,储物柜(哈希表)更快

举例:

SADD nums 1 2 3(纯整数、少元素)→ 整数集合;

SADD fruits "apple" "banana"(非整数)→ 哈希表;

SADD big_nums 1 2 ... 1000(多元素)→ 哈希表。

五、总结:整数集合的设计逻辑(衔接 Redis 整体哲学)

整数集合的所有设计,都和之前学的 SDS、压缩列表、跳跃表一脉相承,体现 Redis“因地制宜、权衡取舍” 的核心思路:

  • SDS:用 “len+free” 平衡 “字符串扩展” 和 “内存浪费”—— 取舍:预分配少量空间换扩展效率;
  • 压缩列表:用 “连续内存 + 可变标签” 平衡 “内存节省” 和 “连锁更新风险”—— 取舍:接受罕见更新换极致省内存;
  • 整数集合:用 “统一规格数组 + 自动升级” 平衡 “内存节省” 和 “类型兼容”—— 取舍:不支持降级换操作简单、效率高。

简单说,整数集合就是 Redis 为 “少量整数集合” 量身定制的 “高效收纳盒”:它没有哈希表的灵活,但胜在省空间、有序;没有压缩列表的复杂,胜在操作简单、只存整数。这种 “专物专用” 的设计,正是 Redis 能兼顾性能和内存的关键。

http://www.xdnf.cn/news/19669.html

相关文章:

  • Obsidian本地笔记工具:构建知识网络关联笔记,支持Markdown与插件生态及知识图谱生成
  • LoRA至今历程回顾(74)
  • 《水浒智慧》第二部 “英雄是怎么炼成的” (上篇)读书笔记
  • Linux文本处理工具
  • 机器算法(五)模型选择与调优
  • 基于SpringBoot的广科大在线图书管理系统设计与实现(代码+数据库+LW)
  • 探索JavaScript机器学习:几款流行的库推荐
  • Leetcode 3670. Maximum Product of Two Integers With No Common Bits
  • HTML第四课:个人简介页面开发
  • 下载速度爆表,全平台通用,免费拿走!
  • DaemonSet Job CronJob 概念理解
  • XML在线格式化 - 加菲工具
  • Leetcode二分查找(3)
  • 移动硬盘删除东西后,没有释放空间
  • 【机器学习入门】5.2 回归的起源——从身高遗传到线性模型的百年演变
  • 狄利克雷分布作用
  • CentOS 创建站点
  • 二进制流进行预览pdf、excel、docx
  • Cisco FMC利用sftp Server拷贝文件方法
  • 0902 C++类的匿名对象
  • 面试问题:c++的内存管理方式,delete的使用,vector的resize和reverse,容量拓展
  • uni-app 布局之 Flex
  • 基于STM32与华为云联动的智能电动车充电桩管理系统
  • QSlider 和 QProgressBar 的区别与实践
  • 【Linux基础】Linux系统启动:深入解析Linux系统启动完整流程
  • 仿真波导中超短脉冲传输中的各种非线性效应所产生的超连续谱
  • AI如何理解PDF中的表格和图片?
  • qt安装FFmpeg后编译遇到error: collect2.exe: error: ld returned 1 exit status错误
  • 链表题类型注解解惑:理解Optional,理解ListNode
  • 数据结构--跳表(Skip List)