缓存与数据库双写不一致问题及终极解决方案(高频面试题)

Python高频面试题3 - cache应用场景扩展分析,Python缓存实现及缓存问题解决方案 阅读详情

大家好,今天我们来彻底吃透后端高频核心问题:缓存与数据库双写数据不一致!

这是面试必问、生产必踩坑的经典问题,几乎所有高并发项目(电商、秒杀、资讯、用户中心)都会遇到。本文从零拆解问题根源,手把手分析所有解决方案的优缺点、适用场景,附带清晰流程图、避坑要点,新手也能完全看懂,文末附生产选型总结!

一、前言:为什么会出现双写不一致?

1.1 缓存+数据库架构初衷

在后端开发中,为了提升查询性能、降低数据库压力,我们都会采用 MySQL + Redis 架构:

  • 读请求:优先查Redis缓存,命中直接返回,未命中查询MySQL,再回填缓存

  • 写请求:更新数据时,需要同时操作MySQL和Redis

但问题随之而来:数据库和缓存是两个独立的存储组件,无法做到原子性同时更新,一旦两次操作执行顺序错乱、并发冲突、操作失败,就会出现缓存存旧数据、数据库存新数据的双写不一致问题。

1.2 核心不一致场景

所有不一致问题,都源于并发读写冲突和操作非原子性,典型场景:

  1. 线程A更新数据库,还没来得及操作缓存,线程B读取旧缓存数据

  2. 先删缓存、后更新数据库的间隙,有请求回填旧缓存

  3. 更新数据库成功,但缓存删除/更新失败,永久脏数据

  4. 高并发读写穿插执行,打乱双写顺序

二、四种错误双写方案(避坑重点)

很多新手会写出四种错误的双写逻辑,看似没问题,实则高并发必崩,下面逐一拆解!

2.1 方案一:先更新缓存,再更新数据库

执行流程:更新Redis → 更新MySQL

致命问题:数据丢失+永久不一致

如果缓存更新成功,数据库更新失败,缓存是新数据,数据库是旧数据,后续所有读请求都会读到错误缓存数据,数据库永久无法同步。

2.2 方案二:先更新数据库,再更新缓存

执行流程:更新MySQL → 更新Redis

两大致命问题:

  1. 并发覆盖问题:高并发下,两个更新请求穿插执行 线程A(更新旧值10→20)、线程B(更新10→30) A更新数据库成功,B更新数据库成功 B先更新缓存为30,A后更新缓存为20 最终数据库30,缓存20,永久脏数据

  2. 无效更新开销:频繁更新但无人查询的数据,反复更新缓存纯属浪费性能

2.3 方案三:先删除缓存,再更新数据库

执行流程:删除Redis → 更新MySQL

核心漏洞:读写并发冲突,百分百出现不一致

并发场景复现:

  1. 线程A:删除缓存,准备更新数据库

  2. 线程B:查询数据,缓存未命中,读取数据库旧数据,回填缓存

  3. 线程A:完成数据库更新

最终结果:数据库是新数据,缓存被线程B回填旧数据,数据永久不一致!

2.4 错误方案总结

以上四种方案生产环境一律禁止使用,全部存在无法规避的并发一致性问题。

三、主流最优解决方案(生产常用)

接下来讲解生产环境主流、稳定、可落地的4种解决方案,从简单到进阶,适配不同一致性需求!

3.1 基础方案:Cache Aside 旁路缓存模式(先更库、后删缓存)

3.1.1 核心流程(最标准主流)

读流程:

  1. 查询Redis缓存,命中直接返回

  2. 未命中,查询MySQL数据库

  3. 将数据库数据回填Redis,返回结果

写流程:

  1. 优先更新MySQL数据库(保证数据源准确)

  2. 数据库更新成功后,删除Redis缓存(而非更新)

3.1.2 为什么是删除缓存,不是更新缓存?
  • 避免无效更新:频繁更新但无人读取的数据,无需频繁更新缓存,删除后下次查询自动回填最新数据

  • 避免并发覆盖:彻底解决多线程更新缓存互相覆盖的问题

