redis subscribe 不支持通配符或层级匹配,需用 psubscribe 实现模式订阅,但存在性能开销;推荐客户端 trie 路由、固定频道+消息内嵌路径或 stream+consumer group 方案替代。

Redis 的 SUBSCRIBE 不支持通配符匹配层级路径
直接用 SUBSCRIBE 订阅 user:1001:order 和 user:1001:profile,必须分别发起两次命令;它不识别冒号分隔的“层级”,也不支持 user:1001:* 这类 glob 模式(那是 PSUBSCRIBE 的事)。想靠原生命令实现“订阅某个用户所有事件”,得用 PSUBSCRIBE 配合模式匹配,但要注意:模式匹配是服务端逐个比对,订阅模式越多,CPU 开销越明显。
常见错误现象:redis-cli 里执行 SUBSCRIBE user:* 没反应——因为 SUBSCRIBE 根本不处理星号,它只认字面频道名。
-
PSUBSCRIBE才支持user:*、user:1001:*等 glob 模式 - 每个
PSUBSCRIBE模式会占用一个连接级匹配槽,大量模式可能拖慢 PUBSUB NUMPAT 返回值 - 客户端断线重连后,需重新
PSUBSCRIBE,模式不会自动恢复
用前缀树(Trie)在客户端做二级路由分发
服务端只负责广播原始消息,真正的“层级解析”和“多级订阅”逻辑应由客户端承担。例如发布消息到 event:user:1001:order:created,客户端收到后,按冒号切分得到 ["event","user","1001","order","created"],再用本地 Trie 结构查出哪些回调函数注册了 event:user:* 、event:user:1001:order:* 或 event:user:1001:* 。
这样做的好处是解耦:Redis 不承担路由逻辑,扩展灵活;坏处是每个客户端都要维护一套订阅映射表,且无法跨进程共享。
- 推荐用
node-redlock(Node)或redis-py-cluster(Python)管理多实例间的一致性 - 避免在回调里做耗时操作,否则阻塞整个 pub/sub 线程(如 Redis-py 默认单线程处理消息)
- 层级过深(如
a:b:c:d:e:f:g)会导致 Trie 查找变慢,建议控制在 5 层以内
用多个频道 + 消息体嵌套实现语义化分级
不依赖模式匹配,改用“固定频道 + 消息内嵌路径”的组合。例如统一发布到 bus:core,消息体为 JSON:
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
{"path": "user.1001.order.created", "data": {...}}。消费者自行解析 path 字段,再决定是否处理。这种方式完全绕开 Redis 的模式机制,兼容任何客户端库,也方便后续接入 Kafka 或 NATS 做协议迁移。但代价是序列化/反序列化开销,以及丢失 Redis 原生的“按频道过滤”能力。
- 务必对
path字段做长度限制(如 ≤ 256 字符),防止恶意长路径打爆内存 - 避免在
path中使用点号以外的分隔符(如斜杠),否则不同语言的 split 行为不一致 - 如果用 Lua 脚本预处理消息,注意
EVAL不能调用PUBLISH,只能用redis.call("PUBLISH", ...)
用 Stream + consumer group 模拟可回溯的层级订阅
当需要“某类事件的历史重放”或“多个下游各自维护偏移量”,Pub/Sub 就不够用了——它无持久化、无 ACK、不保证顺序。此时该换 XADD + XGROUP,把频道层级转为 stream key 名称,例如:stream:user:1001:order、stream:user:1001:profile。
每个 consumer group 对应一个“订阅级别”,比如 group:tenant:all 消费所有 user:* stream,而 group:user:1001 只消费具体用户的 stream。这本质上是用 key 命名空间模拟层级,靠客户端组织读取逻辑。
- Stream 支持
XREADGROUP的阻塞读,体验接近 Pub/Sub,但多了游标控制 - 注意
XTRIM策略:用MAXLEN ~1000防止 stream 无限膨胀 - 不要在一个 stream 里混入多种业务类型事件,否则 group 内部无法做细粒度过滤
实际落地时,最常被忽略的是 consumer group 的初始化时机:XGROUP CREATE 必须在第一个 XADD 之前执行,否则会报错 NOGROUP No such key;而很多人习惯先发消息再建 group,结果上游数据就丢了。










