用Redis过期机制来搞多线程缓存管理,感觉还能这样玩?
- 问答
- 2026-01-10 01:40:48
- 5
那天在网上瞎逛,看到一个程序员老哥分享了他团队里一个“鸡飞狗跳”的故事,然后他们用Redis那个过期时间的特性,想出了一个特别巧妙的办法来搞定多线程环境下的缓存问题,我看完一拍大腿,觉得这思路真是绝了,原来还能这么玩!
事情是这样的,他们有个服务,访问量贼大,为了减轻数据库的压力,用了本地缓存(就是存在应用服务器自己内存里)加Redis分布式缓存这么两层结构,一般的流程是:来了一个请求,先看本地缓存有没有,有就直接返回;没有的话,就去Redis里查,如果Redis里有,就把它拿回来塞到本地缓存里,再返回;要是Redis里也没有,那没辙了,只能硬着头皮去查数据库,查出结果后,同时写到Redis和本地缓存里,下次再用就快了。
这本来是个很常见的套路,但他们遇到了一个头疼的问题:缓存更新,比方说,后台管理员修改了某个商品的信息,这个商品的数据在好多台应用服务器的本地缓存里都存着,你怎么能让所有这些服务器上的旧缓存立刻失效呢?他们最开始的做法是,发一个广播消息(比如用MQ消息队列),告诉所有服务器:“喂,某个商品数据变了,你们赶紧把本地缓存里对应的东西删掉!” 这听起来挺美,但实际跑起来问题一大堆。

广播消息它不一定百分百可靠啊,万一某台服务器当时网络抽风了一下,没收到这个消息,那它就会一直用着脏数据,用户看到的就是过时的信息,除非等到本地缓存自己到期,这增加了系统的复杂性,得额外维护一套消息发布和订阅的机制,万一消息队列本身出点毛病,整个缓存更新就瘫痪了。
就在他们为这事儿头疼的时候,团队里有个小伙儿提出了一个想法:“咱们能不能别主动去通知别人‘缓存失效了’,而是让它们自己主动发现‘缓存可能失效了’呢?”
核心玩法登场:利用Redis的过期事件和本地缓存的短时间失效。

他们调整后的方案是这样的:
-
写缓存与设置过期时间: 当应用服务器从数据库查出数据,往Redis里写的时候,除了设置一个比较长的基本过期时间(比如1小时),还会额外设置一个很短的、用来“通知”的过期时间(比如2分钟)。 这个短的过期时间,他们用一个特殊的键(Key)来存,比如原缓存键是
product:123,那么这个通知键就叫product:123:notify,这个:notify键对应的值无所谓,甚至可以存个空字符串,关键就在于它的过期时间很短。 -
监听Redis的过期事件: 他们的每一台应用服务器,都启动一个监听程序,专门订阅Redis的“键空间通知”(Key-space Notifications),特别是关注
expired事件(就是某个键过期时产生的事件)。
-
神奇的联动效果: 好戏开始了,假设所有服务器本地缓存的有效时间都设成了5分钟。
- 正常情况下,一个请求过来,本地缓存命中,皆大欢喜。
- 当后台更新了商品信息时,程序员不是去发广播消息,而是直接去Redis那里,手动把那个短短的
product:123:notify键给删掉(或者设置它的值为空并重新设置2分钟的过期时间,效果类似)。 - 由于这个
:notify键被手动删除了(或者很快到期),大约2分钟后,Redis就会自动产生一个expired事件。 - 所有监听这个事件的应用服务器,都会收到一条消息:“注意!
product:123:notify这个键过期了!” - 服务器收到这个消息后,程序一看,键名是
product:123:notify,它就能解析出真正需要关注的是product:123这个数据键,它就会主动把自己本地缓存里关于product:123的数据清除掉。
-
后续请求的自动更新: 当下一个请求再来访问商品123的信息时,发现本地缓存没有了(刚刚被清除了),就会走完整的流程:查Redis -> 如果没有(或者Redis里虽然有但可能也是旧的,不过管理员更新后通常会刷新Redis主键),就查数据库 -> 拿到最新数据后,重新写入Redis和本地缓存,这个流程也会重新创建那个短短的
product:123:notify键,为下一次“通知”做准备。
这么玩的好处简直太多了:
- 解耦了: 再也不需要那个脆弱的广播消息系统了,缓存更新方(管理员操作的后台)只需要关心Redis,它只管去删那个小小的
:notify键,剩下的事情Redis的过期机制会帮你搞定,发送方和接收方完全不需要知道对方的存在。 - 更可靠: Redis的过期事件是内置机制,只要Redis本身高可用,这个“通知”的可靠性就非常高,比自己维护一个消息队列集群简单、稳定多了。
- 最终一致性保证得很好: 虽然从删除通知键到所有本地缓存失效,有最多2分钟(这个时间可配置)的延迟,但这在绝大多数业务场景下都是完全可以接受的,达到了最终一致性的目标,总比某台服务器永远收不到消息,一直用脏数据要强一万倍。
- 容错性好: 即使某台服务器当时宕机了,没收到过期事件,也没关系,等它重启后,它的本地缓存是空的,第一个请求过来还是会从Redis/数据库获取最新数据,它本地缓存的有效期(5分钟)比通知键的(2分钟)长,保证了在正常运行时一定能收到通知,而不会因为本地缓存未过期而忽略更新。
这个设计最精妙的地方就在于,它没有自己去造一个通知轮子,而是巧妙地“借力打力”,利用了Redis本身一个非常成熟的特性(键过期事件),把一个复杂的分布式协同问题,简化成了一个简单的设置和监听问题,这哥们儿说,自从用了这个方案,关于缓存不一致的客服投诉几乎降到了零,而且代码比之前用消息队列的时候清爽多了。
看完这个分享,我真是有种豁然开朗的感觉,很多时候,我们总想着用更复杂、更重型的技术去解决问题,却忽略了现有工具的一些特性已经足够优雅和强大,这种“四两拨千斤”的玩法,确实值得好好琢磨和学习。
本文由盈壮于2026-01-10发表在笙亿网络策划,如有疑问,请联系我们。
本文链接:https://www.haoid.cn/wenda/77777.html
