Redis键过期事件默认关闭,需配置notify-keyspace-events Ex并持久化;订阅须用PSUBSCRIBE __keyevent@*__:expired匹配所有库,且必须连接主节点;事件无ACK、不保证顺序与可靠性。
Redis 默认完全关闭键空间事件,notify-keyspace-events 值为空字符串(不是 "",是真正空)——这意味着哪怕你执行了 SET key val EX 10,过期那一刻也不会产生任何通知。这不是延迟或丢包,是压根没开闸。
实操建议:
redis-cli CONFIG GET notify-keyspace-events 查当前值,确认返回的是 ["notify-keyspace-events","Ex"],而不是空或 ""
notify-keyspace-events Ex(无引号、大小写敏感),写成 "Ex" 或 ex 都会被 Redis 静默忽略CONFIG SET notify-keyspace-events Ex 仅本次有效,重启即失效;务必同步写入 redis.conf 并重启,或用 CONFIG REWRITE 持久化Redis 只向固定格式的频道发布过期事件:__keyevent@N__:expired,其中 N 是该 key 所在的数据库编号(比如你在 SELECT 2 后执行 SETEX mykey 5 "v",事件就发往 __keyevent@2__:expired)。
常见问题表现:
__keyevent@0__:expired —— 收不到SUBSCRIBE __keyevent@0__:expired,但 key 在 db1 过期 —— 收不到redis-cli --csv SUBSCRIBE __keyevent@0__:expired,shell 把 @ 当特殊字符解析,实际订阅失败 —— 加单引号:redis-cli --csv SUBSCRIBE '__keyevent@0__:expired'
__keyspace@0__:expired(前缀是 keyspace)—— 这是“键空间”通知,只发操作类型(如 set、del),不发 expired 事件如果你不确定 key 会在哪个 db 过期,或想统一收所有库的过期事件,必须用 PSUBSCRIBE 订阅模式,而不是 SUBSCRIBE。
正确做法:
PSUBSCRIBE __keyevent@*__:expired,星号匹配任意 db 编号SUBSCRIBE 只能精确匹配一个频道名,无法跨 db 监听RedisMessageListenerContainer 支持自动重连,但需确认 setSubscriptionExecutor 和线程池配置合理,否则大量事件可能堆积或丢失键过期事件只由主节点生成并发布,从节点不发事件。即使你的客户端连的是从节点(比如读写分离场景),只要没显式切换到主连接做订阅,就收不到任何 expired 消息。
关键点:
__keyevent@N__:expired,需分别订阅各主节点对应频道readMode = ReadMode.SLAVE,Pub/Sub 默认也可能走错节点最常被忽略的一点:事件通知是 fire-and-forget 的,没有 ACK,没有重试,也没有顺序保证。客户端掉线期间发生的过期,永远丢失。别把它当可靠消息队列用。