Redis Pub/Sub不能当消息队列用,因其为纯内存广播、无持久化、无确认与重试机制,断连即丢消息;可靠场景必须用Stream配合消费组与ACK机制。

Redis 的 Pub/Sub 不能当消息队列用,真要用队列必须上 Stream;C# 里用 StackExchange.Redis 操作 Stream 是当前最稳的路径。
Pub/Sub 在 C# 里为什么不能当队列用
因为 PUB/SUB 是纯内存广播,没持久化、没确认机制、没重试逻辑。只要订阅者断连,期间所有消息就彻底丢弃——PUBLISH 返回 (integer) 0 就是明确告诉你:没人在线,消息蒸发了。
- 常见错误现象:
SUBSCRIBE后程序崩溃或网络抖动,再连上收不到断连期间的消息 - 使用场景仅限实时通知(如 WebSocket 推送、日志广播),不适用于订单、支付、任务调度等需可靠传递的业务
- C# 中用
ISubscriber.Subscribe()建立连接后,一旦 socket 断开,不会自动重连,也无 offset 追溯能力
Stream + 消费组才是 C# 正确的消息队列姿势
Stream 是 Redis 5.0 引入的持久化消息结构,支持多消费者、ACK 确认、pending 列表和自动重投,语义接近 Kafka。C# 要用它,得靠 StackExchange.Redis 的 IDatabase.Stream* 系列方法。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- 必须显式创建消费组:
database.StreamCreateConsumerGroup("mystream", "mygroup", "$"),其中"$"表示从最新消息开始,用"0"才能读历史 - 发消息用
database.StreamAdd("mystream", "*", new NameValueEntry("data", "payload")),"*"让 Redis 自动生成 ID - 消费时调用
database.StreamReadGroup("mystream", "mygroup", "consumer1", ">"),">"表示只读新消息,避免重复消费 - 收到消息后必须手动
database.StreamAcknowledge("mystream", "mygroup", messageId),否则会一直留在PENDING列表里
StackExchange.Redis 操作 Stream 容易踩的坑
这个库对 Stream 的封装比较底层,很多行为不像高级 SDK 那样“自动”,稍不注意就卡死或漏消息。
-
StreamReadGroup默认阻塞 0ms,等于轮询,CPU 白耗;加TimeSpan.FromMilliseconds(5000)参数才能真正阻塞等待 - 消费组名和消费者名区分大小写,
"MyGroup"和"mygroup"是两个组,别混用 - 同一个消费者重复调用
StreamReadGroup不会自动跳过已读消息,得靠 ACK + pending 清理逻辑配合 - 如果用
StreamRange直接查全量数据,大数据量下会 OOM,务必配合count参数分页,比如database.StreamRange("mystream", "-", "+", count: 100)
什么时候该放弃 Redis Stream 改用专业 MQ
当你的业务出现这些信号,说明 Redis 已经在勉强支撑:
- 需要跨数据中心复制消息,
Stream不支持原生主从同步消费进度 - 单个
Stream数据量超 1GB,StreamTrim或XRANGE响应明显变慢 - 要按优先级、延迟、死信路由分发消息,
Stream没有内置策略,全得自己实现状态机 - 团队里没人愿意 debug
XPENDING输出的那堆十六进制 ID 和时间戳——这其实是最容易被忽略的复杂点

















