在线 Java 面试刷题已更新239题https://www.quanxiaoha.com/java-interview面试考察点基础掌握度面试官不仅仅是想知道 先删缓存 还是 后删缓存更是想知道你是否能分析出每种方案在并发场景下的数据不一致问题以及背后的时序原因。方案设计能力考察你是否了解 延迟双删、订阅 Binlog 异步更新、设置 TTL 兜底 等生产级别的解决方案而不是只停留在理论层面。工程思维能否认识到 缓存和数据库的强一致性在分布式系统中几乎不可能接受 最终一致性 并设计对应的补偿机制。核心答案缓存和数据库的一致性有4 种常见方案方案操作顺序一致性推荐程度先更新数据库再更新缓存DB → Cache并发更新会覆盖数据错乱不推荐先删缓存再更新数据库Del → DB → Read → Cache并发读会写回旧值需配合延迟双删先更新数据库再删缓存推荐DB → Del极端情况不一致概率极低Cache Aside 模式订阅 Binlog 异步更新最可靠DB → Binlog → Cache最终一致性生产首选一句话结论推荐Cache Aside 模式先更新 DB再删缓存搭配 TTL 兜底。如果对一致性要求更高用Canal 订阅 MySQL Binlog异步更新缓存。不要追求强一致性接受最终一致性。深度解析一、方案一先更新数据库再更新缓存不推荐img上图展示了 先更新 DB 再更新缓存 在并发场景下的问题线程 A 和线程 B 同时更新同一条数据。线程 A 先更新了数据库20但还没来得及更新缓存时线程 B 已经完成了数据库和缓存的更新30。线程 A 随后更新缓存为 20把线程 B 正确的值 30 给覆盖了出现了数据库和缓存数据不一致的问题。根本问题更新缓存的顺序和更新数据库的顺序可能不一致后面的更新可能把前面的覆盖。另外如果缓存值是经过复杂计算得出的比如多表联查、聚合统计每次更新数据库都要重新计算并写入缓存性能开销大而且很多更新操作可能根本没有读请求白算了一遍。二、方案二先删缓存再更新数据库有缺陷img上图展示了 先删缓存再更新 DB 的问题线程 A 删除缓存后还没来得及更新数据库。这时线程 B 来读数据发现缓存 miss去数据库读到了旧值10。线程 B 把旧值 10 写回缓存。之后线程 A 才把数据库更新为 20。结果数据库是新值 20缓存里还是旧值 10后续所有读请求都会命中缓存的旧值直到缓存过期。补救方案延迟双删img上图展示了延迟双删的核心思路在更新数据库后等一段时间让并发读请求把旧值写回缓存再删一次缓存。问题sleep 时间很难确定而且同步 sleep 会占用线程资源。实际中一般用异步消息MQ 延迟消息来实现第二次删除。三、方案三先更新数据库再删缓存Cache Aside推荐这是业界最广泛使用的方案也叫Cache Aside Pattern旁路缓存模式。img上图展示了 Cache Aside 模式的完整逻辑写操作先更新数据库再删除缓存。注意是删除而不是更新因为删除更轻量且能避免并发更新导致的覆盖问题。读操作先读缓存命中直接返回未命中则读数据库写回缓存。极端不一致的概率极低只有 读请求写缓存 的速度比 写请求更新 DB 删除缓存 还慢时才会出问题而写 DB 一般比读 DB 慢得多涉及锁、磁盘 IO所以实际中几乎不会发生。为什么 删缓存 比 更新缓存 好维度更新缓存删除缓存并发安全❌ 并发更新可能互相覆盖✅ 删除是幂等的多次删除无副作用性能❌ 每次写都要算新值并更新✅ 删除操作轻量懒加载❌ 无论有没有读都更新, 浪费内存✅ 下次读时才从 DB 加载四、方案四订阅 Binlog 异步更新生产最可靠如果对一致性要求更高可以借助 MySQL 的Binlog来异步更新缓存。img上图展示了 Binlog 方案的完整架构Canal是阿里开源的 MySQL Binlog 增量订阅组件它伪装成 MySQL 的从节点实时接收 Binlog 事件。业务服务只管写数据库完全不需要关心缓存。Canal 监听到数据变更后通过 MQKafka / RocketMQ投递消息消费者服务负责更新或删除缓存。优势业务代码零侵入、解耦彻底、Binlog 可靠不丢失、消费失败可重试能保证最终一致性。代价引入了 Canal 和 MQ 组件运维复杂度增加。五、生产环境推荐方案日常方案Cache Aside TTL 兜底① 先更新数据库② 再删除缓存③ 缓存设置 TTL 兜底最终一致性保险④ 删除失败 → 重试MQ 或本地重试表覆盖 99% 场景简单可靠。高可靠方案 Canal Binlog① 业务只写 DB② Canal 订阅 Binlog③ MQ 投递 消费者更新缓存④ 消费失败自动重试⑤ 仍然设置 TTL 兜底适合对一致性要求高的核心业务。关键原则❌ 不要追求强一致性 → 不现实✅ 接受最终一致性 TTL 兜底✅ 删缓存比更新缓存更安全✅ 删除失败要有重试机制面试高频追问追问一为什么不直接更新缓存而要删除缓存更新缓存在并发场景下存在覆盖问题两个线程同时更新后更新的值可能被先更新的旧值覆盖。而删除是幂等操作多次删除没有副作用下次读时自然从 DB 加载最新值。另外更新缓存需要知道新值是什么可能涉及复杂计算删除则不需要更轻量。追问二Cache Aside 模式下删除缓存失败了怎么办最简单的方案是重试可以用 MQ 异步重试也可以用本地消息表 定时任务补偿。更高级的方案是引入 Canal 订阅 Binlog 作为兜底即使业务删缓存失败Canal 也会异步更新缓存。再加上缓存 TTL 兜底即使所有重试都失败了缓存过期后也会从 DB 加载最新值。追问三Redis 和数据库能实现强一致性吗不能。因为 Redis 和 MySQL 是两个独立的存储系统没有任何分布式事务机制能保证两步操作的原子性。即使在同一个事务里操作网络延迟、部分失败等因素都会导致不一致。所以业界共识是接受最终一致性通过 TTL 兜底 重试机制 Binlog 补偿来逼近一致性。常见面试变体变体一先删缓存还是后删缓存为什么变体二缓存和数据库双写不一致怎么解决变体三什么是 Cache Aside 模式变体四Canal 订阅 Binlog 更新缓存了解吗记忆口诀四种方案排序更新缓存不推荐 先删缓存再更新 DB延迟双删 先更新 DB 再删缓存Cache Aside推荐 Canal Binlog最可靠。核心原则更新 DB 是源头缓存只是视图删缓存比更新缓存更安全TTL 是最后一道防线。Cache Aside写 → 更新 DB 删缓存读 → 读缓存 miss 读 DB 回写缓存。总结保证 Redis 和数据库一致性的推荐方案是 Cache Aside 模式先更新数据库再删除缓存配合 TTL 兜底和删除失败重试机制。如果对一致性要求更高引入 Canal 订阅 MySQL Binlog 异步更新缓存实现业务代码零侵入的最终一致性。核心原则不要追求强一致性接受最终一致性用 删缓存 TTL 重试 三重保险来逼近一致。 欢迎加入小哈的星球你将获得:专属的项目实战多个项目 / 1v1 提问 /Java 学习路线 /学习打卡 / 每月赠书 / 社群讨论新项目《Spring AI 项目实战》正在更新中..., 基于 Spring AI Spring Boot 3.x JDK 21;《从零手撸仿小红书微服务架构》 已完结基于 Spring Cloud Alibaba Spring Boot 3.x JDK 17..., 点击查看项目介绍演示地址http://116.62.199.48:7070/《从零手撸前后端分离博客项目全栈开发》2期已完结,演示链接http://116.62.199.48/;专栏阅读地址https://www.quanxiaoha.com/column截止目前累计输出 100w 字讲解图 4013 张还在持续爆肝中..后续还会上新更多项目目标是将 Java 领域典型的项目都整一波如秒杀系统, 在线商城, IM 即时通讯Spring Cloud Alibaba 等等戳我加入学习解锁全部项目已有4500小伙伴加入1. 我的私密学习小圈子从0到1手撸企业实战项目~ 2. 中国最难入职的八家 IT 公司 3. 得物二面什么是缓存击穿、缓存穿透、缓存雪崩修订版 4. 重磅JetBrains 正式发布全新的 AI 开发工具定名 AI IDE AIR最近面试BAT整理一份面试资料《Java面试BATJ通关手册》覆盖了Java核心技术、JVM、Java并发、SSM、微服务、数据库、数据结构等等。 获取方式点“在看”关注公众号并回复 Java 领取更多内容陆续奉上。PS因公众号平台更改了推送规则如果不想错过内容记得读完点一下“在看”加个“星标”这样每次新文章推送才会第一时间出现在你的订阅列表里。 点“在看”支持小哈呀谢谢啦