7.高级应用

7.1.数据库和缓存双写一致性

如果更新了数据库中的数据, 在缓存中的数据如何保持一致性?

4种方案 :

  1. 先写缓存, 再写数据库
  2. 先写数据库, 再写缓存
  3. 先删缓存, 再写数据库
  4. 先写数据库, 再删缓存

7.1.1.先写缓存, 再写数据库

先将更新的数据写入缓存, 再写入到数据

redis双写是干什么的 redis双写一致_双写一致性

可能出现的异常 : 写入缓存成功 , 但写入数据库出现异常.

这种情况在缓存中出现"假数据",

**这种方法在实际工作中通常不用

7.1.2.先写数据库, 再写缓存

先写入数据库, 再写入缓存. 在低并发环境下或者数据不会引发太大的影响, 可以使用

7.1.2.1.异常情况一

redis双写是干什么的 redis双写一致_双写一致性_02

请求a 写完数据库, 由于网络原因, 没有及时写入缓存

之后, 请求b 顺利完成写入数据库及缓存操作

此时缓存中存放的是新的数据

但请求a 在之后又完成了 ,写缓存的操作,

导致缓存中存放的是旧数据

7.1.2.2.写入缓存的操作比较复杂

如果存入缓存的数据得到比较复杂, 频繁的写入是比较消耗资源的

对于 写多 读少 的业务 就会很多问题

7.1.3.先删除缓存, 再写数据库

实现业务里, 可以不及时更新缓存, 而是等到查询里再去更新缓存

7.1.3.1.高并发下的问题

redis双写是干什么的 redis双写一致_redis双写是干什么的_03

客户b 第7步写入的数据库里的旧数据, 没有包含客户a 新操作写入的新数据

7.1.3.2.缓存双删

在 上面的基础上 , 当 客户a 完成写入数据库一段时间后 , 再次将 缓存清空.

redis双写是干什么的 redis双写一致_redis双写是干什么的_04

这里 一定要等一段时间, 因为太快, 可能出现 第9 步在 第7步之前执行 , 就失去意义了,

所以一定要等一段时间, 如: 500ms

一个新的问题, 如果 第9步 执行出错了怎么办?

7.1.4.先写数据库, 再删除缓存

有将数据写入到数据库后, 再删除缓存

但 如果有两个操作, 一个是写, 另一个是读, 情况如下

7.1.4.1.情况一 : 先写后读

先写, 后读, 但写入数据库后 , 再写入缓存时卡顿, 会导致读到旧数据

redis双写是干什么的 redis双写一致_Redis_05

这个时候有问题, 但影响不是很大

7.1.4.2.情况二 : 先读后写

这种情况通常没有问题, 如果

redis双写是干什么的 redis双写一致_双写一致性_06

缓存过期时间到了,自动失效。

请求b 查询缓存,发缓存中没有数据,查询数据库的旧值,但由于网络原因卡顿了,没有来得及更新缓存。

请求a 先写数据库,接着删除了缓存。

请求b 更新旧值到缓存中。

这种情况发生的可能性很少.

7.1.5.删除缓存失败怎么办?

7.1.5.1.定时

  1. 当用户操作写完数据库,但删除缓存失败了,需要将用户数据写入重试表中。如下图所示:
  2. 在定时任务中,异步读取重试表中的用户数据。重试表需要记录一个重试次数字段,初始值为0。然后重试5次,不断删除缓存,每重试一次该字段值+1。如果其中有任意一次成功了,则返回成功。如果重试了5次,还是失败,则我们需要在重试表中记录一个失败的状态,等待后续进一步处理。
  3. 在高并发场景中,定时任务推荐使用elastic-job。相对于xxl-job等定时任务,它可以分片处理,提升处理速度。同时每片的间隔可以设置成:1,2,3,5,7秒等。

7.1.5.2.MQ

  1. 当用户操作写完数据库,但删除缓存失败了,产生一条mq消息,发送给mq服务器。
  2. mq消费者读取mq消息,重试5次删除缓存。如果其中有任意一次成功了,则返回成功。如果重试了5次,还是失败,则写入死信队列中。
  3. 推荐mq使用rocketmq,重试机制和死信队列默认是支持的。使用起来非常方便,而且还支持顺序消息,延迟消息和事务消息等多种业务场景。

–7.1.5.3.binlog

监听binlog,比如使用:canal等中间件。