redis中的缓存一致性

问题:当我们的数据库中的数据更新了,但是我们缓存中的数据也要进行相应的更新,这个时候我们应该怎样做?

通常都是使用双写方案来实现的。

  1. 先更新数据库,再更新缓存
  2. 先删除缓存,再更新数据库
  3. 先更新数据库,再删除缓存

为什么没有先更新缓存,再更新数据库?

1、先更新数据库,再更新缓存。

我们可以想象一下,当我们有两个线程进行操作的时候,假设我们想要线程B中的数据。

  1. 线程A先更新了数据库中的数据,
  2. 线程B更新了数据库的数据。
  3. 线程B更新了缓存,
  4. 线程A更新了缓存。

这样发现问题了没?

我们最后的数据是脏数据,也就是不是我们想要的数据,我们想要的是线程B中的数据。这就不合适了。

还有原因:在写多读少的情况下,我们拼命的更新数据库中的数据,导致缓存也是在更新,但是我们读的情况比较少,这样就会出现浪费性能。

2、删除缓存,在更新数据库

该方案会导致不一致的原因是。同时有一个请求A进行更新操作,另一个请求B进行查询操作。那么会出现如下情形:

  1. 请求A进行写操作,删除缓存
  2. 请求B查询发现缓存不存在
  3. 请求B去数据库查询得到旧值
  4. 请求B将旧值写入缓存
  5. 请求A将新值写入数据库

上述情况就会导致不一致的情形出现。而且,如果不采用给缓存设置过期时间策略,该数据永远都是脏数据。那么,如何解决呢?

采用延时双删策略。伪代码如下:

public void doubleDelay(String key, Object data) {
 
    redis.delKey(key);
 
    db.updateData(data);
 
    Thread.sleep(1000);//1s过后删除数据,就会只有1s的脏数据
 
    redis.delKey(key);
 
}

但是在读写分离的数据库中,刚写的数据更新到了主数据库,但是还没完成主从复制,这个时候就会有问题,比如:

  1. 线程A更新数据库,然后删除缓存。
  2. 线程B查看缓存没有,去从数据库中查找数据,但是这个时候数据库中的数据还没发生主从复制,数据是脏数据。
  3. 线程B更新缓存,
  4. 线程A的主从复制完成,数据库中的数据是A更新的数据。

这种也是可以使用双删延时来进行的,只是在原来的基础上进行增加一些主从复制的时间。

这样的话吞吐量就下降了,我们是不是可以开一个线程去延迟删除(第二次删除的时候),如果第二次删除失败了呢?

3、先更新数据库,再删缓存

  1. 失效:应用程序先从cache取数据,没有得到,则从数据库中取数据,成功后,放到缓存中。
  2. 命中:应用程序从cache中取数据,取到后返回。
  3. 更新:先把数据存到数据库中,成功后,再让缓存失效。

一个请求A做查询操作,一个请求B做更新操作,那么会有如下情形产生:

  1. 缓存刚好失效
  2. 请求A查询数据库,得一个旧值
  3. 请求B将新值写入数据库
  4. 请求B删除缓存
  5. 请求A将查到的旧值写入缓存

但是这个发生的概率一般比较低,我们还是可以使用其他手段来防御的。比如你删除缓存的时候我们可以使用异步删除。那是不是就没有问题了。

有的,这也是缓存更新策略(2)和缓存更新策略(3)都存在的一个问题,如果删缓存失败了怎么办,那不是会有不一致的情况出现么。比如一个写数据请求,然后写入数据库了,删缓存失败了,这会就出现不一致的情况了。这也是缓存更新策略(2)里留下的最后一个疑问。

我们通常有两种方法

方案1:

  1. 更新数据库数据;
  2. 缓存因为种种问题删除失败
  3. 将需要删除的key发送至消息队列
  4. 自己消费消息,获得需要删除的key
  5. 继续重试删除操作,直到成功

然而,该方案有一个缺点,对业务线代码造成大量的侵入。于是有了方案二,在方案二中,启动一个订阅程序去订阅数据库的binlog,获得需要操作的数据。在应用程序中,另起一段程序,获得这个订阅程序传来的信息,进行删除缓存操作

方案二:

  1. 更新数据库数据
  2. 数据库会将操作信息写入binlog日志当中
  3. 订阅程序提取出所需要的数据以及key
  4. 另起一段非业务代码,获得该信息
  5. 尝试删除缓存操作,发现删除失败
  6. 将这些信息发送至消息队列
  7. 重新从消息队列中获得该数据,重试操作。

上述的订阅binlog程序在mysql中有现成的中间件叫canal,可以完成订阅binlog日志的功能

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值