Redis PUB/SUB 中不存在“过期的频道消息”,因为消息不存储、不落盘、不可设TTL;PUBLISH仅实时推送并丢弃,离线订阅者必然丢失;需时效性或可靠性时应改用SET+EX、STREAM等可控制生命周期的数据结构。

Redis PUB/SUB 里根本没有“过期的频道消息”这回事——因为消息从不存储,也就谈不上过期。
为什么PUBLISH发出去的消息不会过期
你调用PUBLISH channel1 "hello",Redis 只是把字符串立刻推给当前所有在线的SUBSCRIBE客户端,然后丢弃。这条消息没有 key、不进任何数据结构、不落盘、不可查、不可设 TTL。哪怕只有一个订阅者断连 100ms,它就永远收不到这条消息。
常见错误现象包括:
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- 误以为监听
__keyevent@0__:expired能捕获“某次 PUBLISH 的过期”,其实它只通知 key 删除,和 PUB/SUB 无关 - 给频道名本身设 EXPIRE,比如
SET channel1 "dummy" EX 60,这跟订阅逻辑完全脱节 - 在代码里手动 sleep 5 秒再 publish,当成“延迟消息”,但无法保证接收端准时收到,也不解决离线问题
想让消息“带有效期”,必须换数据结构
当业务语义明确要求“这条通知只在 30 秒内有效”,就得放弃PUBLISH/SUBSCRIBE,改用可控制生命周期的结构:
- 用
SET msg:abc "payload" EX 30写入,消费者主动GET msg:abc;返回nil即视为过期或已被清理 - 用
STREAM:写入时在消息体里存 Unix 时间戳字段(如{"ts":1748296946,"data":"order_confirm"}),消费端拉取后自行过滤ts < time.time() - 避免用
ZSET模拟定时任务——虽然能按 score 排序,但没有自动触发机制,仍需轮询,且ZRANGEBYSCORE不等价于“到期广播”
监听__keyevent@0__:expired不是万能解药
这个频道确实能告诉你“某个 key 过期了”,但它有三个硬伤:
- 事件触发不精确:设了
EX 5,可能 8 秒后才发出__keyevent@0__:expired,尤其在低频访问或高负载时 - 事件不可靠:客户端网络抖动断连期间发生的过期事件,永久丢失(PUB/SUB 不保证投递)
- 配置易失效:Docker 或云 Redis 默认关闭
notify-keyspace-events,且CONFIG SET notify-keyspace-events Ex重启即丢,必须写进redis.conf并验证(用PSUBSCRIBE __keyevent@0__:expired+SET test EX 3实测)
真正关键的点在于:别试图给 PUB/SUB “打补丁”。需要时效性、可靠性或离线保障,就换结构;仅需瞬时广播且接受丢失,才用 PUB/SUB。混淆这两类语义,是绝大多数“消息过期失败”问题的根源。

















