IntSet
IntSet是redis中set集合的一种实现方式,基于整数数组来实现,并且具备长度可变、有序等特征。
结构如下:
- encoding : 32位,编码方式,支持16位,32位,64位整数
- length: 32位,元素个数
- contents:8位的整数数组,保存集合数据(可以看到才8位,并没有遵循c语言的存储规范,存储数据的大小由encoding控制)
其中encoding 包含三种模式,表示存储的整数大小不同
为了方便查找,redis会将intset中所有的整数按升序排序依次保存到contents数组中,结构如图
现在数据都在16位范围内,因此encoding使用的是INTSET_ENC_INT16,每部分占用的字节数
- encoding: 4字节
- length: 4字节
- contents: 2字节*3=6字节
数组每个元素都是按照同一的编码格式的,这样是为了方便intset根据数组角标去寻址,c语言中数组是指针,指针是一个无符号8位的整数,只需要知道编码格式,就能很方便的找到数组中的每个元素
如:5这个元素的起始地址是0x001, 那么10对应的是0x001 + 2个字节 = 0x003, 20对应的就是0x003 + 2字节 = 0x005,数组的角标是从0开始的,对应的元素查找的时候只需 起始地址 + 字节数 * 角标 就可以找到对应的元素。
IntSet升级
现在,假设有个intset集合,元素位{5, 10, 20},采用的编码是INTSET_ENC_INT16,则每个整数占2个字节
我们向其中添加一个数字:50000,超出了int16的范围,intset会自动升级编码到合适的大小
- 升级编码到INTSET_ENC_INT32,每个整数占4个字节,并按照新的编码方式及元素个数进行扩容
- 倒叙依次将数组中的元素拷贝到扩容的位置(为什么要倒叙,例如:5现在占4个字节,势必会覆盖掉10的位置,导致10的元素找不到了,所以采用倒叙)
- 将待添加的元素放入数组末尾
- 最后将encoding,修改为INTSET_ENC_INT32,将length修改位4
IntSet查询
IntSet检索是根据二分法查找的,因为IntSet是有序的,可以根据二分法查找。
总结
IntSet可以看作是特殊的整数数组,具备一些特点:
- Intset元素是唯一,有序的
- 具备类型升级机制,可以节省内存空间
- 底层采用二分查找的方式来查询