4. Redis 基本数据结构(中):List 类型详解

Redis List 是一种基于双向链表实现的有序字符串集合。它允许在列表的头部(Left)和尾部(Right)进行高效的插入与删除操作,时间复杂度均为 O(1)。List 是 Redis 中表达能力最强的数据结构之一,广泛应用于消息队列、最新列表、时间线等场景。

4.1 核心特性与底层结构

特性 说明
有序性 元素按插入顺序排列,支持通过索引(0 到 N-1)访问
允许重复 同一个值可以在列表中出现多次
双端操作 支持在头部和尾部同时进行 push / pop
最大长度 最多 2³² - 1 个元素(约 42.9 亿)
底层编码 元素较少时使用 quicklist(压缩链表 + 双向链表混合结构)
面试高频:Redis 3.2 之后 List 底层统一使用 quicklist,它结合了 ziplist 的内存紧凑性和 linkedlist 的快速双端操作能力,是空间与时间的平衡设计。

4.2 常用命令详解

4.2.1 插入命令:LPUSH / RPUSH

# LPUSH:将一个或多个值插入到列表头部(左侧)
LPUSH mylist "A"
LPUSH mylist "B" "C"   # 可批量插入,顺序为 C -> B -> A

# RPUSH:将一个或多个值插入到列表尾部(右侧)
RPUSH mylist "X"
RPUSH mylist "Y" "Z"   # 顺序为 X -> Y -> Z
命令 时间复杂度 返回值 典型用途
LPUSH O(1) 每个元素 插入后列表的长度 构建栈、最新消息入队
RPUSH O(1) 每个元素 插入后列表的长度 构建队列、追加日志

4.2.2 弹出命令:LPOP / RPOP

# LPOP:移除并返回列表的第一个元素(左侧)
LPOP mylist

# RPOP:移除并返回列表的最后一个元素(右侧)
RPOP mylist

# 支持可选参数 count(Redis 6.2+)
LPOP mylist 2    # 一次弹出 2 个元素
注意:当列表为空时,LPOP / RPOP 返回 nil。在消息队列场景中,建议配合阻塞命令 BLPOP / BRPOP 使用,避免空轮询浪费 CPU。

4.2.3 长度与范围查询

# LLEN:获取列表长度
LLEN mylist

# LRANGE:获取指定范围内的元素(闭区间)
LRANGE mylist 0 -1    # 获取全部元素
LRANGE mylist 0 2     # 获取前 3 个元素
命令 时间复杂度 说明
LLEN O(1) 直接返回 quicklist 中记录的 len 字段
LRANGE O(S+N) S 为偏移量,N 为返回元素个数;大范围查询需谨慎

4.3 应用场景一:消息队列

Redis List 天然支持生产者-消费者模式:生产者使用 RPUSH 将消息放入队列尾部,消费者使用 LPOP 从队列头部取出消息。这种模型称为 FIFO(先进先出)队列。

经典实现:
# 生产者
RPUSH task_queue "send_email:user_1001"
RPUSH task_queue "generate_report:2024-01"

# 消费者(轮询模式)
LPOP task_queue

# 消费者(阻塞模式,推荐)
BRPOP task_queue 0   # 0 表示无限等待
基于 Redis List 的消息队列架构 生产者 1 生产者 2 生产者 N Redis List RPUSH 写入尾部 LPOP 从头部消费 msg1 msg2 msg3 ... 消费者 1 消费者 2 消费者 N 推荐使用 BRPOP 替代 LPOP 实现阻塞消费,避免空轮询浪费 CPU

4.4 应用场景二:最新列表

利用 LPUSH 将新元素插入列表头部,再配合 LTRIM 截断列表长度,可以高效维护一个固定大小的“最新 N 条”列表。这是社交 Feed、新闻头条、操作日志等场景的经典实现。

# 用户发布一条新动态
LPUSH user:1001:posts "今天天气真好!"

# 只保留最近 10 条动态
LTRIM user:1001:posts 0 9

# 获取最新 5 条
LRANGE user:1001:posts 0 4
步骤 命令 效果
1 LPUSH key new_item 新元素插入头部,成为最新
2 LTRIM key 0 N-1 只保留前 N 个元素,删除旧数据
3 LRANGE key 0 M-1 获取最新的 M 条记录
性能优势:LPUSH + LTRIM 组合的时间复杂度为 O(1) + O(N),其中 N 是被删除的元素数量。对于固定大小的最新列表,整体开销极低,远优于数据库的 ORDER BY + LIMIT 查询。
最新列表实现流程(LPUSH + LTRIM) 新元素 "动态 #4" LPUSH 插入头部 LTRIM 0 2 保留前 3 条 列表变化: LPUSH 前:[动态 #1] [动态 #2] [动态 #3] LPUSH 后:[动态 #4] [动态 #1] [动态 #2] [动态 #3] LTRIM 后:[动态 #4] [动态 #1] [动态 #2] ← 动态 #3 被删除

4.5 阻塞操作与安全队列

Redis 提供了 BLPOP 和 BRPOP 阻塞版本命令,当列表为空时,连接会阻塞直到有新的元素被推入或超时。这是构建可靠消息队列的关键。

# 阻塞弹出,超时时间 30 秒
BRPOP task_queue 30

# 同时监听多个队列,优先返回非空队列的第一个元素
BLPOP queue:high queue:normal queue:low 0
命令 非阻塞版本 超时参数 返回值
BLPOP LPOP 秒数,0 表示无限等待 [key, value] 或 nil
BRPOP RPOP 秒数,0 表示无限等待 [key, value] 或 nil
可靠性提醒:List 实现的消息队列是至少一次语义。如果消费者在 LPOP 后崩溃,消息会丢失。对于需要严格可靠性的场景,建议使用 Redis Stream 或引入消息确认机制(RPOPLPUSH 模式)。

4.6 其他实用命令

# LINDEX:通过索引获取元素
LINDEX mylist 0      # 第一个元素
LINDEX mylist -1     # 最后一个元素

# LSET:修改指定索引的元素
LSET mylist 0 "new_value"

# LREM:删除指定数量的匹配元素
LREM mylist 2 "A"    # 从头部开始删除 2 个值为 "A" 的元素

# RPOPLPUSH:原子性地将源列表尾部元素弹出并推入目标列表头部
RPOPLPUSH source_list target_list
RPOPLPUSH 妙用:可用于实现可靠队列——消费者从源列表 RPOP,同时将消息备份到目标列表。处理成功后删除备份,处理失败则可以从备份列表恢复,实现消息的可靠投递。

4.7 场景选择指南

场景 推荐模式 核心命令 注意事项
消息队列 RPUSH + BLPOP RPUSH / BLPOP / BRPOP 消息可能丢失,考虑 RPOPLPUSH 备份
最新列表 LPUSH + LTRIM LPUSH / LTRIM / LRANGE 控制列表长度,避免内存膨胀
栈结构 LPUSH + LPOP LPUSH / LPOP LIFO 后进先出
时间线/Feed LPUSH + LRANGE LPUSH / LRANGE / LTRIM 结合分页参数实现滚动加载

Redis List 凭借其双端操作的高效性和丰富的命令集,成为构建实时数据流、任务队列和时间线系统的首选数据结构。理解其底层 quicklist 机制和阻塞语义,是正确使用 List 的关键。