红色之缘聊聊Redis集群里那些缓存清理的技巧和坑,删缓存其实没那么简单
- 问答
- 2026-01-14 13:55:27
- 4
根据网络上多位技术博主,如“程序员囧辉”、“石杉的架构笔记”等关于Redis缓存清理的常见问题讨论综合整理)
咱们今天就来聊聊这个Redis,这家伙因为logo是红色的,被大家戏称为“红哥”,用“红哥”来做缓存,现在几乎是每个稍微大点系统的标配了,但你别以为用了缓存就高枕无忧了,尤其是当你想要更新或者删除缓存里的数据时,这里面的门道和坑可多了去了,搞不好就会引来一场线上事故,删缓存,真的不是简单地执行一个DEL命令那么单纯。
最经典的坑,莫过于“先更新数据库,还是先删除缓存”这个千古难题,你可能觉得这有啥好争的,顺序换了能有多大区别?嘿,区别大了去了。
先删缓存,再更新数据库
想象一下这个流程:用户要修改自己的昵称。
- 你先把缓存里旧的昵称数据给删了。
- 然后你去数据库里更新这个新昵称。
听着挺顺对吧?但坑就在这两步操作之间不是“原子”的,不是一瞬间完成的,如果在你刚删完缓存,但数据库还没更新完的这个极短时间里,突然又来了一个请求要读取这个用户的昵称,这时候会发生什么?缓存里是空的(被你删了),它就会乖乖地去数据库里查,但此刻数据库里还是那个旧的昵称!这个旧的昵称又被这个请求塞回到缓存里了,等你数据库更新完,缓存里存的却还是旧数据,之后所有来读数据的请求,读到的都是错的旧昵称,除非这个缓存再次过期或者被删除,这就是所谓的“脏数据”又回来了。
先更新数据库,再删除缓存
那反过来试试看:

- 先更新数据库里的昵称为新值。
- 再删除缓存里的旧数据。
这个方案看起来比第一个好点,因为即使第二步删除缓存失败了,我们最多就是下次读取时拿到旧数据(缓存穿透到数据库),但数据库里的数据至少是新的,我们还有机会重试删除缓存,它也不是完全没风险,同样是在极端的并发场景下,比如在你更新完数据库,但还没来得及删缓存的时候,恰好有一个读请求过来,把数据库里还没被更新的旧数据又加载到缓存里了,然后你的删除操作才执行,但这时候缓存里已经是刚塞进去的旧数据了,结果还是造成了脏数据残留,这种情况发生的概率相对较低,因为数据库的写操作通常比读操作慢,读请求在写操作之后完成并抢在删除前塞回缓存的窗口期非常短。
那有没有更靠谱的办法呢?这就是我们要聊的技巧了。
延时双删
这是一个实践中常用的“笨”办法,但往往很有效,就是在先更新数据库、再删除缓存的基础上,再加一个步骤:在更新完数据库后,隔那么一小段时间(比如几百毫秒到1秒),再删一次缓存,这么做的目的,就是为了清除掉在“更新数据库”到“第一次删除缓存”这个极短时间窗口内,可能被其他读请求塞进缓存的脏数据,这个延迟时间需要根据你自身业务的读写耗时来估算,目的是确保第一次删除前可能发生的并发读操作已经完成并把脏数据写回了缓存,然后我这第二波删除就来个“清场”。

异步消息队列
对于强一致性要求非常高的场景,或者不想在业务代码里写Thread.sleep这种不优雅的延时,可以用消息队列,流程变成:业务线程成功更新数据库后,发一条消息到消息队列(比如Kafka、RocketMQ),消息内容就是“要删除哪个缓存key”,然后有一个专门的消费者来消费这个消息,负责执行删除缓存的操作,如果删除失败了,消息队列本身的重试机制可以保证最终一定会成功删除,这样就把更新数据库和删除缓存这两个操作解耦了,避免了在业务线程中同步等待删除缓存带来的性能问题和复杂性。
集群下的特殊坑:主从同步延迟
上面说的都是在单机Redis的情况,如果你的“红哥”是集群模式,特别是用了主从复制,那又有一个新坑等着你:主从同步延迟,你执行写操作(更新数据库和删除缓存)是在主库上完成的,但紧接着的一个读请求可能会被负载均衡路由到从库上,如果此时主库的数据还没同步到从库,那么这个读请求就会从从库里读到旧数据,并且把这个旧数据又设置到缓存里去了,导致脏数据再现。
对于这种情况,一种比较“重”的解决方案是,在写操作完成后,强制后续的关键读请求在一定时间内都走主库读取,避开从库的延迟,但这会增加主库的压力,这又是一个需要权衡的地方。
和“红哥”打交道,删缓存这个动作看似简单,背后却牵连着数据库的一致性、系统的高并发和集群架构的特性,没有一劳永逸的银弹,最好的办法就是理解这些技巧和坑的原理,然后根据自己业务的容忍度、并发量和一致性要求,选择一个最适合的策略,可能对于大部分场景,“先更新数据库,再删除缓存”加上监控告警(发现删除失败及时告警)就足够了;对于极致要求的场景,可能就需要引入消息队列等更复杂的方案,关键是要意识到,这事儿不简单,得用心对待。
本文由邝冷亦于2026-01-14发表在笙亿网络策划,如有疑问,请联系我们。
本文链接:http://waw.haoid.cn/wenda/80585.html
