3. Redis Stream 核心数据结构详解

Redis Stream 是 Redis 5.0 引入的专为消息队列与事件流场景设计的数据结构。它并非简单的列表或发布/订阅,而是融合了持久化、消费者组、消息确认与重试机制的完整消息中间件模型。理解 Stream 的四大核心组件——Stream、Consumer Group、Consumer 与 Pending Entries List——是掌握异步任务接口契约的基础。

3.1 Stream:消息的持久化日志

Stream 本质上是一个仅追加的日志结构。每条消息都有一个唯一的 64 位整数 ID(通常为 <毫秒时间戳>-<序列号>),并包含一个或多个键值对(field-value)。

  • 消息 ID:自动生成或由客户端指定,保证全局有序且唯一。
  • 消息内容:以哈希表形式存储,支持多个字段,适合结构化数据。
  • 持久化:Stream 数据可持久化到 RDB/AOF,重启后不丢失。
  • 范围查询:支持按 ID 范围、时间范围正向或反向遍历。
关键特性:Stream 的日志结构天然支持“回溯消费”——新消费者可以从任意历史位置开始读取,不会丢失已过期的消息(除非被显式裁剪)。

3.2 Consumer Group:多消费者协作模型

Consumer Group 是 Stream 实现负载均衡与容错的核心机制。一个 Stream 可以挂载多个消费者组,每个组独立维护自己的消费进度。

概念 说明
组内竞争 一条消息只会被组内的一个消费者消费(Round-Robin 分配)。
独立进度 每个组维护一个 last_delivered_id,记录组内已分配的最新消息 ID。
消息不删除 消息被消费后仍保留在 Stream 中,直到被 XDEL 或 XTRIM 清理。
组间隔离 不同组的消费者可以独立消费同一条消息,互不干扰。
设计意图:Consumer Group 解决了“广播”与“点对点”的矛盾——同一组内是点对点,不同组之间是广播。

3.3 Consumer:组内的消费单元

Consumer 是实际处理消息的客户端实例。每个 Consumer 必须属于一个 Consumer Group,并在组内拥有唯一名称。

  • 自动注册:当消费者首次使用 XREADGROUP 读取消息时,Redis 自动将其注册到组中。
  • 状态维护:每个 Consumer 维护自己的 Pending Entries List,记录已接收但未确认的消息。
  • 空闲检测:可通过 XINFO CONSUMERS 查看消费者的空闲时间、待处理消息数等指标。

3.4 Pending Entries List (PEL):消息确认与重试机制

PEL 是每个 Consumer 私有的待确认消息列表,是 Stream 实现至少一次投递语义的基石。

# 消费者读取消息后,消息 ID 被加入 PEL
> XREADGROUP GROUP mygroup consumer1 COUNT 1 STREAMS mystream >
1) 1) "mystream"
   2) 1) 1) "1700000000000-0"
         2) 1) "task_id"
            2) "1001"
            3) "payload"
            4) "{\"type\":\"email\"}"

# 消费者处理完成后,必须显式确认
> XACK mystream mygroup 1700000000000-0
(integer) 1  # 消息从 PEL 中移除
PEL 操作 命令 效果
添加 XREADGROUP 消息投递给消费者后自动加入 PEL
确认 XACK 从 PEL 中移除该消息 ID
查询 XPENDING 查看 PEL 中的消息总数、最老消息、各消费者待处理数
认领 XCLAIM 将其他消费者 PEL 中的消息转移给当前消费者(用于故障转移)
重要:如果消费者崩溃且未调用 XACK,消息将永久停留在 PEL 中。必须通过 XCLAIM 或 XAUTOCLAIM 将超时消息重新分配给其他消费者,否则会导致“消息泄漏”。

3.5 核心组件关系图

下图展示了 Stream、Consumer Group、Consumer 与 PEL 之间的完整交互流程:

Redis Stream 核心组件关系图 Redis Stream 核心组件关系图 Stream (消息日志) ID: 1700000000000-0 | task_id: 1001 Consumer Group A last_delivered_id: 1700000000000-0 Consumer Group B last_delivered_id: 1700000000000-0 Consumer A1 PEL: 2 pending Consumer A2 PEL: 0 pending Consumer B1 PEL: 1 pending Consumer B2 PEL: 3 pending Pending Entries List (PEL) 每个 Consumer 私有的待确认消息列表,通过 XACK 移除,通过 XCLAIM 转移

3.6 消息生命周期与状态流转

一条消息从写入到最终确认,经历以下关键状态:

  1. 已写入:消息追加到 Stream 日志,尚未被任何消费者组关注。
  2. 已投递:Consumer Group 将消息分配给某个 Consumer,消息 ID 进入该 Consumer 的 PEL。
  3. 待确认:消息在 PEL 中等待 XACK。若超时未确认,可被其他 Consumer 通过 XCLAIM 认领。
  4. 已确认:Consumer 调用 XACK,消息从 PEL 中移除,但仍在 Stream 中。
  5. 已删除:通过 XDEL 或 XTRIM 从 Stream 中物理移除。
异步任务场景:在异步任务队列中,PEL 扮演“任务追踪器”角色。任务投递后进入 PEL,Worker 处理完成后发送 XACK。如果 Worker 宕机,任务在 PEL 中滞留,监控系统通过 XPENDING 发现并触发 XCLAIM 重新调度,确保任务最终被执行。

3.7 关键命令速查表

组件 命令 功能
Stream XADD 追加消息到 Stream
Stream XRANGE 按 ID 范围查询消息
Stream XTRIM 裁剪 Stream 长度
Group XGROUP CREATE 创建消费者组
Group XREADGROUP 从组中读取消息
Consumer XINFO CONSUMERS 查看消费者状态
PEL XACK 确认消息处理完成
PEL XPENDING 查看待处理消息
PEL XCLAIM 转移待处理消息所有权

3.8 小结

Redis Stream 通过日志结构保证消息持久化与有序性,通过Consumer Group实现水平扩展与负载均衡,通过PEL提供可靠的消息确认与故障恢复能力。这四个组件共同构成了一个轻量级但功能完备的异步消息中间件,是构建分布式异步任务系统的理想选择。