redis pub/sub 不支持消息过期,因其是纯内存广播通道,消息不存储、无ttl、不落盘;需改用set+expire、stream配合定时清理或zset实现带有效期的消息。

Redis 的 PUB/SUB 本身不支持消息过期,所谓“自动清理”必须由业务层兜底实现,不能依赖频道或消息本身。
为什么 PUB/SUB 消息根本不会过期
Pub/Sub 是纯内存广播通道,PUBLISH 返回成功只代表消息已推送给当前在线订阅者,不代表它被存储、落盘或可被后续查询。你无法对某条已发布的消息调用 EXPIRE,因为它没有 key,也不在任何数据结构中持久化。
- 离线订阅者收不到断连期间的任何消息(哪怕只断 1 秒)
-
__keyevent@0__:expired事件监听的是 key 过期,不是“某次 PUBLISH 的内容过期” - 试图给频道名加 TTL、或用
SET channel_name "msg" EX 5再PUBLISH,本质是混用了数据结构,和 Pub/Sub 无关
用 SET + EXPIRE 替代 Pub/Sub 实现带有效期的消息
当业务语义要求“这条通知 30 秒内有效”,应放弃 PUBLISH/SUBSCRIBE,改用可设 TTL 的 key-value 结构,并由消费者主动轮询或监听过期事件。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- 发送端:用
SET msg:123 "payload" EX 30存消息,key 命名带唯一 ID 和业务上下文 - 消费端:不订阅频道,而是定期
GET msg:123;若返回nil,说明已过期或被删 - 清理端(可选):监听
__keyevent@0__:expired,收到msg:123后触发业务逻辑(如标记为“已丢弃”) - 注意:监听需开启
notify-keyspace-events Ex,且客户端必须保持长连接,否则事件丢失不可追溯
用 STREAM + 时间戳 + 外部扫描模拟“软过期”
STREAM 支持写入时打时间戳,适合需要保留消息历史但又想按有效期筛选的场景(比如订单确认倒计时)。
- 写入:用
XADD order_stream * event "confirm" ts "1748296946",其中ts是 Unix 时间戳(表示过期时刻) - 消费:用
XRANGE order_stream - + COUNT 10拉取,再在代码里过滤ts 的条目 - 清理:用
XTRIM order_stream MAXLEN 1000控制长度,但XTRIM不识别时间,需额外定时任务扫描并XDEL过期项 - 性能注意:
XRANGE全量拉取后过滤在大数据量下会变慢,建议把过期时间存进消息体,用XRANGE+ 应用层判断组合使用
最容易被忽略的三个点
很多人卡在“监听到了 expired 事件,但业务没反应”,问题往往不在 Redis 配置,而在链路断点:
- 监听客户端没开
ignore_subscribe_messages=False(默认True会吞掉 subscribe 成功消息,导致后续收不到事件) - 用
psubscribe __keyevent@*:expired试图跨库监听,但 Redis 不支持通配符 db 号,必须明确写@0、@1等 - 业务处理逻辑里没做幂等,同一 key 过期可能触发多次事件(Redis 在定期删除和惰性删除中都可能发事件),
DEL已不存在的 key 虽然安全,但状态更新类操作必须加判重










