news 2026/5/17 4:31:31

Mysql和Redis双写不一致问题怎么解决?—— 别让 300ms 的延迟毁了你的数据

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Mysql和Redis双写不一致问题怎么解决?—— 别让 300ms 的延迟毁了你的数据

在后端面试中,只要简历上写了 Redis,这道题是100% 必问的:

“怎么保证缓存和数据库的数据一致性?”

因为在生产环境中,细节才是魔鬼。今天我们就从“青铜”到“王者”,拆解 4 种方案,并揭秘3 个连老鸟都容易踩的致命误区


❌ 方案1:先删缓存,再更新 DB(青铜)

这是很多新手的直觉反应:“先把旧缓存清了,再改数据库,下次读的时候不就读到新的了吗?”

错!这是个巨大的坑。只要你的系统有一点并发量,这个方案就是个 Bug 制造机。

翻车现场还原

看看下面这个时序图,你就知道为什么不能用了:

结局:MySQL 是新的,Redis 是旧的。而且因为 Redis 里有数据,后续请求都不会去查库,这个脏数据会一直存在,直到过期。


✅ 方案2:先更新 DB,再删缓存(黄金 - Cache Aside)

这是业界最常用的Cache Aside Pattern

逻辑

  1. 先更新 MySQL。

  2. 更新成功后,删除 Redis 缓存。

为什么这个比方案 1 好?
虽然理论上它也存在并发问题,但在实际生产中,这种情况发生的概率极低
因为“数据库写操作”通常比“缓存读写”慢得多。要触发 Bug,需要读请求在“写请求更新完 DB 但还没删缓存”的极短微秒级时间窗口内完成整个操作,这需要极其巧合的时间差。

适用场景:90% 的读多写少业务。


✅ 方案3:延迟双删(钻石 - 高并发优化)

如果你无法容忍方案 2 中那“万分之一”的概率,或者你的数据库主从延迟比较大,延迟双删是性价比最高的方案。

核心逻辑
先删缓存 -> 更新 DB -> 休眠 N 毫秒 -> 再删缓存

为什么要删两次?

  • 第一次删:为了腾地儿。

  • 第二次删:为了把“在更新 DB 期间,其他读请求可能写入的脏数据”给清理掉。

生产级代码实现

这里有个细节:休眠时间怎么定?

经验公式:Sleep 时间 ≈ (主从同步延迟 + 读请求平均耗时) * 1.5
一般设置为 300ms - 500ms。

