压缩列表(ziplist)是Redis的一种底层数据结构,这是一种非常节省内存的结构,是列表(List)、Hash(哈希)和 Sorted Set(有序集合)的底层实现之一。

压缩列表结构

redis 压缩表 跳表 redis压缩列表 结构_数据结构

通过上图,可以看到压缩列表由一下几个部分构成:

  • zlbytes:表示列表长度,也就是整个压缩列表占用的内存字节数。
  • zltail:表示列表尾的偏移量,也就是最后一个entry的首字节位置。
  • zlen:表示列表中的entry个数。
  • entry:表示列表的节点,代表一个存储元素。
  • zlend:列表列表的结束,特殊值0xFF(十进制为255)

压缩列表之所以能节省内存,就在于它是用一系列连续的 entry 保存数据。

压缩列表节点结构

每个 entry 的元数据包括下面几部分:

  • prev_len:表示前一个 entry 的长度。prev_len 有两种取值情况:1 字节或 5 字节。
  • 当前一个节点的长度小于254个字节时, prev_len的长度为1个字节,直接存储前一个节点的字节长度;
  • 当前一个节点的长度大于或等于254个字节时, prev_len的长度为5个字节,其中的第一个字节被设置为0xFE,随后的四个字节保存前一个节点的字节长度。
  • encoding:表示数据的类型,1 字节;
  • len:表示自身长度,4 字节;
  • key:保存实际数据。

这里之前有个疑惑,

  • 为什么pre_len选择前一个节点小于254作为分界呢?
    我们知道一字节可以表示的最大值是255,但在压缩列表结构的zlend已经用作结束标识了,所以单字节在这里不可用于表示前一个节点的长度。
  • 那254为什么也不能用?
    我们可以看到pre_len中的第二点,Redis将5字节情况下的前一个字节设置为0xFE(十进制为254),也就是用这个标识这个pre_len到底是1字节还是5字节。

在压缩列表中,如果我们要查找定位第一个元素和最后一个元素,可以通过表头三个字段的长度直接定位,复杂度是 O(1)。而查找其他元素时,就没有这么高效了,只能逐个查找,此时的复杂度就是 O(N) 了。

但因为其保存了pre_len,让我们可以通过 prev_len和压缩列表结构中的zltail逆序遍历压缩列表。

小结

压缩列表实际上类似于一个数组,数组中的每一个元素都对应保存一个数据。

和数组不同的是,压缩列表重在“压缩”二字,数组中的元素大小是固定的,即使实际上某个数据元素并没有这么大。

而压缩列表做到了尽可能的利用空间,比数组节约了很多内存,那为什么说是尽可能呢?因为压缩列表为了能够计算出每个节点的位置,也增加了一些数组没有的字段,这也是不可避免的。