Redis 中的 Stream 详解

Redis 在 5.0 引入了 Stream 数据结构,用于解决传统数据结构(如 List、Pub/Sub)在消息队列场景中的不足。

Stream 可以看作是一个:

支持持久化、可回溯、带消费组的日志型消息队列

它的设计目标是提供类似 Kafka 的核心能力,但更轻量、更易用。

目录


一、Stream 是什么

从本质上看,Stream 是一种:

按时间顺序追加的消息日志(append-only log)

你可以类比:

概念 类比
Stream Kafka Topic
Entry(消息) 一条日志
Consumer Group 消费组
Consumer 消费者

Redis Stream从添加消息到消费消息的整个流程


二、Stream 的数据结构

一个 Stream 由多个 Entry(消息)组成:

1
2
3
4
mystream
├── 1700000000000-0 → {name: "A", action: "buy"}
├── 1700000000001-0 → {name: "B", action: "pay"}
└── 1700000000002-0 → {name: "C", action: "refund"}

2.1 消息 ID 结构

每条消息都有唯一 ID:

1
timestamp-sequence

例如:

1
1700000000000-0

含义:

  • timestamp:毫秒时间戳
  • sequence:同一毫秒内的递增序号

👉 特点:

  • 全局有序
  • 可用于定位消费位置

三、基本操作

3.1 写入消息

1
XADD mystream * key value

说明:

  • * 表示自动生成 ID

3.2 读取消息

1
XREAD STREAMS mystream 0

3.3 阻塞读取

1
XREAD BLOCK 5000 STREAMS mystream $
  • $:从最新消息开始
  • BLOCK:阻塞等待新消息

四、消费组机制

Stream 最核心能力之一是消费组。

4.1 创建消费组

1
XGROUP CREATE mystream group1 0

4.2 消费消息

1
XREADGROUP GROUP group1 consumer1 STREAMS mystream >

说明:

  • >:只消费新消息
  • 自动分配给不同消费者

4.3 负载均衡

多个消费者:

1
2
3
4
group1
├── consumer1
├── consumer2
└── consumer3

👉 Redis 会自动分配消息,实现负载均衡


五、消息读取模型:Pull 还是 Push?

5.1 核心结论

Redis Stream 是 拉取模型(Pull)

5.2 两种方式

非阻塞轮询

1
XREAD STREAMS mystream 0

阻塞读取(推荐)

1
XREAD BLOCK 5000 STREAMS mystream $

5.3 工作原理

1
2
3
4
5
消费者发起请求

无消息 → Redis 挂起连接

有消息 → Redis 唤醒连接

👉 本质:

阻塞等待 + 事件唤醒(不是推送)


六、可靠性:为什么能做到至少一次消费

6.1 三大机制

  1. Consumer Group
  2. Pending List(未确认队列)
  3. ACK 确认机制

6.2 消息生命周期

1
2
3
4
5
6
7
8
9
XADD 写入

XREADGROUP 分配

进入 Pending List

业务处理

XACK 确认

6.3 Pending List 的作用

记录:

所有“已投递但未确认”的消息

6.4 宕机恢复

查看未确认消息

1
XPENDING mystream group1

重新分配

1
XCLAIM mystream group1 consumer2 60000 <id>

6.5 为什么不会丢消息

  • 未 ACK 不删除
  • 可以重新分配

6.6 为什么会重复消费

1
2
3
4
处理成功
但 ACK 失败
→ Redis 认为未处理
→ 再次投递

👉 因此是:

At-Least-Once(至少一次)


七、Stream vs List vs Pub/Sub

特性 List Pub/Sub Stream
持久化
消费组
ACK
可回溯
可靠性

八、底层实现原理

Stream 并不是简单链表,而是:

  • Radix Tree(基数树)
    • listpack(压缩结构)

8.1 优势

  • 内存占用低
  • 支持海量数据
  • 按 ID 有序存储

九、使用场景

9.1 异步任务队列

  • 订单处理
  • 邮件发送

9.2 日志流处理

  • 用户行为
  • 系统日志

9.3 轻量级消息队列

  • 替代 RabbitMQ(简单场景)
  • 小规模替代 Kafka

十、面试高频总结

10.1 Stream 是什么?

日志型消息队列,支持消费组和可靠消费

10.2 如何感知新消息?

阻塞拉取(BLOCK)+ 事件唤醒

10.3 如何保证可靠性?

Pending List + ACK + 重分配

10.4 为什么是至少一次?

ACK 可能失败 → 重复消费


十一、总结

Redis Stream 是一个基于拉取模型的持久化消息队列,通过消费组、Pending List 和 ACK 机制,实现可回溯和至少一次的可靠消费。