怎么保证缓存和数据库的一致性?

一、前言

在进行项目开发中,我们为了提高接口的性能,通常会上缓存,不管是本地缓存还是分布式缓存。使用缓存确实能提高我们接口的响应速度,但是怎么保证缓存和数据库的一致性又是我们比较关注的一个点。因为缓存数据不一致,最直接的结果是会导致业务逻辑错误,这是我们不能接受的。

二、缓存与数据库不一致的场景

1. 数据库操作成功,缓存操作失败

数据库的操作和缓存的操作是存在一定的时间差,比如删除一条数据时,通常是先在数据库中删除,然后再缓存中删除。这两个操作是没有办法保证原子性的,也就是有可能一个操作成功,一个操作失败。这样必然会存在不一致的情况。

2.两个线程同时写数据库,然后更新缓存(写-写并发)
线程1 线程2
更新数据库,更新为10
更新数据库,更新为60
更新缓存,更新成60
更新缓存,更新成10(出现不一致)
3. 一个线程读数据,写缓存,一个线程写数据,更新缓存(读-写并发)

这种场景下,读线程先查缓存,发现没有,然后去读数据库,然后把数据写到缓存中。这期间可能有一个写线程,更新了数据库和缓存。

读线程 写线程
读缓存,缓存中没有数据
读数据库,读到的结果为30
更新数据库和缓存,更新为10
写缓存,更新为30(出现不一致)

三、如何解决不一致的问题

为了保证数据库和缓存的一致性,首先是要数据库和缓存的双写,再配合上一下过期策略,定期清空缓存中的数据,然后去加载最新的数据。

下面是常用的解决缓存不一致的方案:

1.设置缓存过期时间

非热点数据,我们需要设置过期时间,通过设置缓存的过期时间,可以减少缓存和数据库不一致的时间窗口。缓存过期后,从数据库重新加载最新的数据,确保数据的新鲜度和准确性。

2.先更新数据库后删除缓存--并发不高

在更新数据的时候最简单的处理方式是删除缓存。有很多是更新缓存,更新缓存也可以,但不是最优的选择。因为,很多场景下缓存中不仅仅是一个简单的字符串,有可能是一个复杂的数据结构(JSON或Map)。

如果是更新的化,需要先把缓存查出来,进行反序列化,然后取出其中一个字段进行更新,再进行序列化,最后再更新到缓存中去。可以看到更新是很复杂的,也容易出错。另外一方面,如果两个线程同时更新,可能会出现不一致的情况。所以说,删除缓存是最优的选择。

3.延迟双删--高并发场景

所谓的延迟双删,是先删除缓存,再更新数据库,最后再删一次数据库。

对比上面的方案,在更新数据库之前多了一次删除缓存。那么为什么要多一次删除缓存?

这是因为数据库的操作和缓存的操作不是一个原子操作,可能会存在失败的情况。如果缓存删除成功了,数据库更新成功没有问题,可能会增加cache miss,但是如果数据库更新成功,而缓存删除失败了,就会产生不一致的情况。高并发场景下,其它的线程来读取的都是脏数据,不是最新的数据。

所以,第一次删除缓存,是为了避免缓存和数据库操作不能作为一个原子操作,而带来的不一致的问题,可能产生脏读的问题。

那么为什么第二次删除需要延迟删除呢?

其实在高并发场景下,我们先删除了缓存,会放大前面提到的读写并发导致不一致的场景。就是写线程删除缓存后,又被一个读线程写入旧值了。那么我们就需要在写线程删缓存,写数据库后,延迟一段时间再删除一次缓存,来解决这个问题。一般延迟1~2s删除即可。

4.监听数据库binlog,异步删除缓存

前面几种方案,在我们的业务代码中会耦合很多删除缓存的逻辑,使得我们维护变得困难,也会降低我们系统的吞吐量和响应速度。

所以,这种方案在代码中只会有我们的业务逻辑,不用关心缓存的清理,可以提高系统的性能 。但是,这种方案是异步删除缓存的,通常是监听到binglog发生变化,然后使用消息中间件发送消息,有一个应用监听消息去删除对应的缓存。异步就意味着会有一定的延迟,我们需要评估能不能接受一定的延迟。

相关推荐
小蜗牛慢慢爬行3 分钟前
有关异步场景的 10 大 Spring Boot 面试问题
java·开发语言·网络·spring boot·后端·spring·面试
小汤猿人类10 分钟前
open Feign 连接池(性能提升)
数据库
新手小袁_J28 分钟前
JDK11下载安装和配置超详细过程
java·spring cloud·jdk·maven·mybatis·jdk11
呆呆小雅28 分钟前
C#关键字volatile
java·redis·c#
Monly2129 分钟前
Java(若依):修改Tomcat的版本
java·开发语言·tomcat
阳冬园31 分钟前
mysql数据库 主从同步
数据库·主从同步
Ttang2331 分钟前
Tomcat原理(6)——tomcat完整实现
java·tomcat
钱多多_qdd42 分钟前
spring cache源码解析(四)——从@EnableCaching开始来阅读源码
java·spring boot·spring
waicsdn_haha44 分钟前
Java/JDK下载、安装及环境配置超详细教程【Windows10、macOS和Linux图文详解】
java·运维·服务器·开发语言·windows·后端·jdk
Q_19284999061 小时前
基于Spring Boot的摄影器材租赁回收系统
java·spring boot·后端