3.1.3 存在的极小概率问题

仅存在一种极端不一致场景:读线程查询缓存未命中,读取旧数据库数据,此时写线程更新库+删缓存,随后读线程回填旧缓存。

该场景发生概率极低,且可以通过缓存过期机制自愈,普通业务完全够用。

3.1.4 优缺点与适用场景

优点:实现简单、性能高、无多余开销、适配绝大多数业务

缺点:极端并发下存在短暂脏数据,依赖缓存过期自愈

适用场景:普通业务、对一致性要求不极致、高并发读写场景(90%互联网项目首选)

3.2 进阶方案:延迟双删策略(解决极端脏数据)

3.2.1 核心原理

在 先更库、后删缓存 的基础上,增加延时二次删除缓存,彻底解决极端并发脏数据问题。

3.2.2 完整执行流程
  1. 更新MySQL数据库

  2. 第一次删除Redis缓存

  3. 延时等待500ms~2s(大于一次读库+回填缓存的耗时)

  4. 第二次删除Redis缓存,清除可能被回填的旧数据

3.2.3 延时时间如何设置?

生产环境禁止写死时间,通过压测获取业务读请求最大耗时,延时略大于该耗时即可,常规设置500ms-2s。同时采用异步线程执行延时删除,不阻塞主线程,不影响接口性能。

3.2.4 优缺点与适用场景

优点:彻底解决极端并发脏数据问题,一致性大幅提升,实现简单

缺点:少量延时开销,无法做到实时强一致

适用场景:一致性要求较高、并发读写频繁的业务(商品信息、用户资料)

3.3 高阶方案:分布式锁(实现实时强一致性)

3.3.1 核心思想

通过分布式锁,锁住同一数据的读写、更新操作,保证更新数据库+删除缓存的原子性,杜绝并发穿插问题。

3.3.2 执行流程
  1. 写请求:获取当前数据的分布式锁 → 更新数据库 → 删除缓存 → 释放锁

  2. 读请求:获取读锁(读写互斥、读读共享)→ 查询缓存/数据库 → 释放锁

3.3.3 优缺点与适用场景

优点:真正实现实时强一致性,无脏数据、无延时

缺点:加锁会降低接口吞吐量,存在锁竞争开销,实现复杂度高

适用场景:一致性要求极高的核心业务(库存、订单、余额、交易数据)

3.4 终极方案:Binlog异步同步(Canal 最终一致性)

3.4.1 核心原理

彻底解耦业务代码,不依赖业务逻辑操作缓存,通过Canal监听MySQL Binlog日志,异步感知数据库数据变更,自动删除/更新缓存。

3.4.2 完整执行链路

MySQL数据更新 → 记录Binlog日志 → Canal监听日志变更 → 推送变更数据 → MQ消息队列 → 消费者异步删除/更新Redis缓存

3.4.3 核心优势
  • 解耦业务:无需在业务代码中写任何缓存操作逻辑,减少代码入侵

  • 容错性极强:业务更新成功即可,缓存同步失败可重试,不会影响主流程

  • 彻底解决一致性:以数据库数据为唯一基准,异步兜底同步

  • 性能极高:主业务无任何缓存操作开销,异步串行同步,无并发冲突

3.4.4 优缺点与适用场景

优点:最终一致性最强、性能最优、业务无侵入、支持重试兜底

缺点:存在毫秒级异步延时,无法实时一致,部署复杂度高(需搭建Canal、MQ)

适用场景:绝大多数高并发生产业务、大数据量更新业务、核心兜底方案

四、所有方案对比+生产选型指南

解决方案

一致性级别

性能开销

实现难度

适用场景

Cache Aside基础版

弱一致(可自愈)

极低

极简

普通非核心业务

延迟双删

高一致(近实时)

低(异步延时)

简单

高频读写、较高一致性业务

分布式锁

实时强一致

较高(锁竞争)

