目录
- 基础命令
- 存储编码
- intset源码分析
- intset存储图解分析
- intset内部编码升级
- 图解升级步骤
- set编码转换条件
- 结尾语
基础命令
- sadd key [values] :向集合添加一个或多个成员
- scard key:获取集合的成员数
- spop key: 随机弹出一个元素(随机)
- sismember key member:判断 member 元素是否是集合 key 的成员
- srem key [members]:移除集合中一个或多个元素(指定)
myRedis:0>sadd key1 test1 test2
"2"
myRedis:0>sadd key1 test3 test4
"2"
myRedis:0>scard key1
"4"
myRedis:0>sismember key1 test4
"1"
myRedis:0>sismember key1 test5
"0"
myRedis:0>spop key1
"test4"
myRedis:0>spop key1
"test2"
myRedis:0>srem key1 test1
"1"
myRedis:0>scard key1
"1"
- sdiff key1 [keys] : 返回第一个集合与后面集合的差集
- sinter key1 [keys]:返回给定所有集合的交集
- sunion key1 [keys] :返回所有给定集合的并集
myRedis:0>sadd key1 test1 test2 test3 test4 test5
"5"
myRedis:0>sadd key2 test2 test4 test6 test7 test8
"5"
myRedis:0>sadd key3 test2 test4 test8 test9 test10
"5"
myRedis:0>sdiff key1 key2 key3
1) "test3"
2) "test5"
3) "test1"
myRedis:0>sinter key1 key2 key2
1) "test2"
2) "test4"
myRedis:0>sunion key1 key2 key3
1) "test6"
2) "test9"
3) "test1"
4) "test3"
5) "test5"
6) "test4"
7) "test7"
8) "test8"
9) "test2"
10) "test10"
存储编码
Redis的集合内部编码有两种
- intset
- hashtable(在上一篇章中已经详细介绍Redis-Hash篇)
intset源码分析
源码位置:intset.h
typedef struct intset {
uint32_t encoding; // intset的内部编码,int16_t,int32_t,int64_t
uint32_t length; // 集合的元素数量
int8_t contents[]; // 保存元素的数组
} intset;
intset存储图解分析
intset内部编码升级
- intset内部编码简介
虽然contents类型是int8,但是实际上的编码则是由encoding决定的,而encoding在集合的内部编码中有三种值:
- INTSET_ENC_INT16 范围:-32768 ~ 32767(-2^15 ~ 2^15-1)
- INTSET_ENC_INT32 范围: -2147483648 ~ 2147483647(-2^31 ~ 2^31-1)
- INTSET_ENC_INT64 范围:-9223372036854775808 ~ 9223372036854775807(-263^ ~ 2^63-1)
- 为什么要升级
- 将一个新元素添加到集合里面,并且新元素的类型比现有元素的类型都要长时,集合就要新进行升级才能存的下新元素
- 升级的步骤
- 根据新元素的类型,扩展底层数据的空间大小
- 将现有的元素都转换成新元素相同的类型,并将转换后的原始元素放到正确的位置上(顺序不变:拷贝过程)
- 添加新元素到底层数组里
- 升级的好处
- 提升整数集合的灵活性
- 尽可能的节约内存
- 降级
- 升级后不支持降级操作,即使把所有超长的元素删了之后,内部编码也不会降低,升级过程不可逆
图解升级步骤
- 假设原始值存的是1 2 3
1 2 3 选用int16类型即可存储,初始值都是16位=>2字节
补充扩展:1字节=8位
- 在初始值的基础上插入一个新元素 100000
新元素100000 已经超出了int16的范围,如果想插入需要使用int32类型
因此接下来要做的事情就是,升级数据类型到int32
注意:这里会按照新的类型和元素个数申请新的空间
总空间大小为:32*4=128位(16字节)
需要申请的大小为:128-48 = 80位
- 新的空间申请好之后,会修改原始数据的类型重新调整内存分配
按照四个元素计算,每个元素占用32位,从大到小依次调整元素的位置
依次类推,调整2 1的位置,最后会得到如下结构
- 调整好原始数据的内存分配之后,再把新的元素插入到指定位置
- 修改encoding为int32_t,修改length为4,新增元素结束
set编码转换条件
- 集合对象保存的所有元素都是整数值
- 集合元素数量小于等于512个
以上条件同时满足,set集合内部编码使用的是intset,否则会用hashtable存储 - hashtable再之前的篇章中已经说过了,区别点在于,hashtable存储集合时用的是key,value为空
结尾语
Redis中的集合重要特征是不重复,非整数类型无序,存整数和存其他类型的用两种不同的编码实现,而如何创建一个有序的集合,将在下一篇章中介绍