当前位置: 首页 > news >正文

Redis和数据库一致性问题

操作模拟

1、先更新数据库还是先更新缓存?

1.1先更新缓存,再更新数据库

按并发的角度来说,有两个线程A、B,操作同一个数据,线程A先更新缓存为1,在线程A更新数据库之前,这时候线程B进来,更新缓存为2,更新数据库为2,这时候线程A更新数据库为1.

此时,缓存中数据为2,但数据库为1,缓存和数据库不一致

画板

因此先更新缓存,再更新数据库会有数据不一致的情况

1.2 先更新数据库,再更新缓存

我们再来进行分析。

线程A先更新数据库为1,还未更新缓存,这时候线程B更新数据库为2,缓存更新为2,最后线程A把缓存更新为1。

最后情况:缓存中为1,数据库中为2

画板

:::info
仍然会出现数据库和缓存不一致问题

:::

小结

【先更新缓存,再更新数据库】和【先更新数据库,再更新缓存】都有可能导致数据库和缓存不一致问题。既然这两者不一致,那么我们能否考虑去掉其中一个呢?当然不能删数据库,那就只能删缓存了。也就是后面的方法。在删缓存的顺序也有讲究。

2、先删缓存,还是先更新数据库?

2.1、先删除缓存,再更新数据库

同样使用多线程来分析,这时候使用读写进行分析。

写线程A进入,删除缓存,这时候°线程B进入,读取数据库值为1,更新缓存为1(由于更新缓存的操作是非常快的,因此出现这种情况的概率会比较大),最后线程A更新数据库为2,就会出现数据库和缓存不一致的情况。

画板

2.2、先更新数据库,再删除缓存

同样使多线程分析。

  • 线程A进入,先更新数据库为2,
  • 后面读线程B进入,读取数据为1
  • 然后线程A删除缓存
  • 线程B写入缓存为1

此时仍然会出现数据库与缓存不一致的情况,但是请注意,读取数据和写入缓存数据通常要快得多,因此出现这种情况的概率是比【先删缓存,再更新数据库】的概率低得多的,所以我们只需要再给缓存增加一个过期时间即可。

画板

删除缓存的操作可能会失败
使用消息队列发送请求

:::color2
- 这时候还可以引入消息队列(利用消息重试机制)来进行操作,将删除缓存放到消费者当中去。但是此方法会导致代码入侵,需要修改原来删缓存的业务代码,然后添加消息队列重试机制。

:::

订阅 MySQL binlog,再操作缓存

我们知道,mysql是有一个二进制日志文件的,当数据库产生更新操作时,就会计入该二进制文件中。

【先更新数据库,再删缓存】的策略的第一步是更新数据库,那么更新数据库成功,就会产生一条

变更日志,记录在 binlog里。

于是我们就可以通过订阅binlog日志,拿到具体要操作的数据,然后再执行缓存删除,阿里巴巴开

源的**Canal中间件**就是基于这个实现的。

Canal模拟MySQL主从复制的交互协议,把自己伪装成一个MySQL的从节点,向MySQL主节点

发送dump请求,MySQL收到请求后,就会开始推送Binlog给Canal,Canal解析 Binlog字节流

之后,转换为便于读取的结构化数据,供下游程序订阅使用。

Canal工作原理

前面我们说到直接用消息队列重试机制方案的话,会对代码造成入侵,那么Canal方案就能很好的

规避这个问题,因为它是直接订阅binlog日志的,和业务代码没有藕合关系,因此我们可以通过

Canal+消息队列的方案来保证数据缓存的一致性。

具体的做法是:

:::color1
将binlog日志采集发送到MQ队列里面,然后编写一个简单的缓存删除消息者订阅

binlog日志,根据更新log删除缓存,并且通过ACK机制确认处理这条更新log,保证数据缓存一致

性。这样,我们其实是在业务代码之外对缓存进行删除的,通过直接订阅mysql的binlog文件,而不是在业务代码中手动发送删除消息。

:::

总结

:::info
数据库和缓存保持一致性的问题,最简单快捷的方式就是【先更新数据库,再删除缓存】,但是这会导致缓存命中率变低,如果对缓存命中率有要求的系统,可以考虑【先更新缓存,再更新数据库】或者【先更新数据库,再更新缓存】的方式,但是需要加上锁机制进行线程互斥(把更新缓存和更新数据库的操作放在同步代码块内),这样才能避免多线程同时操作,导致数据库与缓存不一致的情况,但是这会导致写入性能下降。

:::

http://www.dtcms.com/a/122555.html

相关文章:

  • 微前端架构深度解析
  • 华为海思IC前端中后端(COTXPU)岗位笔试机考题
  • 深入理解 rsync daemon 模式(守护进程)
  • 【简单理解什么是简单工厂、工厂方法与抽象工厂模式】
  • 【“星睿O6”AI PC开发套件评测】在O6开发板使用gemma-2b测试CPU性能
  • 测试用例 [软件测试 基础]
  • 加油站小程序实战教程10开通会员
  • 重构居家养老安全网:从 “被动响应” 到 “主动守护”
  • Windows上使用Qt搭建ARM开发环境
  • 如何在idea中快速搭建一个Spring Boot项目?
  • <项目代码>苹果识别<目标检测>
  • 从零到精通:Kafka学习路径全解析
  • 阅读MySQL实战45讲第9天
  • Windows + vmware + ubuntu+docker + docker-android实现Android模拟器构建和启动
  • Java——字符串(黑马个人听课笔记)
  • flutter开发音乐APP(前提准备)
  • 计算机网络笔记-分组交换网中的时延
  • 【挑战项目】 --- 微服务编程测评系统(在线OJ系统)(一)
  • MongoDB 的详细介绍
  • 网络基础2
  • 【含文档+PPT+源码】基于python爬虫的豆瓣电影、音乐、图书数据分析系统
  • Vue 基础语法介绍
  • 剑指Offer(数据结构与算法面试题精讲)C++版——day9
  • 探秘 Svelte+Vite+TS+Melt - UI 框架搭建,开启高效开发
  • Could not resolve com.google.guava:guava:32.1.1-jre.
  • 【设计模式】面向对象开发学习OOPC
  • vue、vue2、vue3
  • mamba学习1
  • 蓝桥杯填空题攻克
  • 【C++】二叉搜索树(二叉查找树、二叉排序树)详解