publicvoidupdateUser(Useruser) { Stringkey ="user:"+ user.getId(); // 1. 第一次删除 redisTemplate.delete(key); // 2. 更新数据库 userMapper.updateById(user); // 3. 异步延迟第二次删除 (防止阻塞主线程) CompletableFuture.runAsync(() -> { try{ Thread.sleep(500);// 关键:给主从同步留出时间 redisTemplate.delete(key); }catch(InterruptedExceptione) { e.printStackTrace(); } }, executor);}


方案4:Canal + MQ(王者 - 金融级一致性)

如果你做的是金融、支付业务,连“应用层删缓存失败”都不能容忍,那就必须把应用层解耦。

原理:应用层只管写 MySQL,由 Canal 伪装成 MySQL Slave 监听 Binlog,投递到 MQ,消费者负责删缓存并自动重试

深度思考:金融业务的“双标策略”

很多人问:“Canal 也有延迟,金融业务怎么能忍?”
这里有一个认知误区。真实的金融架构采用了双标策略

结论:Canal + MQ 保证的是“展示链路”的数据最终一定是正确的,不会出现“删缓存失败”导致的永久脏数据。


避坑指南:3 个让你背 P0 事故的“隐形误区”

方案选对了就稳了吗?下面这三个坑,踩中一个就是生产事故。

误区 1:在事务(@Transactional)里面删缓存

这是 Spring 开发中最容易犯的低级错误!

❌ 错误代码

@TransactionalpublicvoidupdateUser(Useruser) { userMapper.updateById(user); // MySQL 还没 Commit! redisTemplate.delete("user:1"); // Redis 先删了 // ... 方法结束,事务才 Commit}

后果:Redis 删了,DB 还没提交。读请求进来读到旧值写回 Redis,随后事务提交。Redis 永远是旧值。

✅ 修正方案:利用事务同步管理器

publicvoidupdateUser(Useruser) { userMapper.updateById(user); // 注册一个回调,确保事务提交成功后再删缓存 TransactionSynchronizationManager.registerSynchronization( newTransactionSynchronization() { @Override publicvoidafterCommit() { redisTemplate.delete("user:"+ user.getId()); } } );}

误区 2:针对“热点 Key”直接删缓存

对于微博热搜、秒杀库存这种Top Hot Key千万不能直接删!
删掉缓存的瞬间,几万个请求会直接击穿到 MySQL,数据库瞬间 CPU 100% 宕机。

✅ 修正
针对超热点 Key,使用双写更新(允许短暂不一致)或分布式锁(只放一个线程回写)。

误区 3:迷信延迟双删的 Sleep 时间

Sleep(500ms) 只是一个经验值。如果网络抖动,主从延迟飙升到 1 秒,你睡 500ms 也是白搭。
记住:延迟双删只能降低不一致概率,无法根除


总结一张表


互动话题

你在生产环境中用的是哪种方案?有没有踩过“事务内删缓存”的坑?欢迎在评论区聊聊你的“血泪史”!

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

大模型学习路线图:从数学基础到AGI应用全解析_AI大模型学习路线(2025最新)神仙级大模型教程分享

文章提供了大模型学习的七个阶段路线图:从数学编程基础、机器学习、深度学习到NLP和大模型应用,最后是进阶学习。文章介绍了大模型行业前景、薪资水平和学习资源,强调掌握AI技术的重要性,并提供免费学习资料包,帮助小白…

作者头像 李华
网站建设 2026/5/11 10:37:28

Open-AutoGLM API接口实战手册(从入门到高并发优化)

第一章:Open-AutoGLM API接口实战手册(从入门到高并发优化)快速接入API服务 要开始使用Open-AutoGLM API,首先需获取有效的认证密钥。注册开发者账户后,在控制台生成API Key,并通过HTTP Header传递进行身份…

作者头像 李华
网站建设 2026/5/12 7:32:21

卡帕西2025大模型回顾解读:AI的脑、手、场进化与2026前瞻

最近AI圈有一篇重磅文章——安德烈卡帕西(Andrej Karpathy)的2025年大模型回顾。 卡帕西的行业影响力毋庸置疑:2016年加入OpenAI,是早期最核心的研究员之一;2017-2022年担任特斯拉人工智能高级总监。他的观点横跨软件端…

作者头像 李华
网站建设 2026/5/16 5:52:33

告别文本低效协作:潜在空间多智能体系统(LatentMAS)全面解析

LatentMAS是一种革命性的多智能体协作框架,通过在潜在空间中直接共享智能体的内部"思维状态",而非传统文本交流,实现了高效、无损的信息交换。这种"心灵感应"式协作基于推理表达力、通信保真度和协作复杂度三大支柱&…

作者头像 李华
网站建设 2026/5/15 7:34:02

在 GeckoCIRCUITS 上开发新工具模块的方法

简介 最近在使用开源电力电子仿真软件 GeckoCIRCUITS 进行仿真时,我想使用离散时间控制器,但是 GeckoCIRCUITS 中没有这个模块,需要使用工具栏中自带的 JAVA 模块添加算法代码。但是我想把类似这样常用的模块固定在工具栏里,方便使…

作者头像 李华
网站建设 2026/5/16 10:15:49

9个降aigc工具推荐!继续教育学生高效避坑指南

9个降aigc工具推荐!继续教育学生高效避坑指南 AI降重工具,让论文更自然、更安全 在继续教育的学习过程中,论文写作是不可避免的一环。然而,随着人工智能技术的广泛应用,许多学生发现自己的论文出现了明显的“AI痕迹”…

作者头像 李华