较高

订单、库存、余额核心业务

Binlog+Canal异步同步

最终强一致

极低(异步解耦)

高

大型高并发项目、全局兜底

五、生产最佳实践(重点总结)

  1. 禁止使用四种错误双写方案,一律采用「先更库、后删缓存」为基础规范

  2. 普通业务:Cache Aside模式 + 缓存过期兜底,足够使用

  3. 中高一致性业务:Cache Aside + 异步延迟双删

  4. 核心金融/交易业务:分布式锁保证实时强一致

  5. 大型高并发项目:业务层延迟双删 + Canal Binlog异步兜底,双重保障,万无一失

  6. 通用兜底机制:所有缓存必须设置过期时间,即使出现脏数据,也能自动恢复,避免永久不一致

六、面试高频问答总结

Q1:为什么不推荐先更新缓存,而是删除缓存?

1. 避免无效更新:无人读取的更新操作纯属性能浪费;2. 避免并发缓存覆盖问题,彻底杜绝多线程更新缓存导致的数据错乱。

Q2:延迟双删的延时时间怎么设置?

无需固定值,根据业务压测结果设置,延时时间略大于业务「读库+回填缓存」的最大耗时,生产常规500ms-2s,且必须异步执行不阻塞主线程。

Q3:Binlog方案的核心优势是什么?

业务无侵入、主流程无性能损耗、容错性高、支持失败重试,以数据库为唯一数据源头,从根本上保证最终一致性,是大型互联网公司的终极解决方案。

Q4:如何彻底杜绝缓存数据库不一致?

无绝对完美方案,实时强一致用分布式锁,最终强一致用Binlog异步同步,生产最优组合:业务层主动删缓存 + 延迟双删兜底 + Binlog异步兜底 + 缓存过期自愈。

七、结尾

缓存与数据库双写一致性,没有万能方案,脱离业务场景谈方案都是空谈。

本文从问题根源、错误避坑、逐级解决方案、生产选型、面试问答全方位覆盖,完全满足日常开发、面试、生产落地需求。大家可以根据自己项目的并发量、一致性要求、成本预算灵活选型!

码字不易,欢迎点赞、收藏、关注,后续持续更新后端高并发核心干货!

Python爬虫实战:5分钟搞定东方财富网股票数据抓取(附完整代码) 本文详细介绍了如何利用Python爬虫技术,快速抓取东方财富网的实时股票数据。通过逆向分析其JSONP数据接口,构建了包含请求处理、数据清洗、分页调度及多格式存储的完整自动化采集系统,并提供了应对反爬策略与生产部署的进阶优化思路,附有可直接运行的完整代码。 阅读详情

相关推荐

告别昂贵硬件!用Unity3D 2023 + ROS2 Humble搭建你的第一个SLAM仿真实验室(保姆级教程)

本文提供了一份详细的教程,介绍如何使用Unity3D 2023和ROS2 Humble搭建SLAM仿真实验室,无需昂贵硬件即可实现虚拟传感器模拟和算法测试。通过Docker容器配置、ROS2安装优化及Unity插件集成,开发者可以快速构建高精度的仿真环境,适用于自主导航、多传感器标定等场景。

weixin_42526353的博客 433

告别数据不一致!缓存与数据库双写一致的终极解决方案

摘要:本文探讨了分布式系统中缓存与数据库双写不一致的解决方案。核心思路是通过控制操作时序或消除并发实现最终一致性。主流方案包括:1)缓存延迟双删(最常用),通过异步延时消息二次清理缓存;2)重试机制方案,保证删除操作最终成功;3)异步串行化(强一致性但性能低);4)基于Binlog的异步删除(最优雅但架构复杂)。文章对比了各方案在一致性、性能和复杂度上的权衡,建议根据业务场景选择合适方案,普通业务推荐延迟双删,复杂系统可考虑Binlog同步方案。

随时分享高质量技术文章 501

UNet 改进(39):结合残差连接与CBAM注意力机制的unet模型

