跟我一起来学习Redis中的压缩列表吧(上)

304 阅读5分钟

这是我参与8月更文挑战的第3天,活动详情查看:8月更文挑战

一、压缩列表

1、压缩列表简介
2、压缩列表的构成
3、压缩列表节点的构成

1、压缩列表简介

  压缩列表(ziplist)是列表键和哈希键的底层实现之一。当一个列表键只包含少量列表项,并且每个列表项要么就是小整数值,要么就是长度比较短的字符串,那么Redis就会使用压缩列表来做列表键的底层实现。 例如:执行以下命令将创建一个压缩列表实现的列表键:

redis> RPUSH 1st 1 3 5 10086 "hello" "world"
(integer)6

  另外,当一个哈希键只包含少量键值对,且每个键值对的键和值要么就是小整数值,要么就是长度比较短的字符串,那么Redis就会使用压缩列表来做哈希键的底层实现。

2、压缩列表的构成

  压缩列表是Redis为了节约内存而开发的,是由一系列特殊编码的连续内存块组成的顺序型(sequential)数据结构。一个压缩列表可以包含任意多个节点(entry),每个节点可以保存一个字节数组或者一个整数值。

image.png    上图展示了压缩列表的各个组成部分

属性类型长度用途
                          zlbytes                              uint32_t   4字节记录整个压缩列表占用的内存字节数:在对压缩列表进行内存重分配,或者计算zlend的位置时使用
zltailuint32_t4字节记录压缩列表表尾节点距离压缩列表的起始地址有多少字节:通过这个偏移量,可以快速定位到表尾。
zllenuint16_t2字节记录了压缩列表包含的节点数量:当这个属性的值小于UINT16_MAX(65535)时,这个属性的值就表示节点数量;当这个值等于UINT16_MAX时,节点的真实数量需要遍历才能计算得出。
entryX列表节点不定压缩列表包含的各个节点
zlenduint8_t1字节特殊值0xFF(十进制255),用于标记压缩列表的末端。

image.png
  上图展示包含三个节点的压缩列表

3、压缩列表节点的构成

每个压缩列表节点可以保存一个字节数组或者一个整数值,其中,字节数组可以是以下三种长度之一:

  • 长度小于等于63字节的字节数组;
  • 长度小于等于16383字节的字节数组;
  • 长度小于等于4294967295字节的字节数组 整数值则可以是以下六种长度之一:
  • 4位长,介于0至12之间的无符号整数;
  • 1字节长的有符号整数;
  • 3字节长的有符号整数;
  • int16_t类型整数;
  • int32_t类型整数;
  • int64_t类型整数。 每个压缩列表节点都由previous_entry_length、encoding、content三个部分组成,如下图所示:

image.png

3.1 previous_entry_length

节点的previous_entry_length属性以字节为单位,记录了压缩列表中前一个节点的长度。previous_entry_length属性的长度可以是1字节或者5字节:

  • 如果前一节点的长度小于254字节,那么previous_entry_length属性的长度为1字节:前一节点的长度就保存在这一个字节里面。
  • 如果前一节点的长度大于等于254字节,那么previous_entry_length属性的长度为5字节:其中属性的第一字节会被设置为0xFE(十进制值254),而之后的四字节则用于保存前一节点的长度。   压缩列表的从表尾向表头遍历操作就是使用这一原理实现的,只要我们拥有了一个指向某个节点起始地址的指针,那么通过这个指针以及这个节点的previous_entry_length属性,程序就可以一直向前一个节点回溯,最终到达压缩列表的表头节点。

3.2 encoding

  节点的encoding属性记录了节点的content属性所保存数据的类型以及长度:

  • 一字节、两字节或者五字节长,值的最高位为00、01或者10的是字节数组编码,这种编码表示节点的content属性保存着字节数组,数组的长度由编码除去最高两位之后的其他记录;
  • 一字节长,值的最高位以11开头的是整数编码:这种编码表示节点的content属性保存着整数值,整数值的类型和长度由编码除去最高两位之后的其他位记录; 字节数组编码对应如下表: | 编码 | 编码长度 | content属性保存的值 | | --- | --- | --- | | 00bbbbbb | 1字节 | 长度<=63字节的字节数组 | | 01bbbbbb xxxxxxxx | 1字节 | 长度<=16383字节的字节数组 | | 10______ aaaaaaaa bbbbbbbb cccccccc dddddddd | 1字节 | 长度<=4294967295字节的字节数组 |

整数编码如下:

                   编码         编码长度content属性保存的值
110000001字节int16_t类型的整数
110100001字节int32_t类型的整数
111000001字节int64_t类型的整数
111100001字节24位有符号整数
111111101字节8位有符号整数
1111xxxx1字节使用这一编码的节点没有相应的content属性,因为编码本身的xxxx四个位已经保存了一个介于0和12之间的值,所以它无须content属性

3.3 content

节点的content属性负责保存节点的值,节点值可以是一个字节数组或者整数,值的类型和长度由节点的encoding属性决定。 如下图展示了一个保存字节数组的节点示例:

image.png

1、编码的最高位00表示节点保存的是一个字节数组;
2、编码的后六位001011记录了字节数组的长度11;
3、content属性保存着节点的值"hello world"。