集合对象的编码可以是 `intset` 或者 `hashtable` 。
`intset` 编码的集合对象使用整数集合作为底层实现, 集合对象包含的所有元素都被保存在整数集合里面。
举个例子, 以下代码将创建一个如图 8-12 所示的 `intset` 编码集合对象:
~~~
redis> SADD numbers 1 3 5
(integer) 3
~~~
![](https://box.kancloud.cn/2015-09-13_55f5206caed9d.png)
另一方面, `hashtable` 编码的集合对象使用字典作为底层实现, 字典的每个键都是一个字符串对象, 每个字符串对象包含了一个集合元素, 而字典的值则全部被设置为 `NULL` 。
举个例子, 以下代码将创建一个如图 8-13 所示的 `hashtable` 编码集合对象:
~~~
redis> SADD fruits "apple" "banana" "cherry"
(integer) 3
~~~
![](https://box.kancloud.cn/2015-09-13_55f5206eae7b1.png)
## 编码的转换
当集合对象可以同时满足以下两个条件时, 对象使用 `intset` 编码:
1. 集合对象保存的所有元素都是整数值;
2. 集合对象保存的元素数量不超过 `512` 个;
不能满足这两个条件的集合对象需要使用 `hashtable` 编码。
注意
第二个条件的上限值是可以修改的, 具体请看配置文件中关于 `set-max-intset-entries` 选项的说明。
对于使用 `intset` 编码的集合对象来说, 当使用 `intset` 编码所需的两个条件的任意一个不能被满足时, 对象的编码转换操作就会被执行: 原本保存在整数集合中的所有元素都会被转移并保存到字典里面, 并且对象的编码也会从 `intset` 变为 `hashtable` 。
举个例子, 以下代码创建了一个只包含整数元素的集合对象, 该对象的编码为 `intset` :
~~~
redis> SADD numbers 1 3 5
(integer) 3
redis> OBJECT ENCODING numbers
"intset"
~~~
不过, 只要我们向这个只包含整数元素的集合对象添加一个字符串元素, 集合对象的编码转移操作就会被执行:
~~~
redis> SADD numbers "seven"
(integer) 1
redis> OBJECT ENCODING numbers
"hashtable"
~~~
除此之外, 如果我们创建一个包含 `512` 个整数元素的集合对象, 那么对象的编码应该会是 `intset` :
~~~
redis> EVAL "for i=1, 512 do redis.call('SADD', KEYS[1], i) end" 1 integers
(nil)
redis> SCARD integers
(integer) 512
redis> OBJECT ENCODING integers
"intset"
~~~
但是, 只要我们再向集合添加一个新的整数元素, 使得这个集合的元素数量变成 `513` , 那么对象的编码转换操作就会被执行:
~~~
redis> SADD integers 10086
(integer) 1
redis> SCARD integers
(integer) 513
redis> OBJECT ENCODING integers
"hashtable"
~~~
## 集合命令的实现
因为集合键的值为集合对象, 所以用于集合键的所有命令都是针对集合对象来构建的, 表 8-10 列出了其中一部分集合键命令, 以及这些命令在不同编码的集合对象下的实现方法。
* * *
表 8-10 集合命令的实现方法
| 命令 | `intset` 编码的实现方法 | `hashtable` 编码的实现方法 |
| --- | --- | --- |
| SADD | 调用 `intsetAdd` 函数, 将所有新元素添加到整数集合里面。 | 调用 `dictAdd` , 以新元素为键, `NULL` 为值, 将键值对添加到字典里面。 |
| SCARD | 调用 `intsetLen` 函数, 返回整数集合所包含的元素数量, 这个数量就是集合对象所包含的元素数量。 | 调用 `dictSize` 函数, 返回字典所包含的键值对数量, 这个数量就是集合对象所包含的元素数量。 |
| SISMEMBER | 调用 `intsetFind` 函数, 在整数集合中查找给定的元素, 如果找到了说明元素存在于集合, 没找到则说明元素不存在于集合。 | 调用 `dictFind` 函数, 在字典的键中查找给定的元素, 如果找到了说明元素存在于集合, 没找到则说明元素不存在于集合。 |
| SMEMBERS | 遍历整个整数集合, 使用 `intsetGet` 函数返回集合元素。 | 遍历整个字典, 使用 `dictGetKey` 函数返回字典的键作为集合元素。 |
| SRANDMEMBER | 调用 `intsetRandom` 函数, 从整数集合中随机返回一个元素。 | 调用 `dictGetRandomKey` 函数, 从字典中随机返回一个字典键。 |
| SPOP | 调用 `intsetRandom` 函数, 从整数集合中随机取出一个元素, 在将这个随机元素返回给客户端之后, 调用 `intsetRemove` 函数, 将随机元素从整数集合中删除掉。 | 调用 `dictGetRandomKey` 函数, 从字典中随机取出一个字典键, 在将这个随机字典键的值返回给客户端之后, 调用`dictDelete` 函数, 从字典中删除随机字典键所对应的键值对。 |
| SREM | 调用 `intsetRemove` 函数, 从整数集合中删除所有给定的元素。 | 调用 `dictDelete` 函数, 从字典中删除所有键为给定元素的键值对。 |
- 介绍
- 前言
- 致谢
- 简介
- 第一部分:数据结构与对象
- 简单动态字符串
- SDS 的定义
- SDS 与 C 字符串的区别
- SDS API
- 重点回顾
- 参考资料
- 链表
- 链表和链表节点的实现
- 链表和链表节点的 API
- 重点回顾
- 字典
- 字典的实现
- 哈希算法
- 解决键冲突
- rehash
- 渐进式 rehash
- 字典 API
- 重点回顾
- 跳跃表
- 跳跃表的实现
- 跳跃表 API
- 重点回顾
- 整数集合
- 整数集合的实现
- 升级
- 升级的好处
- 降级
- 整数集合 API
- 重点回顾
- 压缩列表
- 压缩列表的构成
- 压缩列表节点的构成
- 连锁更新
- 压缩列表 API
- 重点回顾
- 对象
- 对象的类型与编码
- 字符串对象
- 列表对象
- 哈希对象
- 集合对象
- 有序集合对象
- 类型检查与命令多态
- 内存回收
- 对象共享
- 对象的空转时长
- 重点回顾
- 第二部分:单机数据库的实现
- 数据库
- 数据库键空间
- 重点回顾
- RDB 持久化
- RDB 文件结构
- 重点回顾
- AOF 持久化
- AOF 持久化的实现
- 重点回顾
- 事件
- 文件事件
- 重点回顾
- 参考资料
- 客户端
- 客户端属性
- 重点回顾
- 服务器
- 命令请求的执行过程
- 重点回顾
- 第三部分:多机数据库的实现
- 复制
- 旧版复制功能的实现
- 重点回顾
- Sentinel
- 启动并初始化 Sentinel
- 重点回顾
- 参考资料
- 集群
- 节点
- 重点回顾
- 第四部分:独立功能的实现
- 发布与订阅
- 频道的订阅与退订
- 重点回顾
- 参考资料
- 事务
- 事务的实现
- 重点回顾
- Lua 脚本
- 创建并修改 Lua 环境
- 重点回顾
- 排序
- SORT <key> 命令的实现
- 重点回顾
- 二进制位数组
- GETBIT 命令的实现
- 重点回顾
- 慢查询日志
- 慢查询记录的保存
- 慢查询日志的阅览和删除
- 添加新日志
- 重点回顾
- 监视器
- 成为监视器
- 向监视器发送命令信息
- 重点回顾
- 源码、相关资源和勘误