本文提出了一种改进的UNet架构,通过融合残差连接和CBAM注意力机制来增强医学图像分割性能。该网络保留UNet的U形结构,但将基础模块替换为ResidualBlock和CBAM的组合:残差块通过快捷连接解决梯度消失问题,CBAM模块结合通道和空间注意力机制突出重要特征。整体架构采用编码器-解码器设计,包含5个下采样和上采样阶段,通过跳跃连接融合多尺度特征。实验表明,这种改进显著提升了原始UNet的性能,特别适用于需要精细边界的医学图像分割任务。

2401_82355416的博客 1012

java面试:请问你是怎么保证缓存一致性的呢?

(1)基本概念 不书面的说,缓存其实就是我们程序和后端之间的一种高速的数据存储层,其存储的就是后端的数据,在我们进行数据查询时优先会先查询缓存,如果有则直接读取数据,没有再去读数据库,这样会更高效方便。

2301_80939853的博客 1258

华为OD机考双机位C卷 - 采购订单 (Java & Python& JS & C/C++ & GO )

华为OD机考双机位C卷 - 采购订单 。提供Java、C++、Python、Go、JavaScript源码实现,并提供完整思路讲解,以及在线OJ模拟做题。

算法大师! 426

基于非接触式微波雷达设计的跌倒检测预警系统【短信报警】

本项目选择基于非接触式微波雷达的跌倒检测预警装置作为技术核心,采用云帆瑞达的R60AFD1跌倒检测模组。这一模组能够根据人体的运动变化,如速度、距离和姿态等特征,判断是否发生跌倒,并可以持续监控老年人是否处于危险状态。此外,本项目还结合4G通信技术,通过Air724UG模块实现短信报警功能,一旦检测到跌倒事故,系统将自动通过4G网络向紧急联系人发送短信报警,及时通知亲属或医护人员进行救援。

1750

解决缓存与数据库双写不一致问题

这样,后续的读操作会从数据库中读取最新的数据,并更新到缓存中。但是,如果在删除缓存后,更新数据库之前,有读请求到来,会从数据库中读取旧数据并更新到缓存中,仍然会导致不一致。例如,线程 A 先更新了数据库,然后线程 B 也更新了数据库,接着线程 A 更新缓存,此时缓存中的数据就变成了旧值。例如,先更新数据库,后删除缓存,如果在删除缓存之前,有其他请求读取了旧的缓存数据,就会导致数据不一致。即在更新数据库后,延迟一段时间再次删除缓存,确保在这段时间内,数据库的更新已经完成,并且没有新的读请求将旧数据写入缓存。

m0_57836225的博客 492

基于YOLOv8/YOLOv7/YOLOv6/YOLOv5的商品识别系统(深度学习+UI界面+训练数据集+Python代码)

随着新零售和智能仓储的快速发展,商品识别技术成为计算机视觉领域的重要研究方向。本文详细介绍了基于YOLO系列算法的商品识别系统的完整实现方案,涵盖算法原理、数据集构建、模型训练、系统集成和可视化界面开发。通过对比YOLOv5到YOLOv8各版本性能差异,我们构建了一个高效、准确且用户友好的商品识别系统,并提供完整的代码实现。

YOLO 282

解决缓存和数据库双写数据一致性问题

双写不一致分析

Zhuxiaoyu_91的博客 1439

手机VPN的连接方法分享

6、返回到VPN页面,可以看到刚刚新添加的VPN,点击开启VPN后面的开关。5、在打开的添加VPN的页面中输入VPN的信息后点击右上角的“保存”按钮。7、待VPN开关开启后,可以看到添加的VPN正在连接的提示信息了。2、在打开的小米手机设置页面中点击“更多连接方式”的菜单项。3、然后在打开的更多连接方式页面中点击VPN的菜单项。4、在打开的VPN设置页面中点击“添加VPN”的按钮。1、首先在手机屏幕左右的滑动,找到手机的设置图标。

惠惠软件 3万+

