news 2026/8/27 20:39:35

都说延迟双删能保证缓存一致性,为什么高并发下它根本不靠谱?

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
都说延迟双删能保证缓存一致性,为什么高并发下它根本不靠谱?

如何保证 Redis 和 MySQL 的数据一致性?标准八股文的答案:先删缓存,再更新数据库,然后延迟一段时间再删一次缓存。

听起来逻辑挺对的,步骤也不复杂。但在高并发的生产环境里,这个方案简直就是坑。

要删两次是因为在步骤 1 删完缓存和步骤 2 更新完数据库之间,有一个时间窗口。在这个窗口内,另一个读请求可能查到了数据库的旧值,然后把旧值又写回了缓存。第二次删除就是为了把这个被回填的脏缓存给清掉。

逻辑上好像没毛病,但问题出在细节里。

延迟时间不好定

休眠 N 毫秒,这个 N 又该定多少呢?

如果设得太短,还没等读请求把旧值写回缓存,第二次删除就执行了,白忙活

如果设得太长,在你休眠的这段时间里,所有读请求都读到脏缓存,脏缓存永不刷新

通常建议 N 设成读请求的耗时 + 几百毫秒,但读请求的耗时在高并发下波动很大,平时 50ms 高峰期可能 500ms。设个固定值,要么平时浪费,要么高峰期覆盖不到。

更要命的是,这个延迟时间一旦定了,就是硬编码在代码里的。数据库换了、网络抖了、机器扩容了,全得重新评估。

线程 sleep 阻塞业务

最直接简单的实现就是在业务线程里Thread.sleep(500)

在高并发场景下,业务线程是非常宝贵的资源,一个写请求进来,处理完数据库更新,然后线程就在那傻等 500ms。如果每秒有 1000 个写请求,就有 1000 个线程同时在 sleep,线程池很快就被占满,后面的请求全部排队甚至超时。

要不我们用异步?用 MQ 发个延迟消息来做第二次删除?

可以是可以,但这又引入了 MQ 的复杂度,MQ 消费失败了怎么办?消费延迟了怎么办?为了一个缓存一致性,引入了一整套消息中间件的运维成本。

第二次删除也可能失败

网络又不是 100% 可靠的,第二次删除 Redis 的操作可能因为网络抖动、Redis 短暂不可用、超时等原因失败,失败了怎么办?加重试?重试几次?重试间隔多久?

每一层兜底方案都会引入新的复杂度,最后你会发现为了保证一个删除操作的可靠性,你写了一大坨重试 + 补偿逻辑。

并发写的坑

延迟双删只考虑了"一个写请求 + 一个读请求"的并发场景,两个写请求并发到来时会发生什么

假设有两个写请求,A 和 B,按照时间顺序:

这个场景下没什么问题。但如果时序稍微变一下:

但请求 B 是后发起的,业务语义上最终值应该是 Y,而数据库里存的却是 X。

这已经不是缓存一致性的问题了,是数据库本身的并发写顺序问题,延迟双删根本管不了这个。

推荐的方案

旁路缓存策略,核心就两句话:先读缓存,缓存没有就读数据库,读完写入缓存;先更新数据库,再删除缓存。

注意:是先更新库再删缓存,不是先删缓存再更新库。

为什么这个顺序更好?因为数据库更新和缓存删除之间的时间窗口通常极短(微秒级),在这个窗口内恰好有读请求命中旧缓存的概率很低。而先删缓存再更新库的时间窗口(数据库写入耗时)要大得多。

如果还想要更强的一致性保证,可以参考下面的方案:

  • Cache Aside(先更新库再删缓存),适用于大多数业务场景

  • Cache Aside+TTL 兜底,适用于允许短暂不一致的场景

  • Canal 监听 binlog + MQ 异步更新,适用于高一致性要求的场景

  • 写请求加分布式锁串行化,适用于并发写冲突严重的场景

延迟双删不是说完全不能用,在低并发、对一致性要求不高的场景下,它确实简单好理解。但一旦并发上来了,它的每一个假设都可能被打破。

如果对一致性的要求高到需要延迟双删,那说明你应该用更靠谱的方案了,如果你对一致性的要求没那么高,那单纯的先更新库再删缓存就够了,延迟双删刚好卡在一个尴尬的位置上。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/27 20:39:30

免费小说推荐平台科普 挑选靠谱平台的实用技巧

免费小说平台行业发展现状与趋势随着数字阅读习惯的普及,免费小说平台已经成为国民日常休闲娱乐的重要载体。过去十年间,行业经历了从盗版横行到正版化规范运营的转型,用户的选择标准也从单纯的“免费”逐渐转向“正版、安全、体验好”。当前…

作者头像 李华
网站建设 2026/8/27 20:38:52

PaperTodo

链接:https://pan.quark.cn/s/3e5fb2cdf0d6PaperTodo 是一个极简的 Windows 桌面便签工具,只用 WPF 原生实现,没有主窗口、没有账号、没有管理器。软件特色多张独立纸片 — 每张纸是一个独立窗口。一个应用,两种纸片: …

作者头像 李华
网站建设 2026/8/27 20:36:24

STM32 HAL库实战:DS18B20测温、数码管显示与EEPROM存储综合应用

1. 项目概述与备赛思路 最近在准备蓝桥杯嵌入式国赛,手头正好有块STM32G4的开发板,就想着把往届真题里那些经典的外设模块都过一遍。这届比赛,HAL库是绕不开的,官方推荐用它,虽然上手时觉得抽象层有点厚,不…

作者头像 李华
网站建设 2026/8/27 20:32:45

关于力扣第239题的题解【滑动窗口最大值】

摘要:本文详细解析力扣第 239 题「滑动窗口最大值」的解法,核心思路是采用滑动窗口 单调队列。遍历数组时,我们维护一个双端队列(Deque),并始终保持队首为当前窗口的最大值。每当加入新元素前,…

作者头像 李华
网站建设 2026/8/27 20:31:04

【Unity小白学习日记2】数据结构必考知识点 | 冒泡、选择、插入排序

1、冒泡排序核心思路描述重复遍历数组,相邻两个元素两两比较,前大于后就交换。每一轮会把未排序区间最大元素 “冒泡” 到末尾。增加交换标记优化,如果一轮没有发生交换,说明数组已经有序,可以直接结束。关键 C# 代码/…

作者头像 李华