Redis与DataBase保持数据一致性


在我们系统中缓存最常用的策略是:服务端需要同时维系DB和Cache,并且是以DB的结果为准, Cache-Aside Pattern(缓存分离模式、旁路缓存)。

1. 读取数据

image-20240808112851354

当用户查询数据时,首先先到Redis中进行查询,如果没有命中,则到数据库中进行查询。在数据库中查询到数据后,将数据返回,并且存入到Redis中,设置过期时间,设置过期时间是为了避免长时间未使用的数据一直存放在Redis中,占用内存空间。

2. 写数据

用户在写入数据时,要操作Redis和DataBase。有以下几种操作:

先操作Redis缓存

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

先操作DataBase数据库

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

对于是更新Redis中的数据,还是删除Redis中的数据,其实更推荐是删除Redis中的数据。

因为,如果是选择修改Redis数据,而不是删除,那么如果再修改Redis数据成功后,再去更新数据库中的数据时失败,这样就会导致Redis和DataBase中的数据不一致。再或者如果先成功更新了数据库中的数据,再去更新Redis中的数据时失败了,那么下次查询到Redis中的数据虽然存在,但是为错误数据。

在更新缓存与删除缓存之中,我们确定了是使用删除Redis缓存中的数据。那么剩下的一个问题就是,先更新数据库,还是先删除Redis缓存?

对于这两种情况,我们分别进行讨论。

2.1 先操作缓存

image-20240808140156405

先操作缓存,也就是先将Redis中的数据删除,然后再更新DataBase中的数据。

数据不一致

考虑这种情况下会出现的问题:

  1. Redis中的数据成功被删除了。
  2. 在数据被写入数据库之前,此时有线程2存在,线程2读取DataBase中的数据,读取成功后存入了Redis中。
  3. 线程2从DataBase中读取数据后,线程1的新数据写入DataBase。
  4. 此时再去读取数据时,因为Redis中数据存在,所以会直接读取Redis中的数据,但是Redis中的数据是旧数据,此时就出现了数据不一致问题。

image-20240808141454554

出现上述这种情况时,该如何解决呢?

双删)有一种解决方法:

  1. Redis中的数据成功被删除了
  2. 在数据被写入数据库之前,此时有线程2存在,线程2读取DataBase中的数据,读取成功后存入了Redis中。
  3. 线程2从DataBase中读取数据后,线程1的新数据写入DataBase。
  4. 线程1的新数据写入DataBase后,再次执行将Redis中的数据删除。
  5. 此时再去读取数据时,因为Redis中数据不存在,所以会到DataBase中查询数据,再将查询到的数据存入Redis中,这样保证了数据的一致性。

image-20240808142513505

但是,这种处理方法,还是存在一种缺陷。如以下情况:

  1. 线程1更新数据,Redis中的数据成功被删除了。
  2. 线程2读取数据,先查缓存,缓存中数据不存在,查询数据库,查询到旧数据。
  3. 线程1删除Redis数据成功后,将新数据写入数据库。
  4. 线程1将新数据写入数据库后,删除Redis中的缓存数据。
  5. 线程2将查询到的旧数据返回,并将数据写入到Redis中。
  6. 此时,Redis中的数据为旧数据,又导致了Redis与数据库中数据不一致的问题。

image-20240808142952938

为了解决上述问题,我们可以将第5步操作进行延迟。在上诉例子中,也就是让线程2先将旧数据存入到Redis中,线程1再去删除Redis中的数据。这种处理方法称为延迟双删

对于先操作缓存,再操作数据库的情况中,处理数据不一致性问题,我们一般采用延迟双删的方法进行处理。

2.2 先操作数据库

image-20240808144242932

先操作数据库,也就是先更新DataBase中的数据,然后再将Redis中的数据删除。

此时,线程2存在,如果数据再Redis中存在,那么不会出现数据不一致问题。

线程2查询数据,首先查询缓存,数据存在直接返回,只是线程2可能查到的为旧数据,但是这并不影响Redis与DataBase的数据一致性。

image-20240808144818490

除了这种情况外,还有一种情况存在。Redis中不存在被更新的该数据。那么就会出现以下情况:

  1. 线程2查询数据,先查询Redis中的数据,数据不存在,到数据库中查询数据,查询到旧数据。
  2. 线程1更新数据库中的数据。
  3. 线程1更新完数据库中的数据后,执行对Redis中数据的删除操作。
  4. 线程2将查询到的旧数据存储到Redis中。此时就出现了数据不一致行问题。

image-20240808145124373

对于这种情况,我们对于图中第4步操作还是采用延迟删除进行处理。

但是这种情况下,也会存在一种问题,就是第3步操作,删除缓存失败。针对这种删除缓存失败的情况,我们可以采用删除重试机制进行解决。我们可以引入MQ消息队列。

image-20240808150609362

但是,加入了删除重试后,非业务代码和业务代码耦合度太高了。

我们想解耦的话,就可以采用另一个组件,叫做canal

canal是阿里的一款开源框架,主要用途是基于MySQL数据库增量日志解析,提供增量数据订阅和消费。

Canal提供了各种语言的客户端,当Canal监听到binlog变化时,会通知Canal的客户端。

image-20240808151104257

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

mango1698

你的鼓励是我最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值