Redis如何保证数据一致性

☆* o(≧▽≦)o *☆嗨~我是小奥🍹
📄📄📄个人博客:小奥的博客
📄📄📄CSDN:个人CSDN
📙📙📙Github:传送门
📅📅📅面经分享(牛客主页):传送门
🍹文章作者技术和水平有限,如果文中出现错误,希望大家多多指正!
📜 如果觉得内容还不错,欢迎点赞收藏关注哟! ❤️

Redis如何保证数据一致性

推荐好文 美团二面:Redis与MySQL双写一致性如何保证? - 掘金 (juejin.cn)

经典缓存策略

Cache Aside Pattern(旁路缓存)

旁路缓存模式中服务器需要同时维护数据库和缓存,并且以数据库结果为准。

旁路缓存模式,流程如下:

(1)读模式:读的时候,先读取缓存,缓存命中,直接返回数据;缓存未命中,查询数据库,写入缓存,返回数据;

(2)写模式:先更新数据库,再删除缓存。

在这里插入图片描述

适用场景

旁路缓存适合读多写少的场景,不适合写多的场景。因为当写入比较频繁时,缓存中的数据会频繁的清理,这样多缓存命中率会有一些影响。如果业务中对缓存命中率有要求,可以适用以下两种解决方案:

  • 更新数据的同时更新缓存,只是在更新缓存前加一个分布式锁,因为这样在同一时间只允许一个线程更新缓存,就不会产生并发问题了,但是会对写入的性能会有一些影响。
  • 更新数据时同时更新缓存,只是给缓存加一个较短的过期时间,这样即使出现数据不一致的情况,缓存的数据也会很快过期,对业务的影响是可以接收的。

存在的问题

(1) 初次请求数据不一定在缓存中。

解决方案:将热点数据提前预热存放入缓存中。

(2) 如果写操作频繁,那么缓存中的数据更新比较频繁,导致缓存命中率较低。

解决方案:

  • 数据库和缓存强一致:更新数据库的同时也更新缓存,不过需要使用锁或者分布式锁来保证数据更新过程中的线程安全问题。
  • 数据库和缓存最终一致:更新数据库的同时更新缓存,但是给缓存加一个比较短的过期时间,这样就可以保证即使出现数据不一致,也只是短时间内的数据不一致。

Read/Write Through Pattern(读写穿透)

读写缓存模式其实是将缓存作为主要的存储,应用的所有读写请求都是直接与缓存服务打交道的。并且数据库的数据由缓存服务来维护和更新。

(1)读模式:从缓存读取数据,缓存命中,直接返回数据;缓存未命中,查询数据库,写入缓存,返回数据;

Read Through Pattern实际上是对Cache Aside的封装,让程序代码变的更加简洁,同时也减少数据源上的负载。

(2)写模式:当发生写请求时,先查询写入的数据是否在缓存中存在,如果存在,则更新缓存中的数据,并且由缓存组件更新到数据库中,然后返回结果。如果不存在,直接更新数据库,然会返回。

在这里插入图片描述

Write behind(异步缓存写入)

异步缓存写入模式就是读写穿透的变形,不同之处在于缓存写入到数据库的时候是异步的。

Write behind只是更新缓存,不直接更新数据库,通过批量异步的方式来更新数据库。这种方式下,缓存和数据库的一致性不强,对一致性要求高的系统要谨慎使用

适用场景:

异步缓存写入模式适合写多的场景,因为发生写操作的时候,只需要更新缓存,就立马返回了。但带来的问题是,数据不是强一致性的,而且会有数据丢失的风险。

在这里插入图片描述

解决方案

要想保证缓存与数据库的双写一致,一共有以下四种策略:

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

(1)更新缓存还是删除缓存的问题

更新:① 优点:每次数据变化都能及时更新缓存,查询时不容易出现未命中的情况 ②缺点:更新的操作对性能的消耗比较大,频繁的更新缓存会影响服务器的性能,如果是写入频繁的场景,那么频繁的更新,却不访问,这就造成了性能的浪费。

删除:① 优点:操作简单,直接删除缓存即可。② 缺点:删除缓存后,下一次查询会出现未命中的情况,这时就需要去访问数据库。

整体上看,删除缓存的操作还是最优的选择

(2)先操作数据库还是先操作缓存

先删除缓存再更新数据库:如果先删除缓存,由于写入数据库的操作相对于删除缓存来说是比较慢的,所以一个新的线程来读取的时候,就会发现缓存为空,于是去数据库查询旧数据并写入缓存,此时缓存中为脏数据。当数据库数据更新完毕,Redis与MySQL中的数据就会出现不一致现象。

先更新数据库再删除缓存:如果先操作数据库,再删除缓存,如果删除缓存的线程没有成功删除,那么新的线程读取的时候就会直接读取缓存中的旧数据,也会导致数据不一致现象。需要注意的是,当数据库中有事务回滚,最好也要将缓存删除。

延时双删

延时双删:在操作数据库的前后都对缓存进行一次删除,并设定合理的超时时间。

具体步骤如下:先删除缓存,然后进行数据库数据的写入,让删除线程休眠一会,再次删除缓存。

休眠时间的确定:需要评估自己读数据的业务逻辑耗时,这样做是为了确保读请求结束,写请求可以删除读请求造成的脏数据;写请求的休眠时间一般在读数据业务上的耗时,再加上几百ms即可。

注意:双删的第一次删除的是还没有更新的旧数据,第二次删除的是因为读并发而导致缓存重新写入的脏数据。

删除缓存重试机制

如果删除失败,可以将删除失败的缓存放入消息队列中,利用消息队列的异步通知继续尝试删除缓存,如果删除仍失败,

可以增加重试的次数,但是这个次数要有一定的限制,如果超过了这个次数,要采取报错、记录日志等提醒措施。

读取binlog异步更新缓存

数据库的更新操作会记录到binlog中,所以我们可以采集binlog日志的变化,利用binlog进行增量订阅消费,将消息通过消息队列消费更新Redis中的缓存。

  • 34
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值