Redis与DataBase保持数据一致性

文章目录

    • [1. 读取数据](#1. 读取数据)
    • [2. 写数据](#2. 写数据)
      • [2.1 先操作缓存](#2.1 先操作缓存)
      • [2.2 先操作数据库](#2.2 先操作数据库)

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

1. 读取数据

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

2. 写数据

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

先操作Redis缓存

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

先操作DataBase数据库

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

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

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

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

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

2.1 先操作缓存

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

数据不一致

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

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

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

双删)有一种解决方法:

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

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

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

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

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

2.2 先操作数据库

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

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

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

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

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

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

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

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

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

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

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

相关推荐
TG_yilongcloud5 分钟前
阿里云国际代理:怎么保障数据库在凭据变更过程中的安全与稳定?
数据库·安全·阿里云·云计算
恣艺30 分钟前
Redis有序集合(ZSet):排行榜功能的最优解,原理与实战
数据库·chrome·redis
麦兜*1 小时前
MongoDB 与 GraphQL 结合:现代 API 开发新范式
java·数据库·spring boot·mongodb·spring·maven·graphql
shan&cen1 小时前
Day02 集合 | 30. 串联所有单词的子串、146. LRU 缓存、811. 子域名访问计数
java·数据结构·算法·缓存
亭台烟雨中1 小时前
SQL优化简单思路
数据库·sql
老华带你飞3 小时前
畅阅读小程序|畅阅读系统|基于java的畅阅读系统小程序设计与实现(源码+数据库+文档)
java·数据库·vue.js·spring boot·小程序·毕设·畅阅读系统小程序
Codeking__5 小时前
mysql基础——库与表的操作
数据库·mysql
_苏沐5 小时前
cte功能oracle与pg执行模式对比
数据库·oracle
一氧化二氢.h6 小时前
通俗解释redis高级:redis持久化(RDB持久化、AOF持久化)、redis主从、redis哨兵、redis分片集群
redis·分布式·缓存
qq_508823409 小时前
金融数据库--3Baostock
数据库·金融