Redis订阅键过期事件时如何获取过期键对应的存储值
问题根源
Redis原生键过期通知的机制决定了:过期事件推送时,对应的键已经被删除,通知消息体只会携带过期键的键名,不会附带键值。此时你在回调里直接对原键执行GET操作只会得到nil,这不是代码逻辑错误,是Redis本身的设计限制。
可落地的解决方案
方案1:影子键映射(生产环境首选)
实现成本最低,对现有业务侵入小,过期实时性和原生通知一致:
- 写入业务键时,同步写入一个对应的影子键:键名加固定前缀做映射(比如原键是
order:1001,影子键为shadow:order:1001),影子键的值存你需要在过期时获取的业务内容,TTL比原业务键长3~5秒(预留足够的回调处理延迟,避免网络波动导致读不到) - 监听到原键过期事件时,直接拼接影子键名读取对应值,处理完业务逻辑后主动删除影子键即可
对应代码修改示例:
# 初始化配置逻辑不变 r.config_set('notify-keyspace-events', 'Ex') pubsub.psubscribe(**{"__keyevent@0__:expired": event_handler}) pubsub.run_in_thread(sleep_time=0.01) # 写入业务数据时同步写影子键 def set_biz_key(key, value, ttl): r.set(key, value, ex=ttl) # 影子键TTL比原键多预留3秒缓冲 r.set(f"shadow:{key}", value, ex=ttl + 3) def event_handler(msg): try: key = msg["data"].decode("utf-8") shadow_key = f"shadow:{key}" value = r.get(shadow_key) if value: # 这里就能拿到过期键关联的值,做后续业务处理 real_value = value.decode("utf-8") print(f"过期键:{key},对应值:{real_value}") # 处理完主动删除影子键,避免冗余 r.delete(shadow_key) except Exception as e: print(e)
注意TTL的缓冲时间要大于你服务订阅Redis通知的最大延迟,避免影子键提前过期。
方案2:逻辑过期替代Redis原生过期
不依赖Redis的自动过期能力,自行维护过期逻辑:
- 写入键时不设置Redis层面的TTL,把业务值和过期时间戳打包存储,比如值结构为
{"content":"业务数据","expire_ts":1723046400} - 启动后台定时任务,分批扫描所有业务键,判断到键的过期时间戳小于当前时间时,直接取出值完成业务处理,再删除对应键
这个方案适合数据规模不大、对过期触发延迟容忍度高的场景,缺点是轮询存在秒级到分钟级的延迟,数据量大时要控制扫描批次和频率,避免阻塞Redis实例。
方案3:自定义Redis模块拦截删除事件
如果有Redis运维能力,可以开发自定义Redis模块,拦截键过期删除的流程,在键被实际删除前把键名和值一起推送到自定义频道,再由你的服务订阅这个频道拿值。这个方案灵活度最高,但运维和开发成本高,非特殊需求不推荐使用。
避坑提示:不要在回调里直接读取原键的值,过期事件触发时原键已经被Redis删除,所有读操作都会返回空。
内容的提问来源于stack exchange,提问作者Tlaloc-ES
相关产品推荐
相关产品推荐

