压缩列表的实现原理
压缩列表(Ziplist)结构如下:

链表头部包含三个字段:
- zlbytes: 统计整个压缩列表的字节数
- zltail: 统计末尾节点距离起始节点的距离
- zllen: 统计整个列表的节点数量
末尾有一个固定标识字段:
- zlend: 标记列表结束点,固定值为 0xFF(十进制 255)
若需查询最后一个节点,依赖 zltail 字段,时间复杂度为 O(1)。访问其他节点需依次查找,时间复杂度为 O(n),因此压缩列表不适合存储大量元素。
每个节点包含三个属性:
- prevlen: 存放前一个节点占用的内存字节大小,支持倒序遍历
- encoding: 存放当前数据的类型(整数型或字符串)
- data: 存储当前数据
插入数据时,压缩列表根据元素类型和大小动态分配空间,提高了内存利用率。
缺点:
遇到连锁更新时,可能导致内存空间重新分配,多次分配会影响访问性能。若节点过多或过大可能引发连锁更新,故适合存储节点较少的场景。
为解决该问题,Redis 3.2 引入 Quicklist,Redis 5.2 引入 Listpack,在保留节省空间优点的同时解决了连锁更新问题。
Listpack 介绍
Listpack 核心目的是解决 Ziplist 的连锁更新问题。

头部包含总字节数和元素数量,结尾有结束标识。每个元素包含三个属性:
- encoding: 定义编码类型,对整数或字符串进行不同编码
- data: 数据
- len: 记录 encoding 和 data 的长度
Listpack 根据内容大小进行不同编码,主要依赖 encoding。移除 prevlen 后,若更改字符串不会导致其他节点内容变化,直接解决了连锁更新问题。
倒序遍历逻辑:
- 通过表头「总字节数」定位到 Listpack 末尾。
- 减去结尾标识(1 字节),得到最后一个节点的末尾地址。
- 读取该节点的
len字段,用末尾地址减去len得到上一个节点的末尾地址。 - 重复步骤 3,直到遍历到表头。
哈希表扩容机制
哈希扩容需要两个哈希表。



