必须配置 notify-keyspace-events ex 才能接收过期事件,且需在每个 redis 节点的 redis.conf 中设置并重启;监听时应使用 psubscribe 订阅 keyevent@n:expired 模式,注意数据库编号与集群各主节点单独启用。

Redis 怎么开启过期事件通知?
不开启 notify-keyspace-events,监听就永远收不到消息——这是 90% 的人卡住的第一步。它默认是关闭的,不是“没配对”,而是压根没通电。
- 必须设为
Ex(大小写敏感):小写e表示键事件,小写x表示过期事件;Ex是最小可用组合,加多了(比如AKE)会推送大量无关事件,拖慢 Pub/Sub 频道 - 生产环境务必改配置文件
redis.conf,再重启;用CONFIG SET notify-keyspace-events Ex临时生效虽快,但 Redis 重启后丢失,集群里漏掉一个节点就可能收不到某库的过期消息 - 注意数据库编号:事件频道是
<strong>keyevent@0</strong>:expired这种格式,@0对应 DB 0;如果你的 key 存在DB 2,得订阅<strong>keyevent@2</strong>:expired,别硬写死 @0
Python 怎么稳定监听 expired 频道?
pubsub.psubscribe() 比 subscribe() 更靠谱,因为频道名含 @ 和冒号,属于模式匹配场景,普通 subscribe 会失败或静默忽略。
- 不要用
r.pubsub().subscribe(...)匿名对象——它可能在 GC 时被回收,导致连接断开、消息丢失;应持有引用并显式管理生命周期 - 消息类型必须判断
message['type'] == 'pmessage'(pattern message),不是message;否则会把订阅成功提示、心跳等干扰信息当过期事件处理 - 过期那一刻,key 已从内存删除,
r.get(expired_key)一定返回None;如果真需要 value,得在 set 时同步存一份到另一个 key,或改用 Lua 脚本原子写入+发布
import redis
r = redis.Redis(decode_responses=True)
p = r.pubsub()
p.psubscribe('__keyevent@0__:expired')
<p>for msg in p.listen():
if msg['type'] == 'pmessage':
key = msg['data']
print(f"⚠️ {key} 刚过期 —— 此刻已无法 get()")</p>为什么监听不到集群中某些节点的过期事件?
Redis 集群不是自动广播事件的:每个分片(shard)只负责自己槽位(slot)内 key 的过期通知,且 notify-keyspace-events 必须在每个主节点上单独开启。
- 常见错误:只在入口 proxy 或某个 master 上执行
CONFIG SET,以为“连上了就能收”;实际只有该节点负责的 key 过期时才会发 - 如果你用 Redisson,它底层会自动发现集群拓扑,但前提是每个节点的
notify-keyspace-events ex都已生效;否则RScheduledExecutorService或监听器会静默失效 - 检查方式很简单:连上任意节点,执行
CONFIG GET notify-keyspace-events,确认返回值含ex,且所有 master 节点都返回一致结果
监听到过期事件后能做什么?
它只是个信号,不是事务钩子——Redis 不保证“发布前 value 还在”“发布和删除原子”,所以别指望靠它做最终一致性校验。
- 适合场景:记录审计日志、触发异步清理(如删关联缓存、发 MQ 通知)、降级开关重置
- 不适合场景:扣减库存、支付回滚、状态机跃迁——这些必须走业务主流程,过期事件只能作辅助补偿
- 性能提醒:如果每秒有几千 key 过期,Pub/Sub 频道可能积压;建议监听端做批处理 + 限流,别一个事件起一个线程
过期事件机制本身轻量,但“以为它可靠”才是最大陷阱。它不重试、不保证顺序、不兜底 value,用之前先想清楚:这个动作丢了,系统是否还能自愈?










