元素码农
基础
UML建模
数据结构
算法
设计模式
网络
TCP/IP协议
HTTPS安全机制
WebSocket实时通信
数据库
sqlite
postgresql
clickhouse
后端
rust
go
java
php
mysql
redis
mongodb
etcd
nats
zincsearch
前端
浏览器
javascript
typescript
vue3
react
游戏
unity
unreal
C++
C#
Lua
App
android
ios
flutter
react-native
安全
Web安全
测试
软件测试
自动化测试 - Playwright
人工智能
Python
langChain
langGraph
运维
linux
docker
工具
git
svn
🌞
🌙
目录
▶
Redis核心
▶
数据结构
字符串实现
哈希表实现
列表实现
集合实现
有序集合实现
▶
内存管理
内存分配策略
淘汰算法
▶
持久化
▶
RDB机制
快照生成原理
文件格式解析
▶
AOF机制
命令追加策略
重写过程分析
▶
高可用
▶
主从复制
SYNC原理
增量复制
▶
哨兵机制
故障检测
领导选举
▶
高级特性
▶
事务系统
ACID实现
WATCH原理
▶
Lua脚本
沙盒环境
执行管道
▶
实战问题
▶
缓存问题
缓存雪崩
缓存穿透
缓存击穿
缓存预热
▶
数据一致性
读写一致性
双写一致性
▶
性能优化
大key处理
热点key优化
发布时间:
2025-03-22 10:35
↑
☰
# Redis列表(List)实现原理 ## 引言 Redis列表是一个双向链表结构,可以高效地在两端进行push和pop操作。本文将深入探讨Redis列表的实现原理、内部编码方式以及实际应用场景,帮助读者更好地理解和使用这一数据结构。 ## 底层实现 ### 双向链表 Redis列表的标准实现是一个双向链表(quicklist),每个节点都包含了指向前后节点的指针: ```c typedef struct listNode { struct listNode *prev; // 前置节点 struct listNode *next; // 后置节点 void *value; // 节点值 } listNode; typedef struct list { listNode *head; // 表头节点 listNode *tail; // 表尾节点 unsigned long len; // 链表所包含的节点数量 void *(*dup)(void *ptr); // 节点值复制函数 void (*free)(void *ptr); // 节点值释放函数 int (*match)(void *ptr, void *key); // 节点值对比函数 } list; ``` ### QuickList结构 为了节省内存,Redis 3.2之后采用quicklist作为列表的内部实现: ```c typedef struct quicklist { quicklistNode *head; // 头节点指针 quicklistNode *tail; // 尾节点指针 unsigned long count; // 所有ziplist中的总元素个数 unsigned long len; // quicklistNode的个数 int fill : 16; // ziplist大小限制 unsigned int compress : 16; // 压缩深度 } quicklist; typedef struct quicklistNode { struct quicklistNode *prev; // 前一个节点 struct quicklistNode *next; // 后一个节点 unsigned char *zl; // ziplist指针 unsigned int sz; // ziplist字节大小 unsigned int count : 16; // ziplist中的元素个数 unsigned int encoding : 2; // RAW==1 or LZF==2 unsigned int container : 2;// NONE==1 or ZIPLIST==2 unsigned int recompress : 1;// 是否需要重新压缩 unsigned int attempted_compress : 1; // 压缩是否成功 unsigned int extra : 10; // 预留字段 } quicklistNode; ``` ### 压缩列表(ziplist) quicklist中的每个节点都是一个压缩列表: ```c // ziplist结构 <zlbytes><zltail><zllen><entry>...<entry><zlend> // entry结构 <prevlen><encoding><data> ``` ## 内部编码 Redis列表有两种内部编码方式: ### 1. ziplist(压缩列表) 当列表的元素个数小于512个,且每个元素的值都小于64字节时,使用ziplist编码: ```redis LPUSH mylist "hello" "world" ``` ### 2. quicklist(快速列表) 当不满足ziplist条件时,使用quicklist编码: ```redis LPUSH biglist "这是一个很长的字符串..." ``` ## 常用命令 ### 基本操作 ```redis # 从列表左端插入元素 LPUSH key value [value ...] # 从列表右端插入元素 RPUSH key value [value ...] # 从列表左端弹出元素 LPOP key # 从列表右端弹出元素 RPOP key ``` ### 范围操作 ```redis # 获取列表指定范围内的元素 LRANGE key start stop # 修剪列表,只保留指定范围内的元素 LTRIM key start stop ``` ### 阻塞操作 ```redis # 阻塞式左端弹出 BLPOP key [key ...] timeout # 阻塞式右端弹出 BRPOP key [key ...] timeout ``` ## 应用场景 ### 1. 消息队列 利用列表的LPUSH+BRPOP命令组合实现消息队列: ```redis # 生产者:插入消息 LPUSH message_queue "message1" # 消费者:阻塞式获取消息 BRPOP message_queue 0 ``` ### 2. 最新动态 使用LPUSH+LTRIM实现最新N条动态的存储: ```redis # 添加新动态 LPUSH user:activities "发布了新文章" # 只保留最新的100条动态 LTRIM user:activities 0 99 ``` ### 3. 任务队列 实现带优先级的任务队列: ```redis # 添加高优先级任务 LPUSH high_priority_queue "task1" # 添加低优先级任务 LPUSH low_priority_queue "task2" # 优先处理高优先级任务 BRPOP high_priority_queue low_priority_queue 0 ``` ### 4. 数据流 实现简单的时间序列数据存储: ```redis # 记录温度数据 LPUSH temperature:logs "25.6" # 获取最近10条记录 LRANGE temperature:logs 0 9 ``` ## 性能优化 ### 1. 列表长度控制 - 使用LTRIM及时清理不需要的数据 - 避免列表过长导致内存占用过大 ```redis # 限制列表长度 LPUSH mylist "new_item" LTRIM mylist 0 999 # 只保留1000条数据 ``` ### 2. 批量操作 优先使用批量命令: ```redis # 优先使用 LPUSH mylist "item1" "item2" "item3" # 而不是多次 LPUSH mylist "item1" LPUSH mylist "item2" LPUSH mylist "item3" ``` ### 3. 内存优化 - 合理设置quicklist配置参数 - 注意压缩深度的设置 ```redis # 配置quicklist参数 list-max-ziplist-size 8kb list-compress-depth 1 ``` ## 注意事项 1. **内存管理** - quicklist节点数量会影响内存使用 - 压缩深度设置需要权衡CPU和内存 2. **原子性** - 单个命令是原子的 - 多个命令的原子性需要使用事务 3. **阻塞操作** - 合理设置超时时间 - 注意处理超时情况 4. **数据持久性** - 考虑是否需要持久化 - 选择合适的持久化策略 ## 总结 Redis列表通过quicklist实现,结合了双向链表和压缩列表的优点,在保证访问效率的同时节省内存空间。它适用于消息队列、最新动态等场景,通过合理使用各种命令和优化策略,可以充分发挥Redis列表的性能优势。在使用时需要注意内存管理、原子性和阻塞操作等问题,根据实际需求选择合适的使用方式。