ziplist 的组成结构

为了节约内存,Redis 开发了 ziplist 数据结构。一个压缩列表包含任意多个节点entry,每个entry 可以保存1个字节数组,或1个整数值。
Redis 探究:压缩列表 ziplist

  • zlbytes:整个压缩列表占用的字节数;
  • zltail:尾节点相对列表起始地址的偏移量,等于 尾节点起始地址 - 压缩列表起始地址;
  • zllen:节点 entry 的个数,当列表节点总数大于 65535 时,及超过2字节能表示的最大值时,需要遍历整个列表才能算出真实数量;
  • entry:列表节点,长度不定,由节点保存的内容决定;
  • zlend:特殊值 0xFF 用于标记列表的末尾,相当与 C 语言用 ‘\0’ 表示字符串的结束符。

entry 的构成

压缩列表的节点由 previous_entry_length、encoding、content 三部分构成。
Redis 探究:压缩列表 ziplist

  • previous_entry_length:记录前一个节点的长度。可以是 1字节 也可以是 5字节(类似 DB 中的 VARCHAR)。如果前一节点长度小于 254 字节,就是 1 字节;否则为 5 字节;
    Redis 探究:压缩列表 ziplist
  • encoding:表示 content 所保存的数据类型(字节数组 或 整数)及长度。最高位以 00、01、10 开头,表示字节数组以 11 开头表示整数编代码,具体见下面两图:
    Redis 探究:压缩列表 ziplist
    Redis 探究:压缩列表 ziplist

连锁更新

由于每个节点的 previous_entry_length 保存的是前一个节点的长度,如果前一个节点长度小于 254 时,previous_entry_length 占用 1 字节,否则使用 5 字节。

假如列表中 entry 长度均为 253,现在要插入一个长度为 256 的节点 entryX,这时保存 entryX 的 previous_entry_length 不能用1字节了,需要扩展到 5 字节。相应的后续 entry 里的 previous_entry_length 长度均要升级。这就叫做连锁更新,实际上,在删除节点时也可能遇到。

连锁更新,在最坏情况下要对 ziplist 进行 N次空间重分配操作,每次空间重分配最坏的复杂度为 O(N) ,所以连锁更新最坏的复杂度为 O(N^2)。但现实中遇到该情况的概率非常低,ziplistPush 等命令的平均复杂度为 O(N)

相关文章: