目录

  • 1、四种同步策略:
  • 2、更新缓存还是删除缓存
    • 2.1 更新缓存
    • 2.2 删除缓存
  • 3、先操作数据库还是缓存
    • 3.1 先删除缓存再更新数据库
    • 3.2 先更新数据库再删除缓存
  • 4、延时双删
    • 4.1 采用读写分离的架构怎么办?
  • 5、利用消息队列进行删除的补偿

1、四种同步策略:

想要保证缓存与数据库的双写一致,一共有4种方式,即4种同步策略:

  1. 先更新缓存,再更新数据库;
  2. 先更新数据库,再更新缓存;
  3. 先删除缓存,再更新数据库;
  4. 先更新数据库,再删除缓存。

从这4种同步策略中,我们需要作出比较的是:

更新缓存与删除缓存哪种方式更合适?应该先操作数据库还是先操作缓存?

2、更新缓存还是删除缓存

下面,我们来分析一下,应该采用更新缓存还是删除缓存的方式。

2.1 更新缓存

优点每次数据变化都及时更新缓存,所以查询时不容易出现未命中的情况。

缺点更新缓存的消耗比较大。如果数据需要经过复杂的计算再写入缓存,那么频繁的更新缓存,就会影响服务器的性能。如果是写入数据频繁的业务场景,那么可能频繁的更新缓存时,却没有业务读取该数据。

2.2 删除缓存

优点操作简单,无论更新操作是否复杂,都是将缓存中的数据直接删除。

缺点删除缓存后,下一次查询缓存会出现未命中,这时需要重新读取一次数据库。从上面的比较来看,一般情况下,删除缓存是更优的方案。

3、先操作数据库还是缓存

下面,我们再来分析一下,应该先操作数据库还是先操作缓存。
首先,我们将先删除缓存与先更新数据库,在出现失败时进行一个对比:

3.1 先删除缓存再更新数据库


如上图,是先删除缓存再更新数据库,在出现失败时可能出现的问题:

  • 线程A删除缓存成功,线程A更新数据库失败;
  • 线程B从缓存中读取数据;由于缓存被删,进程B无法从缓存中得到数据,进而从数据库读取数据;此时数据库中的数据更新失败,线程B从数据库成功获取旧的数据,然后将数据更新到了缓存。
  • 最终,缓存和数据库的数据是一致的,但仍然是旧的数据

3.2 先更新数据库再删除缓存


如上图,是先更新数据库再删除缓存,在出现失败时可能出现的问题:

  • 线程A更新数据库成功,线程A删除缓存失败;
  • 线程B读取缓存成功,由于缓存删除失败,所以线程B读取到的是缓存中旧的数据。
  • 最后线程A删除缓存成功,有别的线程访问缓存同样的数据,与数据库中的数据是一样。
  • 最终,缓存和数据库的数据是一致的,但是会有一些线程读到旧的数据。

经过上面的比较,我们发现在出现失败的时候,是无法明确分辨出先删缓存和先更新数据库哪个方式更好,以为它们都存在问题。后面我们会进一步对这两种方式进行比较,但是在这里我们先探讨一下,上述场景出现的问题,应该如何解决呢?

实际上,无论上面我们采用哪种方式去同步缓存与数据库,在第二步出现失败的时候,都建议采用重试机制解决,上面两幅图中已经画了。




下面我们再将先删缓存与先更新数据库,在没有出现失败时进行对比:

如上图,是先删除缓存再更新数据库,在没有出现失败时可能出现的问题:

  • 线程A删除缓存成功;
  • 线程B读取缓存失败;
  • 线程B读取数据库成功,得到旧的数据;
  • 线程B将旧的数据成功地更新到了缓存;
  • 线程A将新的数据成功地更新到数据库。

可见,进程A的两步操作均成功,但由于存在并发,在这两步之间,进程B访问了缓存。最终结果是,缓存中存储了旧的数据,而数据库中存储了新的数据,二者数据不一致。




如上图,是先更新数据库再删除缓存,在没有出现失败时可能出现的问题:

  • 线程A更新数据库成功;
  • 线程B读取缓存成功;
  • 线程A删除缓存成功。

可见,最终缓存与数据库的数据是一致的,并且都是最新的数据。但线程B在这个过程里读到了旧的数据,可能还有其他线程也像线程B一样,在这两步之间读到了缓存中旧的数据,但因为这两步的执行速度会比较快,所以影响不大。对于这两步之后,其他进程再读取缓存数据的时候,就不会出现类似于进程B的问题了。

最终结论:

经过对比你会发现,先更新数据库、再删除缓存是影响更小的方案。如果第二步出现失败的情况,则可以采用重试机制解决问题。

4、延时双删

上面我们提到,如果是先删缓存、再更新数据库,在没有出现失败时可能会导致数据的不一致。如果在实际的应用中,出于某些考虑我们需要选择这种方式,那有办法解决这个问题吗?答案是有的,那就是采用延时双删的策略,延时双删的基本思路如下

  1. 删除缓存;
  2. 更新数据库;
  3. sleep N毫秒;
  4. 再次删除缓存。
 public void write(String key, Object data) {Redis.delKey(key);db.updateData(data);Thread.sleep(1000);Redis.delKey(key);}

阻塞一段时间之后,再次删除缓存,就可以把这个过程中缓存中不一致的数据删除掉。而具体的时间,要评估你这项业务的大致时间,按照这个时间来设定即可。

4.1 采用读写分离的架构怎么办?

如果数据库采用的是读写分离的架构,那么又会出现新的问题,如下图:

此时来了两个请求,请求 A(更新操作) 和请求 B(查询操作)

  1. 请求 A 更新操作,删除了 Redis;
  2. 请求主库进⾏更新操作,主库与从库进行同步数据的操作;
  3. 请 B 查询操作,发现 Redis 中没有数据;
  4. 去从库中拿去数据;
  5. 此时同步数据还未完成,拿到的数据是旧数据;

此时的解决办法就是如果是对 Redis 进行填充数据的查询数据库操作,那么就强制将其指向主库进⾏查询。

删除失败了怎么办?

如果删除依然失败,则可以增加重试的次数,但是这个次数要有限制,当超出一定的次数时,要采取报错、记日志、发邮件提醒等措施。

5、利用消息队列进行删除的补偿

先更新数据库,后删除缓存这⼀种情况也会出现问题,比如更新数据库成功了,但是在删除缓存的阶段出错了没有删除成功,那么此时再读取缓存的时候每次都是错误的数据了。

此时解决方案就是利用消息队列进行删除的补偿。具体的业务逻辑⽤语⾔描述如下:

  1. 请求 线程A 先对数据库进行更新操作;
  2. 在对 Redis 进行删除操作的时候发现报错,删除失败;
  3. 此时将Redis 的 key 作为消息体发送到消息队列中;
  4. 系统接收到消息队列发送的消息后再次对 Redis 进行删除操作;

但是这个方案会有⼀个缺点就是会对业务代码造成大量的侵入,深深的耦合在⼀起,所以这时会有⼀个优化的方法,我们知道对 Mysql 数据库更新操作后再 binlog 日志中我们都能够找到相应的操作,那么我们可以订阅 Mysql 数据库的 binlog 日志对缓存进行操作。

如何保证Redis缓存与数据库的一致性?相关推荐

  1. 如何保证缓存和数据库的一致性?

    文章目录 1. 问题分析 2. Cache-Aside 2.1 读缓存 2.2 写缓存 2.3 延迟双删 2.4 如何确保原子性 3. Read-Through/Write-Through 3.1 R ...

  2. 缓存与数据库的一致性:先操作缓存还是先操作数据库?

    数据缓存 在我们实际的业务场景中,一定有很多需要做数据缓存的场景,比如售卖商品的页面,包括了许多并发访问量很大的数据,它们可以称作是是"热点"数据,这些数据有一个特点,就是更新频率 ...

  3. 如何保障缓存和数据库的一致性(超详细案例)【转载自 程序员囧辉】

    如何保障缓存和数据库的一致性(超详细案例)[转载自 程序员囧辉] 一.前言 二.正文 方案1:同步删除 1.1 核心流程 1.2 存在的问题 方案2:延迟双删 2.1 核心流程 2.2 存在的问题 方 ...

  4. 预约活动(秒杀)项目中如何高效的保证下单交易成功?保证redis,mysql的最终一致性?

    预约活动(秒杀)项目中如何高效的保证下单交易成功?保证redis,mysql的最终一致性? 前言 `在秒杀项目中,秒杀下单的过程中都需要经历四步,分别为: 1.校验下单状态,商品是否存在,用户是否合法 ...

  5. 链接mysql_使用python链接mysql及redis(缓存型数据库)

    python链接数据库mysql操作,首先我们需要借助第三方库pymysql. cmd终端下载:pip install pymysql import pymysql#链接数据库db = pymysql ...

  6. Redis缓存与数据库双写一致性

    前言: 首先,缓存由于其高并发和高性能的特性,已经在项目中被广泛使用.在读取缓存方面,大家没啥疑问,都是按照下图的流程来进行业务操作.         但是在更新缓存方面,对于更新完数据库,是更新缓存 ...

  7. 缓存与数据库的一致性

    本文来说下缓存与数据库的双写一致性.分布式缓存是现在很多分布式应用中必不可少的组件,但是用到了分布式缓存,就可能会涉及到缓存与数据库双存储双写,你只要是双写,就一定会有数据一致性的问题,那么你如何解决 ...

  8. redis缓存跟数据库数据不一致问题解决

    如何保证缓存与数据库的双写一致性? 最经典的缓存+数据库读写的模式. 读的时候,先读缓存,缓存没有的话,就读数据库,然后取出数据后放入缓存,同时返回响应. 更新的时候,先删除缓存,再更新数据库. 为什 ...

  9. 更新数据时redis缓存与数据库数据不一致的问题

    最初级的缓存不一致问题及解决方案 问题:先修改数据库,再删除缓存.如果删除缓存失败了,那么会导致数据库中是新数据,缓存中是旧数据,数据就出现了不一致. 解决思路:先删除缓存,再修改数据库.如果数据库修 ...

最新文章

  1. 4.API的调用过程(系统服务表)
  2. ap统计学需要什么计算机,准备AP统计学考试不需要死背公式
  3. java卡片布局源码_Java编程使用卡片布局管理器示例【基于swing组件】
  4. .NET 5 的 Target Framework 详解[上篇]
  5. AFNetworking 3.1.0 使用中某些知识点讲解
  6. 红魔游戏手机6 Pro氘锋透明版明日开启预售:售价5599元
  7. mysql 性能剖析_MySQL服务器性能剖析(一)
  8. apollo 部署 使用
  9. 深度学习:循环神经网络RNN
  10. day22 属性 类方法 静态方法 反射 https://www.cnblogs.com/jin-xin/articles/9214247.html
  11. [工业互联-6]:PLC工业控制系统快速概览
  12. 必读论文 | 机器交互必读论文8篇
  13. (c++ 遗传算法解决TSP问题)不是吧,这就是遗传算法吗?爱了爱了
  14. html判断是否在微信里打开,JavaScript判断浏览器内核,微信打开自动提示在浏览器打开...
  15. 【修真院“善良”系列之十七】请拿好这支时间之箭
  16. Python中Numpy中省略号的作用
  17. 第17课 项目成本管理
  18. 无法安装 cloudera-manager-agent
  19. 电商名词sku和spu的区别
  20. IDEA javadoc快捷键

热门文章

  1. Google guava工具类库的介绍和使用
  2. zf框架的思想及学习总结
  3. 2022年华为ICT大赛 全球总决赛!中文综合任务书-网络赛道真题!
  4. js获取class对象
  5. 创建linux目录的基本命令
  6. poj3310Caterpillar(树直径)
  7. 【蓝桥杯省赛学习题Java】小邋遢的衣橱
  8. 超详细:实现过程-Linux 环境下的简易聊天室,采用CS模型,实现多客户端之间的稳定数据传输。--注册和登录(但之后会连续更新内容,直至全部实现)
  9. java sql传参_JAVA执行带参数的SQL语句
  10. java 长轮询_基于springboot 长轮询的实现操作