压缩列表(ziplist)是列表键和哈希键的底层实现之一。
当一个列表键只包含少量的列表项,并且每个列表项要么是小整数值,要么是长度比较短的字符串时,Redis会使用压缩列表来做列表键的底层实现;
当一个哈希键只包含少量的键值对,并且每个键值对的键和值要么是小整数值,要么是长度比较短的字符串,Redis会使用压缩列表来做哈希键的底层实现;
7.1 压缩列表的构成
压缩列表是Redis为了节约内存而开发的,是由一系列特殊编码的连续内存块组成的顺序型数据结构。一个压缩列表可以包含任意多个节点,每个节点可以保存一个字节数组或者一个整数值。



zlbytes属性的值为0x50(十进制80),表示压缩列表的总长为80字节;
zltail属性的值为0x3c(十进制60),表示如果存在指向压缩列表起始地址的指针p,我们使用指针p加上偏移量60,可以计算得出表尾节点entry3的地址;
zllen属性的值为0x3(十进制3),表示压缩列表包含3个节点;
7.2 压缩列表节点的构成
每个压缩列表节点可以保存一个字节数组或者一个整数值;
其中,字节数组的长度可以是以下三种长度中的一种:
- 长度小于等于2^6-1字节的字节数组;
- 长度小于等于2^14-1字节的字节数组;
- 长度小于等于2^32-1字节的字节数组;
整数值可以是以下六种长度中的一种:
- 4位长,介于0~12之间的无符号整数;
- 1字节长的有符号整数;
- 3字节长的有符号整数;
- int16_t类型整数;
- int32_t类型整数;
- int64_t类型整数;
每个压缩列表节点都由previous_entry_length、encoding、content三部分组成:
![]()
7.2.1 previous_entry_length
节点previous_entry_length属性以字节为单位,记录压缩列表中前一个节点的长度。previous_entry_length属性的长度可以是1字节或者5字节;
- 如果前一个节点的长度小于254字节,则previous_entry_length的长度为1字节;
- 如果前一个节点长度大于等于254字节,则previous_entry_length的长度为5字节,第一个字节设置为0xFE,之后的四个字节用于保存前一个节点的长度;
previous_entry_length的存在,使得程序可以通过指针运算,根据当前节点的起始地址计算出前一个节点的起始地址;压缩列表从表尾向表头的遍历操作就是通过这一原理实现的。
7.2.2 encoding
节点的encoding属性记录节点content属性所保存的数据类型及长度;
- 一字节、两字节或者五字节长,值的最高位为00、01或10的是字节数组编码,节点的content属性保存着字节数组
- 一字节长,值的最高位为11开头的是整数编码,节点content属性保存着整数值;


7.2.3 content
节点的content属性负责保存节点的值,值的类型和长度由节点的encoding属性决定;
7.3 连锁更新
添加新节点引发连锁更新:
在一个压缩列表中,有多个连续的、长度介于250字节~253字节之间的节点e1至eN,当将一个长度大于254字节的新节点添加到压缩列表的表头时,可以引起后续所有节点的连锁反应,执行空间重分配操作;
删除节点引发连锁更新:
![]()
如上所示,e1至eN都是大小介于250字节~253字节的节点,big节点的长度大于等于254字节,small节点的长度小于254字节,当small节点从压缩列表中删除之后,e1的previous_entry_length属性会记录big节点的长度,引起后续所有节点的连锁反应;
连锁更新在最坏情况下,需要对压缩列表执行N次空间重分配操作,每次空间重分配的最坏复杂度为O(N),因此连锁更新的最坏复杂度为O(N^2);
浙公网安备 33010602011771号