压缩列表的数据结构
zset和hash容器对象在元素个数较少的时候,采用压缩列表(ziplist)来存储。压缩列表是一块连续的内存空间。结构如下
属性 | 类型 | 长度 | 用途 |
---|---|---|---|
zlbytes | uint32_t | 4字节 | 整个压缩列表占用字节数 |
zltail | uint32_t | 4字节 | 最后一个元素距离压缩列表起始位置的偏移量,用于快速定位最后一个元素 |
zllen | uint16_t | 2字节 | 压缩列表的节点数量,值小于UINT16_MAX(65535)时,这个属性值就是压缩列表包含节点的数量,值等于UINT16_MAX,节点的数量需要遍历整个压缩列表才能计算得出 |
entry | 不定 | 不定 | 元素内容,可以是字节数组,也可以是整数 |
zlend | uint8_t | 1字节 | 压缩列表结束标志,值恒为0xFF(十进制255) |
下图是压缩列表的示意图
zlbytes的值为0x50(十进制80),表示压缩列表的总长度为80字节
zltail的值为0x3c(十进制60),entry3元素距离列表起始位置的偏移量为60,起始位置的指针加上60就能算出表尾节点entry3的地址
zllen的值为0x3(十进制3),表示压缩列表包含3个节点
那么每个元素的组成又是怎样的呢?
每个元素的数据结构如下图所示
previous_entry_length
previous_entry_length以字节为单位,记录了压缩列表中前一个节点的长度,这样主要为了方便倒着遍历。通过zltail属性直接定位压缩列表的最后一个节点,然后通过previous_entry_length定位前一个节点。
- 如果前一个节点的长度小与254字节,那么previous_entry_length属性的长度为1字节(1个字节可以表示的最大长度为28-1=255,但是255被设置为压缩列表的结束标志了,所以为254)
- 如果前一个节点的长度大于等于254字节,那么previous_entry_length属性的长度为5字节。属性的第一字节会被设置成0xFE(十进制为254),而之后的四个字节则用于保存前一字节的长度
encoding
encoding存储了元素内容的类型编码信息,ziplist通过这个字段来决定后面的content的形式
Redis通过encoding字段的前缀来识别存储的格式
字节数组编码
编码 | 编码长度 | content属性保存的值 |
---|---|---|
00xxxxxx | 1字节 | 长度小于等于63的字节数组(26-1) |
01xxxxxx xxxxxxxx | 2字节 | 长度小于16383的字节数组(214-1) |
10xxxxxx aaaaaaaa bbbbbbbb cccccccc dddddddd | 5字节 | 长度小于238-1的字节数组 |
整数编码
编码 | 编码长度 | content属性保存的值 |
---|---|---|
11000000 | 1字节 | int16_t类型的数 |
11010000 | 1字节 | int32_t类型的数 |
11100000 | 1字节 | int64_t类型的数 |
11110000 | 1字节 | int24类型的数 |
11111110 | 1字节 | int8类型的数 |
1111xxxx | 1字节 | 0-12之间的数字,没有content |
可以看到编码为 1111xxxx时,没有content
因为编码本身的xxxx四个位已经保存了一个介于0-12之间的值,所以它无需content属性。xxxx的范围只能是(0001-1101),也就是1~13,因为0000,1110,1111都被占用了。读取value后会减1,即能0-12之间的值
content
保存节点的值,节点值可以是字节数组或者整数,值的类型和长度由encoding决定
级连更新
ziplist总结
优点:使用了一块连续的内存,省区了头尾指针占用内存的大小
缺点:对于插入或者增加新的元素会触发连锁更新反应
参考博客
[1]https://jishuin.proginn.com/p/763bfbd3293c