一、压缩列表的底层实现
压缩列表是哈希键和列表键的底层实现之一。压缩列表是为了节约内存而开发的,是由特殊编码的连续内存块组成的顺序性数据结构;可以包含任意多个节点,每个节点包含一个字节数组或者一个整数值。
当列表键中包含的条目较少并且条目都是小整数或者短字符串,那么列表键就会选择压缩列表作为底层实现。
当哈希键中包含的键值对较少,并且键值对都是小整数或者短字符串,那么哈希键就会选择压缩列表作为底层实现。
压缩列表的结构:
zlbytes | zltail | zllen | entry1 | entry2 | ...... | entryN | zlend |
属性 | 类型 | 长度 | 说明 |
zlbytes | uint32_t | 4字节 | 压缩列表所占用的内存字节数 |
zltail | uint32_t | 4字节 | 记录压缩列表最后一个节点的内存地址距压缩列表起始地址的偏移量 |
zllen | uint16_t | 2字节 | 压缩列表中节点的数量 |
entry | 列表节点 | 不定 | 压缩列表中的节点,节点的长度由其中保存的内容决定 |
zlend | uint8_t | 1字节 | 0xFF(255)标志压缩列表的结尾 |
二、压缩列表节点的构成
previous_entry_length | encoding | content |
每个压缩列表的节点可以保存一个字节数组或一个整数值。
1. previous_entry_length
previous_entry_length记录了前一个节点的长度;该属性的大小可能是1字节或5字节。
- 如果前一个节点的长度小于254,则该属性用1个字节表示
- 如果前一个节点的长度不小于254,则该属性用5个字节表示;第一个字节用0xFE表示,标志这是一个五字节长的属性。
2. encoding
encoding属性记录了content所保存的内容的类型及长度。
编码 | 编码长度 | content保存的值的类型 | content保存的值的长度 | 说明 |
00xxxxxx | 1字节 | 字节数组 | <= 2^6-1 | 00表示编码长度为8位的字节数组,后6位表示content的长度 |
01xxxxxx xxxxxxxx | 2字节 | 字节数组 | <= 2^14-1 | 01表示编码的长度位16位的字节数组,后14位表示content的长度 |
10_____ xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | 5字节 | 字节数组 | <= 2^32-1 | 10表示编码的长度位40位的字节数组,后32位表示content的长度 |
11000000 | 1字节 | int16_t类型整数 | int16_t | 11表示content的类型为整数,后6位表示content的长度位2字节 |
11010000 | 1字节 | int32_t类型整数 | int32_t | - |
11100000 | 1字节 | int64_t类型整数 | int64_t | - |
11110000 | 1字节 | 24位有符号整数 | 24位 | - |
11111110 | 1字节 | 8位有符号整数 | 8位 | - |
1111xxxx | 1字节 | 没有content属性 | 0 | 后4位记录了0-12的整数 |
3. content
content保存着节点的值,节点的值可以是字节数组或整数,值的类型和长度由encoding属性决定。
字节数组示例:
previous_entry_length | encoding | content |
...... | 00001011 | "hello world" |
整数示例:
previous_entry_length | encoding | content |
...... | 110000000 | 10086 |
三、连锁更新
在插入节点时,如果新插入的节点的长度大于下一个节点previous_entry_length的长度,就会重新分配内存,以满足需求,如果后面的节点因此也发生了内存重新分配,称为连锁更新。
连锁更新最坏的时间复杂度为O(n^2),尽管连锁更新的复杂度较高,但是真正引发连锁更新的几率很低。
四、API
函数 | 作用 | 时间复杂度 |
ziplistNew | 创建一个新的压缩列表 | O(1) |
ziplistPush | 在表头或表尾添加一个新的节点 | 平均O(n),最坏O(n^2) |
ziplistInsert | 在指定节点之后插入一个新节点 | 平均O(n),最坏O(n^2) |
ziplistIndex | 返回指定索引上的节点 | O(n) |
ziplistFind | 查找指定值的节点 | O(n^2) |
ziplistNext | 指定节点的下一个节点 | O(1) |
ziplistPrev | 指定节点的前一个节点 | O(1) |
ziplistGet | 获取指定节点的值 | O(1) |
ziplistDelete | 删除指定的节点 | 平均O(n),最坏O(n^2) |
ziplistDeleteRange | 删除指定索引范围的节点 | 平均O(n),最坏O(n^2) |
ziplistLen | 压缩列表节点数量 | O(1) |
ziplistBlobLen | 压缩列表所占用内存的字节数 | O(1) |