redis 快速链表 详解

1. 什么是快速链表?

快速链表(QuickList)是 Redis 3.2 版本引入的一种列表(List)底层实现结构,它结合了双向链表 和**压缩列表(ZipList)**​ 的优点,在内存使用效率和操作性能之间取得了良好的折衷。

2. 为什么需要快速链表?

在 Redis 3.2 之前,列表的底层实现有两种选择:

  • 双向链表:操作时间复杂度 O(1),但内存碎片较多,内存利用率低

  • 压缩列表:内存紧凑,利用率高,但修改操作可能导致连锁更新,性能不稳定

快速链表的设计目标:

  • 保持双向链表的高效操作特性

  • 提高内存使用效率

  • 避免压缩列表的连锁更新问题

3. 快速链表的结构设计

3.1 整体结构

复制代码
QuickList
├── head: QuickListNode*
├── tail: QuickListNode*
├── count: unsigned long     // 所有元素总数
├── len: unsigned long       // 节点数量
├── fill: int               // 单个节点最大容量限制
├── compress: int          // 压缩深度
└── ...

3.2 节点结构

每个 QuickListNode 包含:

复制代码
typedef struct quicklistNode {
    struct quicklistNode *prev;  // 前驱指针
    struct quicklistNode *next;  // 后继指针
    unsigned char *zl;           // 指向压缩列表的指针
    unsigned int sz;             // 压缩列表的大小
    unsigned int count : 16;     // 节点中元素个数
    unsigned int encoding : 2;   // 编码方式:RAW=1, LZF=2
    unsigned int container : 2;  // 容器类型:NONE=1, ZIPLIST=2
    unsigned int recompress : 1; // 是否被压缩过
    unsigned int attempted_compress : 1; // 测试用
    unsigned int extra : 10;     // 预留字段
} quicklistNode;

4. 核心特性详解

4.1 分块存储

  • 将大列表分割成多个小压缩列表节点

  • 每个节点的大小由 list-max-ziplist-size配置控制

  • 默认单个压缩列表节点最多包含 8KB 数据

4.2 压缩机制

  • 支持对中间节点进行 LZF 压缩

  • 压缩策略由 list-compress-depth配置控制:

    • 0:不压缩(默认)

    • 1:首尾各1个节点不压缩,中间节点压缩

    • 2:首尾各2个节点不压缩,中间节点压缩

    • 以此类推...

4.3 内存布局示例

复制代码
QuickList
    ↓
Node1(zl1) ↔ Node2(zl2) ↔ Node3(zl3) ↔ ... ↔ NodeN(zlN)
    ↓           ↓           ↓                   ↓
[entry1..n]  [entry1..m]  [entry1..k]        [entry1..p]

5. 操作特性分析

5.1 插入操作

  • 头部/尾部插入:O(1) 时间复杂度

  • 中间插入:需要定位到具体节点,然后在压缩列表内插入

  • 插入时如果节点超过大小限制,会进行分裂

5.2 查找操作

  • 按索引查找:O(n) 时间复杂度,但比纯链表快

  • 由于记录了每个节点的元素数量,可以快速定位到目标节点

5.3 删除操作

  • 删除后如果节点为空,会回收该节点

  • 删除后如果相邻节点都很小,可能会进行合并

6. 配置参数

6.1 list-max-ziplist-size

复制代码
# 正值表示节点最多包含的entry数量
# 负值表示单个节点最大内存大小:
#   -1: 4KB    -2: 8KB    -3: 16KB
#   -4: 32KB   -5: 64KB
list-max-ziplist-size -2  # 默认8KB

6.2 list-compress-depth

复制代码
# 压缩深度,0表示不压缩
list-compress-depth 0  # 默认不压缩

7. 性能优势

7.1 内存效率

  • 相比双向链表:内存使用减少 50%-80%

  • 相比纯压缩列表:避免了大数据量时的性能抖动

7.2 操作性能

  • 保持了链表 O(1) 的头尾操作性能

  • 批量操作时可以利用局部性原理

7.3 避免连锁更新

  • 将大压缩列表分割成小压缩列表

  • 单个节点的修改不会影响整个列表

8. 使用场景

8.1 适合场景

  • 需要存储大量列表数据

  • 频繁进行头尾操作(LPUSH/RPOP等)

  • 对内存使用敏感的应用

8.2 不适用场景

  • 需要频繁按索引随机访问

  • 列表元素非常大(单个元素超过64KB)

9. 实际应用示例

9.1 Redis 列表操作

复制代码
# 创建列表
127.0.0.1:6379> LPUSH mylist "item1" "item2" "item3"
(integer) 3

# 查看列表信息
127.0.0.1:6379> LLEN mylist
(integer) 3

# 弹出元素
127.0.0.1:6379> RPOP mylist
"item1"

9.2 监控快速链表状态

复制代码
# 查看内存使用情况
127.0.0.1:6379> MEMORY USAGE mylist
(integer) 176

# 使用redis-rdb-tools分析RDB文件
# 可以查看快速链表的详细统计信息

10. 总结

快速链表是 Redis 在列表实现上的重要优化,它:

  • ✅ 结合了双向链表和压缩列表的优点

  • ✅ 提供了优秀的内存使用效率

  • ✅ 保持了良好的操作性能

  • ✅ 避免了连锁更新问题

  • ✅ 支持灵活的压缩策略

相关推荐
AlunYegeer2 小时前
论mysql的redo_log和bin_log,redis的RDB和AOF的类似记忆
数据库·redis·mysql
gutsola2 小时前
用 Rust 构建高性能 KV 存储:FoxKV 性能超越 Redis 2.5 倍的技术实践
redis
一叶飘零_sweeeet2 小时前
Redis 高可用全链路拆解:从主从复制到集群架构的原理与实践
redis·架构·redis高可用架构
2401_874732532 小时前
构建一个桌面版的天气预报应用
jvm·数据库·python
y = xⁿ2 小时前
【LeetCodehot100】T114:二叉树展开为链表 T105:从前序与中序遍历构造二叉树
java·算法·链表
逆境不可逃2 小时前
LeetCode 热题 100 之 160. 相交链表 206. 反转链表 234. 回文链表 141. 环形链表 142. 环形链表 II
算法·leetcode·链表
码云数智-园园2 小时前
坚如磐石:数据库事务ACID特性的实现奥秘
数据库·oracle
十月南城2 小时前
文档化与知识库方法——ADR、Runbook与故障手册的结构与维护节奏
大数据·数据库
qq_417695052 小时前
实战:用Python开发一个简单的区块链
jvm·数据库·python