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

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

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

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

  • ✅ 避免了连锁更新问题

  • ✅ 支持灵活的压缩策略

相关推荐
草莓熊Lotso7 小时前
【Linux网络】UDP Socket 编程全解析:从回显服务到通用字典服务,从零实现工业级代码
linux·运维·服务器·数据库·c++·单片机·udp
洛水水13 小时前
【力扣100题】18.随机链表的复制
算法·leetcode·链表
woxihuan12345614 小时前
SQL删除数据时存在依赖关系_设置外键级联删除ON DELETE
jvm·数据库·python
东风破13714 小时前
DM8达梦共享存储集群DSC搭建步骤
数据库·学习·dm达梦数据库
雪碧聊技术14 小时前
当数据库字段数大于Java实体类属性数时,MyBatis还能映射成功吗?一文详解
数据库·自动映射·mybatis映射机制·java实体类·宽容映射机制
Jetev14 小时前
如何确定SQL字段是否为空_使用IS NULL与IS NOT NULL
jvm·数据库·python
摇滚侠14 小时前
Redis 秒杀功能 超卖问题 一人一单问题 分布式锁 精彩!精彩!
redis·分布式·bootstrap
m0_7020365314 小时前
mysql如何处理不走索引的OR查询_使用UNION ALL优化重写
jvm·数据库·python
代钦塔拉15 小时前
Qt4 vs Qt5 带参数信号槽的连接方式详解
开发语言·数据库·qt
2401_8463395615 小时前
MySQL在云环境如何选择存储类型_SSD与高性能云盘配置建议
jvm·数据库·python