Redis Stream 深入理解:它到底解决了什么问题

在 Redis 5.0 之前,如果你想用 Redis 做"消息",基本绕不开 List 或 Pub/Sub。List 可以做阻塞消费,但消息一旦被取走就彻底消失,消费者挂了就是事故;Pub/Sub 更直接,消息不落地,订阅者一掉线,消息就像没发生过一样。这两种模型都不具备可靠消息系统的核心能力:可追溯、可确认、可重试。

Redis Stream 的出现,本质上就是 Redis 官方承认了一件事:Redis 需要一个真正的日志型消息结构

Stream 的核心设计非常像 Kafka:所有消息只追加、不修改,每条消息都有一个全局有序的 ID。这个 ID 由时间戳和序号组成,例如:

bash 复制代码
1700000000000-0
1700000000000-1

Redis 保证 ID 单调递增,这一点非常关键,因为它让 Stream 天然支持范围查询、顺序消费和断点恢复。

向 Stream 中写入消息使用的是 XADD

bash 复制代码
XADD mystream * user alice action login

* 表示让 Redis 自动生成 ID。每条消息不是一个简单字符串,而是一组 field-value,这一点和 List 最大的不同在于:Stream 的 value 是结构化的

真正让 Stream 成为"消息队列"的,并不是 XADD,而是 消费组(Consumer Group)。消费组解决的是"同一条消息只能被一个消费者处理,并且必须被确认"。

你可以先创建一个消费组:

bash 复制代码
XGROUP CREATE mystream group1 0

0 表示从头开始消费历史消息。

消费者使用 XREADGROUP 拉消息:

bash 复制代码
XREADGROUP GROUP group1 c1 COUNT 1 BLOCK 0 STREAMS mystream >

这里有一个非常容易被忽略但极其重要的点:

当这条消息被投递给消费者 c1 时,它并没有从 Stream 中删除 ,而是被放进了消费组的一个结构里,叫 PEL(Pending Entries List)

PEL 的含义是:已投递,但尚未确认的消息

只要你没 XACK,Redis 就认为这条消息"可能没处理完"。

bash 复制代码
XACK mystream group1 1700000000000-0

ACK 之后,这条消息才会从 PEL 中移除。但注意:消息本身依然留在 Stream 里,只是这个消费组不再关心它。

PEL 的存在,直接解决了 List 和 Pub/Sub 永远解决不了的问题:
消费者宕机怎么办?

假设消费者 c1 读到一条消息,还没来得及处理就挂了,这条消息就会一直躺在 PEL 里。你可以通过:

bash 复制代码
XPENDING mystream group1

看到有哪些"卡住"的消息,然后用:

bash 复制代码
XCLAIM mystream group1 c2 60000 1700000000000-0

把超过 60 秒没处理的消息,抢给另一个消费者 c2

这一套机制,本质上就是 Redis 自己实现的 at-least-once 消费模型

从内部实现来看,Stream 并不是一个简单的链表。Redis 使用的是 Radix Tree(基数树) 来存储消息 ID 和内容,这让它在 ID 有序的前提下,依然可以高效做区间扫描,比如:

bash 复制代码
XRANGE mystream 1700000000000-0 +

这也是为什么 Stream 可以非常自然地支持"重放历史消息",而 List 和 Pub/Sub 做不到。

需要注意的是,Stream 不会自动删除消息。如果你只消费、不裁剪,Stream 会无限增长:

bash 复制代码
XTRIM mystream MAXLEN ~ 100000

这里的 ~ 是近似裁剪,Redis 会在性能和精确度之间做权衡。

还有一个实践中的坑:PEL 一定要被清理。如果消费者逻辑有问题,消息一直不 ACK,PEL 会越来越大,最后看起来像"没消息可消费",但实际上全卡在 PEL 里。

相关推荐
oradh19 小时前
Oracle XTTS实现跨版本迁移和升级(Oracle 11g单库升级至19C RAC集群)
数据库·oracle·11g升级19c·xtts跨版本迁移和升级
z1234567898620 小时前
2026最新两款AI编程工具深度对比实测
java·数据库·ai编程
程序猿DD20 小时前
一个 API Key,统一调用大模型、生图和联网搜索
数据库·网关
TlSfoward21 小时前
抓包代理链路下的 TLS 指纹变化分析 TLSFOWARD抓包工具
数据库·爬虫·网络协议·搜索引擎·php
数智化管理手记21 小时前
账龄分析手工统计易遗漏?自动账龄分析工具怎么搭建
大数据·网络·数据库·人工智能·数据挖掘
LabVIEW开发21 小时前
NI Package Manager安装LabVIEW时InternalServerError错误
网络·数据库·labview·labview知识·labview功能·labview程序
SelectDB1 天前
PostgreSQL 实时同步到 Apache Doris:一站式 CDC 解决方案
数据库
Database_Cool_1 天前
AI Agent 长期记忆怎么存?阿里云 PolarDB-X 对接 Mem0 记忆框架实践
数据库·阿里云
时空无限1 天前
vllm 缓存对模型启动时间的影响
缓存·vllm
会编程的土豆1 天前
数据库范式
数据库·oracle