Redis——缓存双写一致性问题

news/2024/12/22 2:46:00/

文章目录

  • 1、情况描述
  • 2、缓存双写一致性
    • 2.1 情况讨论
    • 2.2 双检加锁
    • 2.3 数据库缓存一致性的几种更新策略。
  • 总结

1、情况描述


默认不存在缓存雪崩和缓存击穿情况。首先Java先查询redis,若redis中存在数据则直接返回数据。若redis中不存在数据,需要查询mysql,将mysql中的数据返回,同时需要将mysql中的数据回写进入redis中。

2、缓存双写一致性

2.1 情况讨论

(1)如果redis中有数据:需要和数据库中的值相同
(2)如果redis中无数据:数据库中的值要是最新值,且准备回写redis

缓存按照格式来分:

  1. 只读缓存
  2. 读写缓存:(1) 同步读写策略:<1>写数据库后也同步写redis缓存缓存数据库中的数据一致;<2>对于读写缓存来说,要想保证缓存数据库中的数据一致,就要采用同步直写策略。(2)异步缓写策略:<1>正常业务运行中,mysql数据变动了,但是可以再业务上容许出现一定时间后才作用于redis,比如仓库、物流系统。<3>异常情况出现了,不得不将失败的动作重新休比,有可能需要借助kafka或者RabbitMQ等消息中间件,实现重试重写。

2.2 双检加锁

问题:微服务查询redis中没有数据,而mysql数据有,为保证数据双写一致性回写redis时需要注意什么。(双检加锁是什么?如何尽量避免缓存击穿?)

解决措施:多个线程同时去查询数据库的这条数据,那么我们可以在第一个查询数据的请求上使用一个互斥锁来锁住它,其他线程走到这一步拿不到锁就等着,等第一个线程查询到了数据,然后做缓存。后面的线程进来发现已经有缓存了,就直接走缓存

2.3 数据库缓存一致性的几种更新策略。

目的:达到最终一致性
缓存设置过期时间,定期清理缓存并回写,是保证最终一致性的解决方案。

我们可以对存入缓存的数据设置过期时间,所有的写操作以数据库为准,对缓存操作只是尽最大努力即可。也就是说如果数据库写成功,缓存更新失败,那么只要到达过期时间,则后面的读请求自然会从数据库中读取新值然后回填缓存,达到一致性,切记,要以mysql的数据库写入库为准

可以停机的情况:(1) 挂牌报错,凌晨升级,温馨提示,服务降级。(2) 单线程,这样重量级的数据操作最好不要多线程。

4种不同的更新策略:

  1. 先更新数据库,再更新缓存:(1) redis回写失败(更新redis失败),这导致数据库里面和缓存redis里面的数据不一致,读到redis脏数据。(2) 多线程环境下,A、B两个线程同时对mysql进行修改,可能A先修改完数据库,B接着修改数据库,然而回写redis时是B先回写,A后回写,于是导致mysql和redis数据不一致。

  2. 先更新缓存,再更新数据库:(1) 业务上一般把mysql作为底单数据库,保证最终解释。(2) 多线程环境下,A、B两个线程同时对redis进行修改,A更新redis,B紧接着更新mysql,B先更新mysql,A接着更新mysql,导致mysql和redis数据不一致。

  3. 先删除缓存,再更新数据库:(1) A线程先成功删除了redis中的数据,然后去更新mysql,此时mysql正在更新,B线程来读取缓存的数据,B读取mysql的数据,并把旧值写回mysql。A更新完mysql,发现redis里的缓存是脏数据,此时出现数据不一致的情况。
    延迟双删: 在第一次删除缓存值后,延迟一段时间再次进行删除。

    • 问题1:删除该休眠多少。线程A sleep的时间,就需要大于线程B读取数据再写入缓存的时间。确定时间的方法。<1> 在业务程序运行的时候,统计下线程读数据和写缓存的操作时间,自行评估自己的项目的数据业务逻辑的耗时,以此为基础来进行估算。然后写数据的休眠时间则在读数据业务逻辑的耗时基础上加百毫秒即可。(这么做的目的,就是确保读请求结束,写请求可以删除读请求造成的缓存脏数据)。<2> 新启动一个后台监控程序,如WatchDog监控程序,会加时。
    • 问题2:这种同步淘汰策略,吞吐量降低怎么办?解决方式: 启用一个CompletableFuture将第二次删除作为异步的。自行起一个线程,异步删除。这样,写的请求就不用沉睡一段时间后了,再返回,可以增加吞吐量。
  4. 先更新数据库,再删除缓存:线程A更新MySQL还未来得及删除Redis中数据,结果线程B就读走了Redis中的脏数据。


