这是我参与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),每个节点可以保存一个字节数组或者一个整数值。
上图展示了压缩列表的各个组成部分
| 属性 | 类型 | 长度 | 用途 |
|---|---|---|---|
| zlbytes | uint32_t | 4字节 | 记录整个压缩列表占用的内存字节数:在对压缩列表进行内存重分配,或者计算zlend的位置时使用 |
| zltail | uint32_t | 4字节 | 记录压缩列表表尾节点距离压缩列表的起始地址有多少字节:通过这个偏移量,可以快速定位到表尾。 |
| zllen | uint16_t | 2字节 | 记录了压缩列表包含的节点数量:当这个属性的值小于UINT16_MAX(65535)时,这个属性的值就表示节点数量;当这个值等于UINT16_MAX时,节点的真实数量需要遍历才能计算得出。 |
| entryX | 列表节点 | 不定 | 压缩列表包含的各个节点 |
| zlend | uint8_t | 1字节 | 特殊值0xFF(十进制255),用于标记压缩列表的末端。 |
上图展示包含三个节点的压缩列表
3、压缩列表节点的构成
每个压缩列表节点可以保存一个字节数组或者一个整数值,其中,字节数组可以是以下三种长度之一:
- 长度小于等于63字节的字节数组;
- 长度小于等于16383字节的字节数组;
- 长度小于等于4294967295字节的字节数组 整数值则可以是以下六种长度之一:
- 4位长,介于0至12之间的无符号整数;
- 1字节长的有符号整数;
- 3字节长的有符号整数;
- int16_t类型整数;
- int32_t类型整数;
- int64_t类型整数。 每个压缩列表节点都由previous_entry_length、encoding、content三个部分组成,如下图所示:
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属性保存的值 |
|---|---|---|
| 11000000 | 1字节 | int16_t类型的整数 |
| 11010000 | 1字节 | int32_t类型的整数 |
| 11100000 | 1字节 | int64_t类型的整数 |
| 11110000 | 1字节 | 24位有符号整数 |
| 11111110 | 1字节 | 8位有符号整数 |
| 1111xxxx | 1字节 | 使用这一编码的节点没有相应的content属性,因为编码本身的xxxx四个位已经保存了一个介于0和12之间的值,所以它无须content属性 |
3.3 content
节点的content属性负责保存节点的值,节点值可以是一个字节数组或者整数,值的类型和长度由节点的encoding属性决定。 如下图展示了一个保存字节数组的节点示例:
1、编码的最高位00表示节点保存的是一个字节数组;
2、编码的后六位001011记录了字节数组的长度11;
3、content属性保存着节点的值"hello world"。