整数集合
当集合中只有整数,且数目不多时,redis会使用整数集合intset作为集合的实现。
整数集合可保存16位 32位以及64位整数,且去重。
typedef struct intset {
//编码方式
uint32_t encoding;
//元素数目
uint32_t length;
//保存元素的数组
int8_t contents[];
} intset;
contents中各个元素从小到大有序排列,并且不包含重复项
contents 数组中真正的类型取决于 encoding属性的值
当整数集合中添加一个新元素且 新元素类型在encoding编码下已经存放不下 需要进行升级:
1 重新分配空间
2 迁移
3 添加新元素
需要对每个元素进行类型转换 时间复杂度O(N)
好处:
1 提升灵活性 不用考虑整数集合类型 直接添加即可,服务端进行类型转换,避免类型不符合错误
2 尽可能节约内存空间,只有在有需要的时候才进行升级。
降级:
整数集合只支持升级操作,不支持降级。
压缩列表
压缩列表是列表键和哈希键的底层实现之一。当一个列表键只包含少量列表项,并且每个列表项要么就是小整数,要么就是长度比较短的字符串,redis就会使用压缩列表来做列表键的底层实现
当一个哈希键只包含少量键值对,并且每个键值对的键和值要么就是小整数值,要么就是长度比较短的字符串,那么Redis就会使用压缩列表来做哈希键的底层实现。
压缩列表是Redis为了节约内存而开发的是由一系列特殊编码的连续内存块组成的顺序型数据结构,一个压缩列表可以包含任意多个节点,每个节点可以保存一个字节数组或者一个整数值
ziplist 数据结构
压缩列表节点的构成
每个压缩列表节点可以保存一个字节数组或者一个整数值,其中字节数组可以是以下三种长度的其中一种
长度小于等于63字节的字节数组
长度小于等于16383字节的字节数组
长度小于等于4294967295字节的字节数组
数值则可以是以下六种长度的其中一种
1: 4位长介于0至12之间的无符号整数
2:1字节长的有符号整数
3: 3字节长的有符号整数
4:int16类型整数
5:int32类型整数
6 : int64类型整数
压缩列表的数据结构
previous_entry_length 属性以字节为单位,记录了压缩列表中前一个节点的长度,previous_entry_length属性的长度可以是1字节或者5字节
如果前一节点的长度小于254字节那么previous_entry_length属性的长度为1字节 如果前一节点的长度大于等于254字节previous_entry_length属性的长度为5字节
根据当前节点的地址和previous_entry_length的值来计算出前一个节点的地址
压缩列表的从表尾向表头遍历操作就是使用这一原理实现的,只要我们拥有了一个指向某个节点起始地址的指针,那么通过这个指针以及这个节点的previous_entry_length属性
程序就可以一直向前一个节点回溯,最终到达压缩列表的表头节点。
节点encoding属性记录了节点的content属性所保存数据的类型以及长度
一字节、两字节或者五字节长,值的最高位为00 、01、或者10的是字节数组编码这种编码表示节点的content属性保存着字节数组,数组的长度有编码除去最高两位之后的其他位记录
一字节长 值的最高位以11开头的是整数编码,这种编码表示节点的content属性保存着整数值,整数值的类型和长度有编码除去最高两位之后的其他位记录
节点的content属性负责保存节点的值,节点值可以是一个字节数组或者整数值的类型和长度由encoding决定
连锁更新
连锁更新在最坏情况下需要对压缩列表执行N次空间重分配操作,而每次空间重分配的最坏复杂度为O(n) 连锁更新最坏的时间复杂度O(n*n)