整数集合(intset)是 Redis 用于保存整数值的集合抽象数据结构, 它可以保存类型为 `int16_t` 、 `int32_t` 或者 `int64_t` 的整数值, 并且保证集合中不会出现重复元素。
每个 `intset.h/intset` 结构表示一个整数集合:
~~~
typedef struct intset {
// 编码方式
uint32_t encoding;
// 集合包含的元素数量
uint32_t length;
// 保存元素的数组
int8_t contents[];
} intset;
~~~
`contents` 数组是整数集合的底层实现: 整数集合的每个元素都是 `contents` 数组的一个数组项(item), 各个项在数组中按值的大小从小到大有序地排列, 并且数组中不包含任何重复项。
`length` 属性记录了整数集合包含的元素数量, 也即是 `contents` 数组的长度。
虽然 `intset` 结构将 `contents` 属性声明为 `int8_t` 类型的数组, 但实际上 `contents` 数组并不保存任何 `int8_t` 类型的值 —— `contents` 数组的真正类型取决于 `encoding` 属性的值:
* 如果 `encoding` 属性的值为 `INTSET_ENC_INT16` , 那么 `contents` 就是一个 `int16_t` 类型的数组, 数组里的每个项都是一个 `int16_t` 类型的整数值 (最小值为 `-32,768` ,最大值为 `32,767` )。
* 如果 `encoding` 属性的值为 `INTSET_ENC_INT32` , 那么 `contents` 就是一个 `int32_t` 类型的数组, 数组里的每个项都是一个 `int32_t` 类型的整数值 (最小值为 `-2,147,483,648` ,最大值为 `2,147,483,647` )。
* 如果 `encoding` 属性的值为 `INTSET_ENC_INT64` , 那么 `contents` 就是一个 `int64_t` 类型的数组, 数组里的每个项都是一个 `int64_t` 类型的整数值 (最小值为 `-9,223,372,036,854,775,808` ,最大值为 `9,223,372,036,854,775,807` )。
图 6-1 展示了一个整数集合示例:
* `encoding` 属性的值为 `INTSET_ENC_INT16` , 表示整数集合的底层实现为 `int16_t` 类型的数组, 而集合保存的都是 `int16_t` 类型的整数值。
* `length` 属性的值为 `5` , 表示整数集合包含五个元素。
* `contents` 数组按从小到大的顺序保存着集合中的五个元素。
* 因为每个集合元素都是 `int16_t` 类型的整数值, 所以 `contents` 数组的大小等于 `sizeof(int16_t) * 5 = 16 * 5 = 80` 位。
![](https://box.kancloud.cn/2015-09-13_55f51a1132a4b.png)
图 6-2 展示了另一个整数集合示例:
* `encoding` 属性的值为 `INTSET_ENC_INT64` , 表示整数集合的底层实现为 `int64_t` 类型的数组, 而数组中保存的都是 `int64_t` 类型的整数值。
* `length` 属性的值为 `4` , 表示整数集合包含四个元素。
* `contents` 数组按从小到大的顺序保存着集合中的四个元素。
* 因为每个集合元素都是 `int64_t` 类型的整数值, 所以 `contents` 数组的大小为 `sizeof(int64_t) * 4 = 64 * 4 = 256` 位。
![](https://box.kancloud.cn/2015-09-13_55f51a139a148.png)
虽然 `contents` 数组保存的四个整数值中, 只有 `-2675256175807981027` 是真正需要用 `int64_t` 类型来保存的, 而其他的 `1` 、 `3` 、 `5` 三个值都可以用 `int16_t` 类型来保存, 不过根据整数集合的升级规则, 当向一个底层为 `int16_t` 数组的整数集合添加一个 `int64_t` 类型的整数值时, 整数集合已有的所有元素都会被转换成 `int64_t` 类型, 所以 `contents` 数组保存的四个整数值都是 `int64_t` 类型的, 不仅仅是`-2675256175807981027` 。
接下来的一节将对整数集合的升级操作进行详细的介绍。
- 介绍
- 前言
- 致谢
- 简介
- 第一部分:数据结构与对象
- 简单动态字符串
- SDS 的定义
- SDS 与 C 字符串的区别
- SDS API
- 重点回顾
- 参考资料
- 链表
- 链表和链表节点的实现
- 链表和链表节点的 API
- 重点回顾
- 字典
- 字典的实现
- 哈希算法
- 解决键冲突
- rehash
- 渐进式 rehash
- 字典 API
- 重点回顾
- 跳跃表
- 跳跃表的实现
- 跳跃表 API
- 重点回顾
- 整数集合
- 整数集合的实现
- 升级
- 升级的好处
- 降级
- 整数集合 API
- 重点回顾
- 压缩列表
- 压缩列表的构成
- 压缩列表节点的构成
- 连锁更新
- 压缩列表 API
- 重点回顾
- 对象
- 对象的类型与编码
- 字符串对象
- 列表对象
- 哈希对象
- 集合对象
- 有序集合对象
- 类型检查与命令多态
- 内存回收
- 对象共享
- 对象的空转时长
- 重点回顾
- 第二部分:单机数据库的实现
- 数据库
- 数据库键空间
- 重点回顾
- RDB 持久化
- RDB 文件结构
- 重点回顾
- AOF 持久化
- AOF 持久化的实现
- 重点回顾
- 事件
- 文件事件
- 重点回顾
- 参考资料
- 客户端
- 客户端属性
- 重点回顾
- 服务器
- 命令请求的执行过程
- 重点回顾
- 第三部分:多机数据库的实现
- 复制
- 旧版复制功能的实现
- 重点回顾
- Sentinel
- 启动并初始化 Sentinel
- 重点回顾
- 参考资料
- 集群
- 节点
- 重点回顾
- 第四部分:独立功能的实现
- 发布与订阅
- 频道的订阅与退订
- 重点回顾
- 参考资料
- 事务
- 事务的实现
- 重点回顾
- Lua 脚本
- 创建并修改 Lua 环境
- 重点回顾
- 排序
- SORT <key> 命令的实现
- 重点回顾
- 二进制位数组
- GETBIT 命令的实现
- 重点回顾
- 慢查询日志
- 慢查询记录的保存
- 慢查询日志的阅览和删除
- 添加新日志
- 重点回顾
- 监视器
- 成为监视器
- 向监视器发送命令信息
- 重点回顾
- 源码、相关资源和勘误