Redis过期监听需开启notify-keyspace-events "Ex"并订阅__keyevent@0__:expired频道,但存在延迟、丢失、无ACK等缺陷,仅宜作补充;更可靠方案是用Sorted Set+定时扫描主动处理。

Redis 的过期 Key 监听机制确实能快速搭起一个“看起来能用”的订单超时关闭方案,但它本质是旁路辅助手段,不能当作主链路。下面直接说清楚怎么配、怎么写、为什么慎用。
Redis 配置必须开事件通知
默认 Redis 是关闭键空间通知的,必须手动开启。编辑 redis.conf,找到这一行:
notify-keyspace-events ""
改成:
立即学习“Java免费学习笔记(深入)”;
notify-keyspace-events "Ex"
Java Maven 项目审查工作流的通用输入处理。当 Java Maven 项目以 ZIP压缩包或 GitLab 仓库 URL 形式提供且需要...时使用。
- E 表示启用键事件通知(event)
- x 表示只监听过期(expired)事件
- 不需要 k(键空间),因为监听的是事件频道,不是键名前缀
- 改完后必须重启 Redis,否则不生效
Spring Boot 中监听过期事件
核心是订阅 __keyevent@0__:expired 这个频道(0 是数据库编号,按实际 DB 调整)。Spring Data Redis 提供了现成封装:
- 定义 RedisMessageListenerContainer Bean,注入连接工厂
- 写一个类继承 KeyExpirationEventMessageListener
- 重写 onMessage 方法,从 message.toString() 拿到过期 key
- 判断 key 是否以 "order:" 开头,再查 DB 确认订单状态是否仍为“待支付”
- 是则更新状态为“已取消”,不是则忽略(比如支付成功后已删 key)
关键缺陷必须提前兜底
这个方案最大的问题是:它不保证准时、不保证送达、不保证成功。
- Redis 过期是“定时扫描 + 惰性删除”,过期通知可能延迟数秒甚至几分钟
- 通知走的是 Pub/Sub,断连期间事件直接丢失,没有重试和 ACK
- 监听器只能拿到 key,拿不到 value 或上下文,业务逻辑要自己查库补全
- 高并发下单时,大量 key 同时过期可能引发事件洪峰,压垮监听服务
所以实际项目中,它只能作为补充——比如配合定时任务做二次兜底,或用于非核心场景的轻量提醒。
更靠谱的替代思路
如果只是想轻量又可靠,建议换一种“伪延时”设计:
- 下单时把订单 ID 和过期时间(如 System.currentTimeMillis() + 30 * 60 * 1000)存入 Redis,用 Sorted Set,score 设为过期时间戳
- 起一个轻量调度线程,每 5 秒 zrangebyscore 查一批 score ≤ 当前时间的订单 ID
- 批量处理并删除已处理项 —— 这种方式可控、可重试、可监控,且不依赖 Redis 过期机制本身
不复杂但容易忽略:真正稳定的延时任务,从来不是靠“等 Redis 删除”,而是靠“主动查+精准捞”。

















