wap网站做微信小程序,wordpress 防cc,行业电子商务网站建设,彩票网站该怎么建设Redis缓存双写一致性是指在将数据同时写入缓存#xff08;如Redis#xff09;和数据库#xff08;如MySQL#xff09;时#xff0c;确保两者中的数据保持一致性。在分布式系统中#xff0c;缓存通常用于提高数据读取的速度和减轻数据库的压力。然而#xff0c;当数据更新…Redis缓存双写一致性是指在将数据同时写入缓存如Redis和数据库如MySQL时确保两者中的数据保持一致性。在分布式系统中缓存通常用于提高数据读取的速度和减轻数据库的压力。然而当数据更新时如果没有适当的机制来同步缓存和数据库可能会导致用户读到的数据是过时的或不一致的。
1.缓存双写一致性的理解
如果redis中有数据需要和数据库中的值相同
如果redis中无数据 数据库中的值要是最新值且准备回写redis
缓存按照操作来分细分以下2种 只读缓存 读写缓存 同步直写策略写数据库之后也同步写redis缓存缓存和数据库中的数据一致 对于读写缓存来说要想保证缓存和数据库中的数据一致就要采用同步直写策略 异步缓写策略正常业务中MySQL数据变了但是可以在业务上容许出现一定时间后才作用于redis比如仓库、物流系统异常情况出现了 不得不将失败的动作重新修补有可能需要借助kafka或者RabbitMQ等消息中间件实现重试重写
采用双检加锁策略
多个线程同时去查询数据库的这条数据那么我们可以在第一个查询数据的请求上使用一个互斥锁来锁住它。其他的线程走到这一步拿不到锁就等着等第一个线程查询到了数据然后做缓存。后面的线程进来发现已经有缓存了就直接走缓存。 如果数据库里没有缓存空数据避免大量请求发生缓存穿透
2.数据库和缓存一致性的更新策略
目的:达到数据最终一致性 给缓存设置过期时间定期清理缓存并回写是保证最终一致性的解决方案。 我们可以对存入缓存的数据设置过期时间所有的写操作以数据库为准对缓存操作只是尽最大努力即可。也就是说如果数据库写成功缓存更新失败那么只要到达过期时间则后面的读请求自然会从数据库中读取新值然后回填缓存达到一致性切记要以mysql的数据库写入库为准。
上述方案和后续落地案例是调研后的主流成熟的做法但是考虑到各个公司业务系统的差距不是100%绝对正确不保证绝对适配全部情况需要自己酌情选择打法合适自己的最好。
可以停机的情况
挂牌报错凌晨升级温馨提示服务降级
单线程这样重量级的数据操作最好不要多线程
我们讨论4种更新策略
2.1 先更新数据库在更新缓存不可取
异常问题1
先更新mysql的某商品的库存当前商品的库存是100更新为99个。先更新mysql修改为99成功然后更新redis。此时假设异常出现更新redis失败了这导致mysql里面的库存是99而redis里面的还是100。上述发生会让数据库里面和缓存redis里面数据不一致读到redis脏数据
异常问题2
【先更新数据库再更新缓存】﹐A、B两个线程发起调用
【正常逻辑】
1 A update mysql 100
2 A update redis 100
3 B update mysql 80
4 B update redis 80【异常逻辑】
多线程环境下A、B两个线程有快有慢有前有后有并行
1 A update mysql 100
3 B update mysql 80
4 B update redis 80
2 A update redis 100最终结果mysql和lredis数据不一致
mysql80,redis100
2.2 先更新缓存再更新数据库不可取
不推荐业务上一般把MySQL作为底单数据库 保证最后解释
[先更新缓存再更新数据库]A、B两个线程发起调用
[正常逻辑]
1 A update redis 100
2 A update mysql 100
3 B update redis 80
4 B update mysql 80[异常逻辑]多线程环境下A. B两个线程有快有慢有并行
A update redis 100
B update redis 80
B update mysq| 80
A update mysql 100mysql100,redis80
2.3 先删除缓存在更新数据库不可取
异常问题
步骤分析先删除缓存再更新数据库
1 A线程先成功删除了redis里面的数据然后去更新mysql,此时mysql正在更新中还没有结束。(比如网络延时)
B突然出现要来读取缓存数据。2 此时redis里面的数据是空的B线程来读取先去读redis里数据(已经被A线程delete掉了)此处出来2个问题:
2.1 B从mysq|获得了旧值
B线程发现redis里没有(缓存缺失)马上去mysql里面读取从数据库里面读取来的是旧值。
2.2 B会把获得的旧值写回redis
获得旧值数据后返回前台并回写进redis(刚被A线程删除的旧数据有极大可能早被写回了)。3 A线程更新完mysql,发现redis里面的缓存是脏数据A线程直接懵逼了o(T_ .τ)o两个并发操作一个是更新操作另一个是查询操作A删除缓存后B查询操作没有命中缓存B先把老数据读出来后放到缓存中然后A更新操作更新了数据库。
于是在缓存中的数据还是老的数据导致缓存中的数据是脏的而且还一直这样脏下去了。
4总结流程:
(1)请求A进行写操作删除redis缓存后工作正在进行中更新mysql.....
A还么有彻底更新完mysql,还没commit
(2)请求B开工查询查询redis发现缓存不存在(被A从redis中删除了)
(3)请求B继续去数据库查询得到了mysq中的旧值(A还没有更新完)
(4)请求B将旧值写回redis缓存
(5)请求A将新值写入mysql数据库
上述情况就会导致不一致的情形出现。
先删除缓存再更新数据库如果数据库更新失败或超时或返回不及时导致B线程请求访问缓存时发现redis里面没数据缓存缺失B再去读取mysql时从数据库中读取到旧值还写回redis 导致A白干了
解决方案采用延时双删策略 加上sleep的这段时间就是为了让线程B能够先从数据库读取数据再把缺失的数据写入缓存然后线程A再进行删除。所以线程A sleep的时间就需要大于线程B读取数据再写入缓存的时间。这样一来其它线程读取数据时会发现缓存缺失所以会从数据库中读取最新值。因为这个方案会在第一次删除缓存值后延迟一段时间再次进行删除所以我们也把它叫做“延迟双删”。
延迟双删面试题
这个删除该休眠多久呢线程A sleep的时间就需要大于线程B读取数据再写入缓存的时间。
这个时间怎么确定呢? 第一种方法 在业务程序运行的时候统计下线程读数据和写缓存的操作时间自行评估自己的项目的读数据业务逻辑的耗时以此为基础来进行估算。然后写数据的休眠时间则在读数据业务逻辑的耗时基础上加百毫秒即可。 这么做的目的就是确保读请求结束写请求可以删除读请求造成的缓存脏数据。 第二种方法 新启动一个后台监控程序比如后面要讲解的WatchDog监控程序会加时
这种同步淘汰策略吞吐量降低怎么办 2.4 先更新数据库再删除缓存
异常问题
时间线程A线程B出现的问题t1更新数据库中的值......t2缓存立刻命中此时B读取的是缓存旧值A还没来得及删除缓存的值导致B缓存命中读到旧值t3更新缓存的数据over
先更新数据库在删除缓存假如缓存删除失败或者来不及删除导致请求再次访问redis时缓存命中读取到的是缓存的旧值。
解决方案 可以把要删除的缓存值或者是要更新的数据库值暂存到消息队列中例如使用Kafka/RabbitMQ等)。当程序没有能够成功地删除缓存值或者是更新数据库值时可以从消息队列中重新读取这些值然后再次进行删除或更新。如果能够成功地删除或更新我们就要把这些值从消息队列中去除以免重复操作此时我们也可以保证数据库和缓存的数据一致了否则还需要再次进行重试 4 如果重试超过的一定次数后还是没有成功我们就需要向业务层发送报错信息了通知运维人员。
3. 双写一致总结
方案如何选择利弊如何
在大多数业务场景下 个人建议是优先使用先更新数据库再删除缓存的方案(先更库→后删存)。理由如下:
1先删除缓存值再更新数据库有可能导致请求因缓存缺失而访问数据库给数据库带来压力导致打满mysql。
2如果业务应用中读取数据库和写缓存的时间不好估算那么延迟双删中的等待时间就不好设置。
多补充一句:如果使用先更新数据库再删除缓存的方案)
如果业务层要求必须读取一致性的数据那么我们就需要在更新数据库时先在Redis缓存客户端暂停并发读请求等数据库更新完、缓存值删除后再读取数据从而保证数据一致性这是理论可以达到的效果但实际不推荐因为真实生产环境中分布式下很难做到实时一致性一般都是最终一致性。
策略高并发多线程条件下问题现象解决方案先删除redis缓存再更新mysql无缓存删除成功但数据库更新失败Java程序从数据库中读到旧值再次更新数据库重试有缓存删除成功但数据库更新中... 有并发请求并发请求从数据库读到旧值并回写到redis导致后续都是从redis读取到旧值再次删除缓存重试先更新mysql再删除redis缓存无数据库更新成功但缓存删除失败Java程序从redis中读到旧值再次删除缓存重试有数据库更新成功但缓存删除中...... 有并发读请求并发请求从缓存读到旧值等待redis删除完成这段时间数据不一致短暂存在。
4.最后
祝愿大家国庆快乐
感谢大家请大家多多支持