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 在列表实现上的重要优化,它:

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

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

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

  • ✅ 避免了连锁更新问题

  • ✅ 支持灵活的压缩策略

相关推荐
Elastic 中国社区官方博客2 小时前
Elasticsearch:使用 Agent Builder 的 A2A 实现 - 开发者的圣诞颂歌
大数据·数据库·人工智能·elasticsearch·搜索引擎·ai·全文检索
2301_816660213 小时前
PHP怎么处理Eloquent Attribute Inference属性推断_Laravel从数据自动推导类型【操作】
jvm·数据库·python
qq_372154233 小时前
Go 中自定义类型与基础类型的显式转换规则详解
jvm·数据库·python
_下雨天.4 小时前
NoSQL之Redis配置与优化
数据库·redis·nosql
LiAo_1996_Y4 小时前
CSS如何实现文字渐变效果_通过background-clip实现艺术字
jvm·数据库·python
2401_887724504 小时前
CSS如何让表单在手机端友好展示_利用Flexbox实现堆叠排版
jvm·数据库·python
数据库小组4 小时前
MySQL 删库后怎么恢复?binlog2sql 之外,NineData 还能做什么
数据库·sql·mysql·安全·数据·ninedata·删库
zhangchaoxies4 小时前
Layui轮播图(carousel)怎么设置自动播放间隔
jvm·数据库·python
切糕师学AI5 小时前
HBase:一文搞懂分布式宽列数据库(原理 + 架构 + 实战)
数据库·分布式·nosql·hbase·分布式宽列数据库·wide column db
competes5 小时前
慈善基金投资底层逻辑应用 顶层代码低代码配置平台开发结构方式数据存储模块
java·开发语言·数据库·windows·sql