Skip to content

02.6 - Stream 消息队列

定位: Redis 5.0 引入的消息队列类型——支持消费者组、消息持久化、ACK 确认 面试高频度: ⭐⭐⭐ 考查方式: 消费组模型、消息 ID、List vs Stream 对比、ACK 机制


一、这是什么?为什么需要它?

是什么

Redis Stream 是一个仅追加(append-only)的日志数据结构,每条消息有唯一的 ID,支持多消费者、消费者组、消息确认。

为什么需要 Stream?

为什么有了 List、Pub/Sub 还需要 Stream?

List(BRPOP/LPUSH)的缺陷:
  ✓ 简单的生产消费者
  ✗ 无法多播(同一条消息给多个消费者)
  ✗ 无消息 ID(ack 靠业务自己)
  ✗ BPOP 是"抢"模式,不是"订阅"模式

Pub/Sub 的缺陷:
  ✓ 支持多播
  ✗ 消息不持久——客户端断开则丢失
  ✗ 无 ACK 机制

Kafka 过于重:
  ✓ 功能完整
  ✗ 需要搭建 Kafka 集群
  ✗ 引入 ZK/KRaft 等依赖

Stream 的定位:
  ✓ 持久化(基于 RDB/AOF)
  ✓ 消费者组(负载均衡消费)
  ✓ 消息 ID + ACK 机制
  ✓ 轻量级——内嵌在 Redis 中
  
它们的关系:
  简单队列 → List
  广播通知 → Pub/Sub
  可靠消息 → Stream

二、原理拆解

2.1 核心概念

2.2 消息 ID 设计

2.3 消费者组工作机制

2.4 底层存储(Rax——基数树)


三、图解全景


四、实战验证

4.1 基本生产消费

bash
redis-cli

# 1. 生产者:添加消息
127.0.0.1:6379> XADD mystream * name "Alice" action "login"
"1678901234567-0"
127.0.0.1:6379> XADD mystream * name "Bob" action "logout"
"1678901234568-0"

# 2. 消费者:读取消息(非阻塞)
127.0.0.1:6379> XRANGE mystream - +  # 所有消息
1) 1) "1678901234567-0"
   2) 1) "name"
      2) "Alice"
      3) "action"
      4) "login"
2) 1) "1678901234568-0"
   2) 1) "name"
      2) "Bob"
      3) "action"
      4) "logout"

# 3. 阻塞读取(从最新开始等)
127.0.0.1:6379> XREAD COUNT 10 BLOCK 5000 STREAMS mystream $
# 等待 5 秒,有消息则返回

# 4. 按 ID 范围读取
127.0.0.1:6379> XRANGE mystream "1678901234567" +

4.2 消费者组

bash
# 创建消费组
redis-cli XGROUP CREATE mystream mygroup $  # 从最新开始
# 或
redis-cli XGROUP CREATE mystream mygroup 0-0  # 从头开始

# 消费者1 读取
redis-cli XREADGROUP GROUP mygroup consumer1 COUNT 1 STREAMS mystream >
# > = 给这个消费者分配未读取的消息

# 消费者2 读取
redis-cli XREADGROUP GROUP mygroup consumer2 COUNT 1 STREAMS mystream >

# ACK
redis-cli XACK mystream mygroup "1678901234567-0"

# 查看 PEL(未确认的消息)
redis-cli XPENDING mystream mygroup

# 查看所有消费者
redis-cli XINFO CONSUMERS mystream mygroup

4.3 Stream vs List 性能对比

bash
# Stream 生产测试
redis-benchmark -n 100000 "XADD mystream * msg hello" -q
# ~110000 条/秒

# List 生产测试(同等操作)
redis-benchmark -n 100000 "LPUSH mylist hello" -q
# ~120000 条/秒

# 结论:Stream 的性能不低于 List,功能更丰富

五、面试视角

追问答案要点
Stream 和 List 做消息队列的区别?List 简单生产消费、无 ACK、无多播;Stream 有消费组、ACK、消息 ID、多播能力
Stream 和 Pub/Sub 的区别?Pub/Sub 消息不持久、连接断开丢消息;Stream 持久化、有 ACK、可以回溯消费
Stream 的消息 ID 怎么生成?格式 时间戳-序号,由 Redis 自动生成或客户端自定义
消费组中的 PEL 是什么?Pending Entries List:已投递消费者但未 XACK 的消息列表,用于故障恢复
XREADGROUP 的 > 参数是什么含义?"给我分配这个组中未被读取的消息",而非指定 ID
Stream 的削峰填谷能力?Stream 可以累积消息(受内存限制),消费慢的生产不受影响;但量超大时建议用 Kafka
什么时候用 Stream 不合适?海量消息堆积(远超内存)→ 用 Kafka;不需要持久化→Pub/Sub

📚 相关链接

  • **List 与 Quicklist** — List 做队列与 Stream 的对比
  • **Pub/Sub** — 三种消息模式的对比
  • ← 返回 **核心数据结构索引**

Knowledge4J — Java 知识库