Redis 列表(List)完全攻略
“双向链表” —— 消息队列、最新列表、时间线、任务调度… 轻量级队列首选!
一、List 核心特点
| 特性 | 说明 |
|---|---|
| 底层结构 | 双向链表(linkedlist)或 压缩列表(ziplist) |
| 有序 | 插入顺序保持不变 |
| 允许重复 | 同一元素可多次出现 |
| 最大长度 | 2^32 – 1(约 42 亿) |
| 核心操作 O(1) | LPUSH / RPUSH / LPOP / RPOP |
| 阻塞操作 | BLPOP / BRPOP 支持阻塞等待 |
| 内部编码 | 小列表 → ziplist,大列表 → linkedlist |
二、List 命令全表(共 18 个)
| 命令 | 说明 | 示例 |
|---|---|---|
LPUSH key v1 v2 | 左插入(头部) | LPUSH tasks "backup" |
RPUSH key v1 | 右插入(尾部) | RPUSH queue "msg" |
LPOP key | 左弹出(头部) | LPOP tasks |
RPOP key | 右弹出(尾部) | RPOP queue |
BLPOP key timeout | 阻塞左弹出 | BLPOP tasks 30 |
BRPOP key timeout | 阻塞右弹出 | BRPOP queue 0 |
LRANGE key 0 -1 | 查看全部 | LRANGE list 0 -1 |
LINDEX key 0 | 获取索引 | LINDEX list 0 |
LLEN key | 长度 | LLEN queue |
LREM key count value | 删除元素 | LREM list 2 "dup" |
LSET key index value | 设置索引值 | LSET list 0 "new" |
LINSERT key BEFORE|AFTER pivot v | 插入到指定元素前后 | LINSERT list BEFORE "b" "x" |
LTRIM key start end | 保留区间 | LTRIM list 0 99 |
RPOPLPUSH src dest | 原子移动(右出左进) | RPOPLPUSH q1 q2 |
BRPOPLPUSH src dest timeout | 阻塞版移动 | BRPOPLPUSH q1 q2 0 |
三、核心实战场景
1. 消息队列(生产者-消费者)
# 生产者
RPUSH msg:queue "task1" "task2" "task3"
# 消费者(阻塞等待)
BRPOP msg:queue 0
# 返回: 1) "msg:queue" 2) "task3"
轻量级队列首选,比 Stream 简单
2. 最新列表 / 时间线
# 发布动态
LPUSH timeline:user:1001 "post:3001" "post:3002"
# 获取最新10条
LRANGE timeline:user:1001 0 9
# 限制长度(只保留100条)
LTRIM timeline:user:1001 0 99
3. 任务调度(延迟队列)
# 延迟5秒执行
RPUSH delay:queue "task:123"
EXPIRE delay:queue 5
# 消费者轮询
BRPOP delay:queue 1
更推荐用 Sorted Set 做精确延迟队列
4. 环形缓冲区(日志/监控)
RPUSH logs "error: db down"
LTRIM logs 0 999 # 只保留最新1000条
5. 原子任务转移(可靠队列)
# 从待处理 → 处理中
RPOPLPUSH pending:jobs processing:jobs
# 处理完成 → 删除
LREM processing:jobs 1 "job:123"
四、List vs String/Hash 对比
| 场景 | 推荐 |
|---|---|
| 顺序访问 | List |
| 随机访问 | String 或 Hash |
| 频繁中间插入/删除 | 不推荐(O(N)) |
| 需要去重 | Set |
| 复杂消息 + 消费组 | Stream |
五、内部编码优化(ziplist vs linkedlist)
# 小列表 → ziplist(省内存)
LPUSH small a b c
OBJECT ENCODING small → "ziplist"
# 大列表 → linkedlist
LPUSH large {1..10000 elements}
OBJECT ENCODING large → "linkedlist"
切换阈值(配置文件)
| 配置 | 默认值 | 说明 |
|---|---|---|
list-max-ziplist-size | -2 | -2 = 最大 8KB |
list-compress-depth | 0 | 0 = 不压缩 |
生产建议:保持默认,避免大 List
六、性能与内存优化建议
| 建议 | 说明 |
|---|---|
| 避免大 List | > 10,000 元素 → 拆分 |
| 控制长度 | 用 LTRIM 限制 |
| 阻塞弹出 | BLPOP / BRPOP 更高效 |
| Pipeline 批量插入 | 减少网络 RTT |
| 设置 TTL | 防止内存泄漏 |
# 批量插入 1000 条
redis-cli --pipe < commands.txt
七、大 List 排查与拆分
排查大 List
redis-cli --bigkeys
# Biggest list found "msg:queue" with 100000 items
拆分方案
# 原:LPUSH msg:queue "task1" "task2" ...
# 拆:按时间分片
LPUSH msg:queue:20251112 "task1"
LPUSH msg:queue:20251113 "task2"
八、一键速查表
# 队列
RPUSH q v
BRPOP q 0
# 最新列表
LPUSH feed v
LRANGE feed 0 9
LTRIM feed 0 99
# 管理
LLEN q
LINDEX q 0
LREM q 1 "dup"
LTRIM q 0 999
# 原子移动
RPOPLPUSH src dest
九、客户端代码示例
Python (redis-py)
import redis
r = redis.Redis()
# 生产者
r.rpush('tasks', 'backup', 'clean', 'report')
# 消费者(阻塞)
task = r.brpop('tasks', timeout=0)
print(task) # (b'tasks', b'report')
# 最新动态
r.lpush('timeline:1001', 'post:1', 'post:2')
r.ltrim('timeline:1001', 0, 99)
posts = r.lrange('timeline:1001', 0, 9)
Go (go-redis)
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{Addr: "localhost:6379"})
rdb.RPush(ctx, "queue", "task1")
task, _ := rdb.BRPop(ctx, 0, "queue").Result()
fmt.Println(task) // [queue task1]
十、常见问题与解决方案
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 内存暴涨 | List 无限增长 | 加 LTRIM |
LPOP 卡顿 | List 太大 | 拆分或用 Stream |
| 消息丢失 | 进程崩溃 | 用 RPOPLPUSH 可靠队列 |
| 重复消费 | 无 ACK | 用 Stream |
十一、List 在高并发中的妙用
1. 可靠队列模式(处理中队列)
# 消费者
task = RPOPLPUSH pending:jobs processing:jobs
# 处理 task
LREM processing:jobs 1 task # 确认完成
2. 限流队列
LPUSH rate:limit:api:1001 "1"
LTRIM rate:limit:api:1001 0 59 # 保留60个
EXPIRE rate:limit:api:1001 60
LLEN rate:limit:api:1001 > 60 → 限流
完成!你已精通 Redis List!
# 一行命令体验 List 全功能
redis-cli <<EOF
RPUSH q a b c
LPUSH q x y
LRANGE q 0 -1
BRPOP q 1
LLEN q
LTRIM q 0 1
EOF
下一步推荐:
需要我送你:
- “高可靠任务队列(pending + processing)”?
- “社交时间线完整方案(List + ZSet)”?
- “限流系统(List + Pipeline)”?
回复:可靠队列 | 时间线 | 限流 即可!