总结

在大多数情况下,优先使用先更新数据库,再删除缓存的方案(先更库——>后删存)。理由如下:

  1. 先删除缓存值,再更新数据库,有可能导致请求因缓存缺失而访问数据库,给数据库带来压力导致打满mysql。
  2. 如果业务应用中读取数据库和写缓存的时间不好估算,那么,延迟双删中的等待时间不好设置。

http://www.ppmy.cn/news/1557087.html

相关文章

包子凑数(2017年蓝桥杯试题H)

【问题描述】 小明几乎每天早晨都会在一家包子铺吃早餐&#xff0c;他发现这家包子铺有N种蒸笼&#xff0c;其中第i种蒸笼恰好能放Ai(i为下标)个包子。每种蒸笼都有非常多个&#xff0c;可以认为是无限笼。 每当有顾客想买X个包子。卖包子的大叔就会迅速选出若干笼包子&#xf…

Git Rebase分支合并

Git Rebase 的应用场景&#xff0c;包括如何合并多 次提交记录和分支合并 // 1.分支合并 从master切一个开发分支 feature1, git checkout -b feature1 // 2.代码开发完提交代码完后&#xff0c;回到master拉取最新代码 git checkout master git pull // 3.回到feature1 进行代…

黑客术语3

19、免杀 : 就是通过加壳、加密、修改特征码、加花指令等等技术来修改程序&#xff0c; 使其逃过杀毒软件的查杀。 20 、加壳 : 就是利用特殊的算法&#xff0c;将 EXE 可执行程序或者 DLL 动态连接库文件的 编码进行改变&#xff08;比如实现压缩、加密&#xff09;&a…

Vue 滚动条样式

模板 <template><div class"box"><div v-for"(item, index) in 100" :key"index" class"box_item">{{ item }}</div></div> </template>样式 <style lang"scss" scoped> .box …

Dhatim FastExcel 读写 Excel 文件

Dhatim FastExcel 读写 Excel 文件 一、说明1、主要特点2、应用场景 二、使用方法1、引入依赖2、Sheet 数据3、读取 Excel4、写入 Excel 一、说明 Github 地址&#xff1a;Dhatim FastExcel Dhatim FastExcel是一个高性能、轻量级的Java库&#xff0c;专门用于读取和写入Exce…

OpenHarmony-4.HDI 框架

HDI 框架 1.HDI介绍 HDI&#xff08;Hardware Device Interface&#xff0c;硬件设备接口&#xff09;是HDF驱动框架为开发者提供的硬件规范化描述性接口&#xff0c;位于基础系统服务层和设备驱动层之间&#xff0c;是连通驱动程序和系统服务进行数据流通的桥梁&#xff0c;是…

Go框架比较:goframe、beego、iris和gin

由于工作需要&#xff0c;这些年来也接触了不少的开发框架&#xff0c;Golang的开发框架比较多&#xff0c;不过基本都是Web"框架"为主。这里稍微打了个引号&#xff0c;因为大部分"框架"从设计和功能定位上来讲&#xff0c;充其量都只能算是一个组件&…

Rust中<‘_>是什么意思

在 Rust 中&#xff0c;<_> 是一种匿名生命周期的语法&#xff0c;用来简化代码中对生命周期的显式标注。 背景 在 Rust 的类型系统中&#xff0c;生命周期用于表示引用的有效范围&#xff0c;以确保引用不会超过其原始数据的生命周期。通常我们会使用显式的生命周期标…