了解一下数据库跟缓存的双写一致性

1. 关于数据一致性理解

	为了系统性能一般都会引入缓存机制,比如 Redis。
	这种情况下当用户读数据时一般会按照如下流程:

在这里插入图片描述
但是对于数据库的更新,怎么操作才算合理的操作呢?

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

2. 一致性解决办法

2.1、缓存TTL

简单直接又暴力的方法,如果有些数据不重要,我们读完一次数据到缓存后设置个TTL即可,等待超时后缓存自动从数据库读取下数据。

2.2、先更新数据库,再更新缓存

假设现在有两个请求(线程)A、B,都要改变数据库的数据。A请求将age = 14,B请求将age = 12。下图是正常执行跟非正常执行情况:
在这里插入图片描述
由上图可知,出现网络问题可以导致数据库与缓存不一致。因此这个方法是不可取的。出现如下场景也不可取:

  • 写场景多,但是读场景少的时候,由于和数据库关联比较多,和缓存关联比较少,但是缓存却经常被更新。
  • 如果缓存的数据是被经过大量复杂计算后的数据,那么每次更新缓存都要进行大量的计算,影响系统性能。

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

假设现在有两个请求A、B。A请求去写数据,B请求去读数据。如果先删除缓存,再更新数据库,就会出现如下情况:
在这里插入图片描述
由上图可知,就会出现缓存是脏数据。那么有办法解决吗?,答案是:有的
我们可以采用延迟双删策略,代码如下:

public void write(String key,Object value){
	redis.delKey(key);
	db.updateValue(value);
	Thread.sleep(1000); // 再次删除
	redis.delKey(key);
}
  1. 先淘汰缓存;
  2. 再写数据库(这两步和原来一样);
  3. 休眠1秒;
  4. 再次淘汰缓存。

sleep的时间要根据业务数据逻辑耗时而定,反正目的是确保读请求结束,写请求可以删除读请求造成的缓存脏数据。这么做,可以将1秒内所造成的缓存脏数据,再次删除!

可是其实第二次删除还是有不妥的地方:

  • 二次删除前面涉及到休眠,可能导致系统性能降低,可以采用异步的方式,再起一个线程来进行异步删除。
  • 如果二次删除失败了,还是会导致缓存脏数据存在的啊!

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

假如此时A、B两个线程同时请求,正常来讲不管你是读写分离还是单机版,读一般比写快。那删除缓存一般是有效的。
在这里插入图片描述
该方案相比先删除缓存再更新数据库还是稳妥些的,但是也不是万无一失的。不管是先删缓存再更新数据库还是先更新数据库再删缓存,如果删除缓存失败了都会导致缓存跟数据不一致问题!

2.5、消息队列确保消息删除

通过消息队列的确认消费机制来删除缓存。
在这里插入图片描述
缺点也很明显:

  • 对业务线代码造成大量的侵入,引入了中间件。
  • 消息的延迟删除也会造成短暂的不一致。

2.6、专门程序+消息队列确保消息删除

该方案启动一个订阅程序去订阅数据库的binlog,获得需要操作的数据。在应用程序中,另起一段程序,获得这个订阅程序传来的信息,进行删除缓存操作。
在这里插入图片描述
订阅binlog程序在mysql中有现成的中间件叫canal,可以完成订阅binlog日志的功能。

3. 总结

分析后你会发现数据更新时缓存是删除不是更新,而删除缓存一般有三种方法:

  • 如果缓存数据不敏感,直接给缓存设置TTL即可。
  • 先删缓存再更新数据库,此时需配合延时双删技术,但可能导致二次删除失败。
  • 先更新数据库再删缓存,此时需配合binlog消费 + 消息队列来实现。

文章参考

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值