news 2026/6/8 20:13:11

阿里二面:如何保证 Redis 和 MySQL 的数据一致性?还在背“延时双删”的Sleep玄学?教你高性能 + 高可靠的方案

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
阿里二面:如何保证 Redis 和 MySQL 的数据一致性?还在背“延时双删”的Sleep玄学?教你高性能 + 高可靠的方案

写在开头:

我在公众号发了一篇《先更新数据库还是先删除缓存?》的文章,里面和大家聊到了 binlog 兜底方案的实现思路。

文章发出后,有细心的小伙伴后台私信我,指出方案里漏掉了先删除缓存这个关键操作。

这个反馈非常精准!缓存操作的顺序直接决定了用户是否能立刻读到新数据。 所以今天,我特意整理了优化后的最终完整版方案,补全了“先删缓存”的逻辑,并增加了一个轻量级的降级方案,希望能给被这个问题困扰的朋友一个生产级的标准参考。

前天跟一个老同学喝茶,他最近在给阿里某 P7 岗位招人。 他说面了十几个“五年经验”的 Java 开发,一问到“如何保证 Redis 和 MySQL 的数据一致性”,90% 的人都像背书一样回答: “用延时双删策略!先删缓存,再更数据库,休眠 1 秒,再删缓存。”

老同学问:“为什么要休眠 1 秒?这 1 秒是怎么算出来的?如果数据库主从同步延迟了 1.5 秒,你这第 1 秒删了个寂寞?难道你要休眠 2 秒?那接口响应时间(RT)还要不要了?” 对方直接哑火。

说实话,“延时双删”在低并发的小系统里能用,但在大厂高并发场景下,它就是个“缝合怪”方案:既拖慢了性能(阻塞 Sleep),又无法保证 100% 的一致性(不可靠的 Sleep 时间)。

今天咱们就撕开这层遮羞布,看看大厂真正的“高可靠最终一致性”是怎么做的。

一、 为什么 “延时双删” 在高并发场景下不适用?

为了解决“更新数据库期间,旧数据被回填到缓存”的问题,有人发明了“延时双删”:先删缓存 -> Update DB -> Sleep(N) -> 再删缓存

这个方案在生产环境有两个致命硬伤:

硬伤 1:Sleep 多少秒是个玄学

你是 Sleep 500ms 还是 1s?这个时间必须大于“主从同步延迟 + 业务查询耗时”。 但在大促流量洪峰下,MySQL 主从延迟可能瞬间飙升到 3 秒甚至更久。你怎么保证你的 Sleep 时间一定够?只要 Sleep 结束时从库数据还没同步过来,别的线程读到的依然是旧数据,并再次回填到缓存。

硬伤 2:吞吐量自杀

你可是高并发系统啊!为了数据一致性,强行让业务线程Thread.sleep几百毫秒? 这意味着你的接口响应时间(RT)直接增加了几百毫秒,Tomcat 线程池瞬间被占满,吞吐量(QPS)腰斩。在秒杀场景下,这种代码就是给 CPU 递刀子。

二、 生产级的标准答案:Cache Aside + Binlog 异步兜底

在大厂的核心链路,我们追求的是“低延迟的最终一致性”。 既然“第二次删除”是为了防止脏数据回填,那为什么非要在业务线程里傻等?把这个“等待”和“删除”的动作剥离出来,交给异步组件去做,才是正解。

真正的架构方案:

  1. App 侧(保障实时性):先删除缓存,再更新数据库。
    • 注意:保留“先删缓存”,是为了让后续的读请求直接打到 DB 拿最新数据,防止用户看到旧值。
    • 防坑指南:对于热点 Key,删除缓存后可能会引发“缓存击穿”导致 DB 压力骤增。建议配合分布式锁或逻辑过期策略来保护 DB,而不是在业务代码里搞复杂的“回写旧值”。
  1. MySQL 侧:数据变更产生 Binlog。
  2. 中间件侧(保障最终一致性):Canal 监听 Binlog -> 投递到 MQ -> 消费者收到消息。
  3. Consumer 侧(兜底清理):消费者收到消息后,执行第二次删除 Redis。

这套方案是如何降维打击“双删”的?

  • 解耦(彻底去掉了 Sleep)

    业务线程执行完Update DB直接返回成功,不需要留下来陪跑。接口性能拉满。

  • 可靠性(MQ 重试机制)

    如果 Redis 挂了,或者删除失败,MQ 的 ACK 机制会保证消息不断重试,直到删除成功为止。而“双删”方案里,如果第二次删除失败了,那缓存里永远是脏数据。

  • 解决主从延迟(自适应)

    MQ 的消息传输本身就有天然的“延时”。如果担心主从延迟极高,可以在 Consumer 端配置“消费延迟”(比如 RocketMQ 的 Level 3),或者让 Consumer 查不到数据再删。这比在业务代码里硬写Sleep(1000)优雅一万倍。

  • 补充兜底策略

    针对 Canal 宕机、MQ 消息堆积等异常场景,务必给缓存加上过期时间(TTL)。即使异步删除彻底失败,脏数据也会在 TTL 到期后自动失效,这是最后一道防线。

