Redis之缓存一致性

前面的文章我们已经介绍过了缓存的三种异常(缓存击穿、缓存穿透和缓存雪崩)以及如何解决。这篇文章我们来讲解一下缓存一致性问题。

什么是缓存一致性问题?

一般情况下,Redis是用作应用程序和数据库之间读操作的缓存,主要目的是减少数据库的IO,还可以提升数据的IO性能。

当应用程序要去读取一个数据时,首先会尝试从Redis中读取,如果命中了就直接返回结果,如果没有命中,就从数据库中查询,查询到数据后再把这个数据缓存到Redis中并返回结果。

在这样的架构中,会出现一个问题,就是一份数据,同时保存在数据库和Redis中,当数据发生变化的时候,需要同时更新Redis和MySql,由于更新是有先后顺序的,这种两边写入的情况下,并不能像单纯数据库操作一样,可以满足ACID的特性。因此,就可能存在一方更新成功而另一方更新失败的情况,从而出现缓存和数据库中数据不一致的问题,也就是缓存一致性问题。

如何解决?

我们先将出现缓存一致性问题的场景进行分类,分别是先删除缓存,再更新数据库先更新数据库,再删除缓存两种场景。

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

先删除缓存,此时数据库还没有及时更新成功,此时如果有另一个线程来读取缓存中的值,由于缓存被删除,这条线程就会去数据库中进行读取,但是数据库此时还没有更新,所以读取到的还是旧值,缓存不一致问题就发生了。

解决方法------延时双删

延时双删的方案的思路是,为了避免更新数据库的时候,其他线程从缓存中读取不到数据,就在更新完数据库之后,再Sleep一段时间,然后再次删除缓存,具体的流程如下:

(1)线程1删除缓存,然后去更新数据库;

(2)线程2来读缓存,发现缓存已经被删除,所以直接从数据库中读取数据,这时候由于数据库还没有及时更新,所以读取到的还是旧值,然后把旧值写入了缓存;

(3)线程1,根据估算的时间来进行Sleep操作,Sleep醒来后,线程1再次删除缓存;

(4)如果此时还有其他线程来读取缓存的话,就会再次从数据库中读取到最新值。

存在的问题

如何取Sleep的时间其实很难进行衡量,如果时间取的过短,很有可能数据库还没有更新的时候双删操作就完成了,缓存就会一直保持旧值;如果时间取的过长,就会导致更新后的缓存又被删除了,造成重复写入缓存。

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

如果将上面的场景反过来,这个产生的问题会更加明显,就是更新数据库的操作成功了,但是缓存删除操作失败了,或者没有来得及进行缓存删除操作,这个时候其他线程去访问缓存,拿到的还是原来的旧值,缓存一致性问题出现了。

解决方案------消息队列

先更新数据库,成功之后往消息队列中发送消息,消费到消息后再删除缓存,借助消息队列的重试机制来实现缓存的删除,最终达到数据的一致性。

存在的问题

(1)引入消息中间件之后,问题更加复杂了,怎么保证消息不丢失更加麻烦;

(2)就算不考虑消息丢失的问题,由于缓存的删除不是立即进行的,所以不能保证缓存一致性的高时效性,只能保证最终的缓存一致性。

进阶版的消息队列解决方案

为了解决缓存一致性的问题单独引入一个消息队列,太复杂了。其实,一般的大公司本身都会有监听binlong消息的消息队列存在,主要是为了做一些核对的工作。

可以借助监听binlog消息的消息队列来做删除缓存的操作。这样做的好处是,不用引入一个新的消息队列到业务代码中,同时保证了高可用。

当然问题还是上面提到的问题,不过在并发量不是特别高的场景下,这种做法的实时性和一致性都还算可以接受的。

为什么是删除缓存而不是更新缓存?

就以先更新数据库,再删除缓存为例,如果不是删除缓存而是更新缓存的话,如果数据库在1个小时内更新了2000次,那么相对的缓存也要跟着更新2000次,但是在这1个小时内,缓存只被访问到了1次,那么这2000次的更新操作和1次的删除操作相比,显然是删除操作的性能更高一些。换句话说,缓存不需要实时更新,只有在用到缓存的时候在去数据库中读取新数据即可。

这篇文章我们讲解了Redis的缓存一致性问题和解决方案,大家有什么问题或者勘误的话可以在评论区留言,笔者看到都会回复的。

相关推荐
花好月圆春祺夏安4 分钟前
基于odoo17的设计模式详解---备忘模式
数据库·设计模式
有想法的py工程师32 分钟前
PostgreSQL 查询库中所有表占用磁盘大小、表大小
数据库·postgresql
R_AirMan2 小时前
深入浅出Redis:一文掌握Redis底层数据结构与实现原理
java·数据结构·数据库·redis
Hello.Reader2 小时前
RedisJSON 内存占用剖析与调优
数据库·redis·缓存
wkj0013 小时前
php 如何通过mysqli操作数据库?
android·数据库·php
九皇叔叔5 小时前
【9】PostgreSQL 之 vacuum 死元组清理
数据库·postgresql
风雅的远行者5 小时前
mysql互为主从失效,重新同步
数据库·mysql
晨岳5 小时前
CentOS 安装 JDK+ NGINX+ Tomcat + Redis + MySQL搭建项目环境
java·redis·mysql·nginx·centos·tomcat
执笔诉情殇〆5 小时前
前后端分离(java) 和 Nginx在服务器上的完整部署方案(redis、minio)
java·服务器·redis·nginx·minio
宇钶宇夕6 小时前
S7-1200 系列 PLC 中 SCL 语言的 PEEK 和 POKE 指令使用详解
运维·服务器·数据库·程序人生·自动化