如何保证Redis缓存与数据库的一致性

2022-04-12 0 298

本篇文章给大家带来了关于Redis的相关知识,其中主要介绍了如何保证redis缓存与数据库的一致性相关问题,其中包括了更新缓存和更新数据库等等,希望对大家有帮助。

如何保证Redis缓存与数据库的一致性

推荐学习:Redis学习教程

1、四种同步策略:

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

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

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

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

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

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

2.1 更新缓存

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

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

2.2 删除缓存

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

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

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

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

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

如何保证Redis缓存与数据库的一致性
如上图,是先删除缓存再更新数据库,在出现失败时可能出现的问题:

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

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

如何保证Redis缓存与数据库的一致性
如上图,是先更新数据库再删除缓存,在出现失败时可能出现的问题:

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

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

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




下面我们再将先删缓存与先更新数据库,在没有出现失败时进行对比:
如何保证Redis缓存与数据库的一致性
如上图,是先删除缓存再更新数据库,在没有出现失败时可能出现的问题:

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

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



如何保证Redis缓存与数据库的一致性
如上图,是先更新数据库再删除缓存,在没有出现失败时可能出现的问题:

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

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

最终结论:

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

4、延时双删

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

删除缓存;更新数据库;sleep N毫秒;再次删除缓存。

public void write(String key, Object data) {        Redis.delKey(key);        db.updateData(data);        Thread.sleep(1000);        Redis.delKey(key);    }

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

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

如果数据库采用的是读写分离的架构,那么又会出现新的问题,如下图:
如何保证Redis缓存与数据库的一致性
此时来了两个请求,请求 A(更新操作) 和请求 B(查询操作)

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

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

删除失败了怎么办?

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

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

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

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

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

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

推荐学习:Redis教程

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

【声明:根据2013年1月30日《计算机软件保护条例》2次修订第17条规定: 为了学习和研究软件内含的设计思想和原理,通过安装、显示、传输或者存 储软件等方式使用软件的,可以不经软件著作权人许可,不向其支付报酬! 鉴于此,也希望大家按此说明研究软件!】
本站所有源码尽量保证原汁原味,如有特殊情况会作出声明及标注,网站资源不做任何二次加密(原版加密除外,不影响程序使用的不会做解密处理),方便您更好的学习参考。 在您的能力范围内,为了大环境的良性发展,请尽可能的选择正版资源。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。

即刻码站__国内靠谱的站长资源下载平台 php教程 如何保证Redis缓存与数据库的一致性 https://www.jike1995.com/33725.html

常见问题
  • 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用
查看详情
  • 最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度
查看详情

相关文章

发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务