三、 如果面试官问“Binlog 方案太重了怎么办?”

有些面试官会杠:“引入 Canal 和 MQ,系统复杂度太高了,中小公司玩不起怎么办?”

这时候你要甩出“轻量级线程池延迟删除”方案:如果不上一整套 Binlog 中间件,可以在 Update DB 成功后,往当前服务的“延迟消息队列”(可以是基于内存的 DelayQueue,或者 RocketMQ 的延迟消息)扔一个任务。 任务内容:{key: "item_100", delay: 1000ms, retry: 3}。 由后台线程池去执行这个“第二次删除”。

注意事项:延迟队列虽然可以基于内存(DelayQueue),但在生产环境强烈建议使用RocketMQ/Kafka 延迟消息Redisson 延迟队列。 因为基于内存的队列一旦服务重启,任务就丢了,会导致缓存永远删不掉。

四、 面试标准答案模板(建议背诵)

下次面试官再问“一致性”,别上来就背双删,试试这个高阶回答:

  1. 破题(否定野路子)

    “面试官,在生产环境中,我们一般不使用‘延时双删’。因为它强依赖Sleep时间,不仅严重拖累接口吞吐量,而且在数据库主从延迟抖动时,依然无法保证一致性。”

  2. 抛出方案(亮肌肉)

    “我们采用的是‘Cache Aside Pattern 的增强版’,也就是先删缓存 + Update DB + 基于 Binlog 的异步兜底删除。 业务线程先删缓存保证数据的实时性,然后更新 DB。 至于‘防止脏数据回填’的逻辑,我们剥离给 Canal + MQ 去做。Canal 监听 Binlog 变动投递给 MQ,消费者负责进行‘第二次删除’。”

  3. 升华价值(讲收益)

    “这样做有两个核心优势: 第一是高性能,业务线程完全不需要阻塞等待,响应极快; 第二是高可靠,利用 MQ 的重试机制,即使 Redis 抖动或删除失败,也能通过重试保证缓存最终一定被清理,真正实现了‘最终一致性’。当然,该方案的核心是用架构复杂度换取高性能,在中小系统如果不具备 Canal 条件,我们也会降级为‘轻量级延迟消息’方案,兼顾不同业务体量的需求。”

写在最后

架构设计的本质是权衡(Trade-off)。 “延时双删”是用性能换一致性(而且还换得不彻底)。 “Binlog 异步删”是用架构复杂度(引入 MQ)换取了高性能 + 高可靠。 作为 P7 架构师,我们永远选择后者。

https://mp.weixin.qq.com/s/2CcZZctJ2_kcokhMnNhY8A

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

基于 S7 - 1200 和博图 15.1 的三层立体车库 PLC 设计

三层立体车库plc s7-1200 博图15.1 1、设置启动、停止按钮,且设置指示灯显示车库的开关状态; 2、7个车位的车俩可以自由存取,且车库可以实现自动存取(存取选择最优路径); 3、每个车位均有电机控制&#…

作者头像 李华
网站建设 2026/6/7 20:19:17

当图像开始跳舞:用Matlab玩转频率域滤镜

标题:基于matlab的理想滤波器和巴特沃斯滤波器设计 关键词:一阶巴特沃斯滤波器 理想滤波器 二维傅里叶变换 fft2 描述:对任意图像进行灰度化,然后进行二维傅里叶变化,设定一个阈值,对高频或者低频信号进行滤…

作者头像 李华
网站建设 2026/6/6 20:08:13

Java面试必看:ConcurrentHashMap并发度解析

文章目录Java面试必看:ConcurrentHashMap并发度解析?场景还原:面试官与我的对话什么是并发度?并发度的核心思想并发度的实现细节1. Segment数组2. 分段锁机制3. 动态调整Segment数量4. 高效的查找机制如何配置合适的并发度&#x…

作者头像 李华
网站建设 2026/5/20 23:20:52

计算机基础小题

第一章 填空题 基于(存储程序)原理的冯诺依曼计算机,其工作方式的基本特点是(按地址访问并顺序执行指令)(指令)和(数据)都存放在存储器中,(控制器…

作者头像 李华
网站建设 2026/5/30 17:03:04

Vite 在项目中的使用分析

## 📋 目录 - [Vite 工作流程](#vite-工作流程) - [开发服务器流程](#开发服务器流程) - [构建流程](#构建流程) - [插件处理流程](#插件处理流程) - [关键配置说明](#关键配置说明) - [依赖关系](#依赖关系) ## Vite 工作流程 ## 开发服务器流程mermaid ## 构建…

作者头像 李华
网站建设 2026/5/29 15:05:54

【计算机毕业设计案例】基于Springboot实现动漫推荐系统的协同过滤算法基于协同过滤算法的动漫推荐系统(程序+文档+讲解+定制)

博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围:&am…

作者头像 李华