基于OpenCV实现暗通道先验去雾算法及改进,效果惊人

深度技术文,前方高能

公号:一条coding 2万+

高并发场景下的redis缓存和数据库双写不一致问题分析与解决方案设计

1.最经典的缓存+数据库读写的模式,cache aside pattern 1.1、Cache Aside Pattern (1)读的时候,先读缓存,缓存没有的话,那么就读数据库,然后取出数据后放入缓存,同时返回响应 (2)更新的时候,先删除缓存,然后再更新数据库 1.2、为什么是删除缓存,而不是更新缓存呢? (1)、如果写数据库的值与更新到缓存值是一样的,不需要经过任何的计算,可以...

数据与算法架构提升之路专栏 8335

Android UI 组件系列(二):Button 进阶用法

在这篇博客中,我们深入探讨了 Button 的进阶用法,重点介绍了如何通过 事件处理 来响应用户的操作,如何使用 自定义样式 来提升按钮的外观,并利用 StateListDrawable 来管理按钮在不同状态下的表现。这些技巧能够帮助你打造更具交互性、视觉吸引力和动态响应的按钮,提升用户体验。

weixin_39339407的博客 1624

一个高频面试题:怎么保证缓存与数据库的双写一致性?

来源:https://blog.youkuaiyun.com/chang384915878/article/details/86756463分布式缓存是现在很多分布式应用中必不可少的...

HollisChuang's Blog 2876

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

Redis--如何保障缓存数据库一致性?(面试高频问题)

缓存与数据库一致性如何解决?根据业务背景不同会有不一样的解决办法。

你只管努力,其他的交给天意。 1887

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

读写锁ReentrantReadWriteLock: 缓存+数据库一致性问题如何优雅解决

读写锁机制: 读读共享、读写互斥、写写互斥缓存一致性: 读操作加读锁查DB回写,写操作加写锁更新DB删缓存锁降级: 写锁可以降级为读锁,读锁不能升级为写锁适用场景: 单机、读多写少、需要强一致性。

✨ 欢迎来到【Seal ^_^ 的优快云博客】!✨ 1354

高频面试题:如何保证缓存与数据库的双写一致性?

分布式缓存是现在很多分布式应用中必不可少的组件,但是用到了分布式缓存,就可能会涉及到缓存与数据库双存储双写,你只要是双写,就一定会有数据一致性的问题,那么你如何解决一致性问题? Cache Aside Pattern 最经典的缓存+数据库读写的模式,就是 Cache Aside Pattern。 读的时候,先读缓存,缓存没有的话,就读数据库,然后取出数据后放入缓存,同时返回响应。 更新...

勇往直前的专栏 379

Redis 和 MySQL双写一致性的更新策略有哪些?常见面试题深度解答。

本文探讨了Redis与MySQL双写一致性问题及解决方案。首先分析了四种更新策略的优缺点,指出先操作数据库后操作缓存更为合理,并推荐优先删除缓存而非更新缓存。针对不同业务场景提出三种方案:低频修改数据采用缓存过期+延迟双删;高频修改数据使用Binlog监听+消息队列;资金类敏感数据采用数据库事务+同步更新。文章还详细介绍了Canal中间件的配置使用,并提供了代码实现示例。最后总结了保证数据最终一致性的核心思路:数据库为主、缓存为辅,根据业务特点选择合适的同步策略。

m0_70325779的博客 1902

面试:高频面试题:如何保证缓存与数据库的双写一致性?

点击上方蓝色“程序猿DD”,选择“设为星标”回复“资源”获取独家整理的学习资料!来源 |blog.youkuaiyun.com/chang384915878/ar...

程序猿DD 504
上一篇: IntelliJ IDEA 试用过期怎么办?傻瓜式一键重置使用时间(支持2026版)
下一篇: MySQL InnoDB引擎八大核心特性详解(高频面试题)
呆呆企鹅仔
博客等级 码龄6年 1740粉丝 125原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

呆呆企鹅仔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值