1.6. redis数据结构-压缩列表

123 阅读4分钟

压缩列表主要存储int字节数组

压缩队列是Redis 底层一个很重要的数据结构,hash,list,set等Redis 对象在存储的元素个数不多,元素大小不大的情况下。都是使用压缩队列。只有在达到一定的阈值的时候,会升级为hashmap,linkedlist,skiplist等数据结构。

数据结构

字段说明字节数
zlbytes当前已存储的字节数4(也就表明最大的大小只能为int32的最大长度,大概2G多)
zltail列表尾偏移量,
zentry.previous_entry_len来实现倒序遍历
4
zllength元素个数2,当小于uint16_max(65535)时,该值为列表元素个数,
否则固定为65535,此时需要遍历元素或者列表长度
zentry1存储节点值不定
zentry2存储节点值不定
zentry3存储节点值不定
zlend特殊标识值(0xff),标记队尾1

zentry

字段说明
previous_entry_len前一个节点的字节长度
encoding节点编码
content存储的实际长度,可能为一个整形或者字节数组,应该为void *

previous_entry_len

  1. 保存的是前一个节点的所占的字节数。
  2. previous_entry_len本身可以占1个或者5个字节
  3. 1byte 的previous_entry_len 表明前一个节点大小254bytes1个字节 =255。因为需要区分previous_entry_len1个还是5个,需要占用FE(254). (那还是还浪费一个啊)
  4. previous_entry_len的第一个字节是FE的时候,表明前一个节点是大于254,此时previous_entry_len后的4个字节表示的int表明前一个节点的大小。例如0xFE00002766.表明前一个节点大小为0x2766(10进制 = 10086)

encoding说明

ziplist 可以存放整数 和 字节数组,所以encoding必须可以区分存放的是整数还是字节数组

encoding使用第一个字节的高两位作为类型判断. 当最高位是11的时候,表示的是整形,非11表示是byte数据。其中encoding所占的空间不固定,受类型影响,可能为1或者2或者5个字节

当第一个字节的最高位为11的时候,encoding长度为1.表示的是整数. 整数又分为各种长度的类型.分布如下

标识content说明
11 111110保存有符号int8_t
11 000000保存int16_t
11 010000保存int32_t
11 100000保存int64_t
11 11000保存24位有符号整数(没见过)
11 11xxxxcontent不占用空间,
此时encoding的后四位保存的4位整数.

encoding最高位非11的时候(b表示一个16进制数,_ 表示无意义)

标识encoding长度content说明
00 bbbbbb1字节长度为0xbbbbbb的字节数组
01 bbbbbb bbbbbbbb2字节长度为2^14的字节数组
10 ______ bbbbbbbb
bbbbbbbb
bbbbbbbb
bbbbbbbb
5字节长度为2^32的字节数组

连锁更新

假设下面的情况

假设一个ziplist.总共3个节点,每个entry.content内容为253个字节.此时每个节点为内容为 | 1字节的entry_prev | 1字节的encoding | 内容(253-1-1) | ,

此时如果在压缩列表头部或者第二个位置前插入一个大于253字节长度的内容时,此时第二个节点的prev_entry_len的1个字节长度肯定不够(看prev_entry_len说明).必定要进行扩容,当第二个元素扩容后,大小必定大于254,此时原第三个元素的prev_entry_len1的一个字节必须要变成5字节,如果后面还有相同结构的内容.则所有的元素都要扩容.

上面就是连锁更新

虽然出现连锁更新的时候,效率很低,但是出现概率较为苛刻必须所有节点的content大小刚好都在253附近.否则任何一个大于pre_entry_len=5byte的都不需要扩展.

同样也会出现删除时连锁降级更新的情况

问题

  1. 第一个节点的prev_entry_len存放啥值?0,我猜的
  2. 向前遍历比向后遍历还麻烦?一样。
  3. 因为节点的encoding包含了长度信息。通过长度信息,previous的信息。encoding的类型信息可以计算出一个点节点的长度信息