您的位置:首页 > 数据库

缓存与数据库的双写一致性

2019-08-13 23:59 1046 查看
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://blog.csdn.net/weixin_43268878/article/details/99486068

项目中用到其他服务一些数据,每次调用接口时候都需要发起请求到另外一个服务获取数据。跨机房和地区的调用,一些网络波动引起的间发性响应缓慢。其实类似于服务树这种较大的数据,每个人的权限变化不会太大,获取的服务树节点可以考虑用缓存,给定一个过期时间,这样极大的解决获取权限树时转圈圈的情况。

使用缓存造成的问题:

使用缓存不得不提到的一点就是缓存和数据库的一致性。是先更新数据库,再操作缓存,还是先操作缓存再更新数据库;另外,缓存是删除还是更新。在高并发场景下,数据不一致的问题暴露的可能性非常大。

几种处理方式:

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

正常情况下,一个查询请求过来,先查缓存,缓存没有再查数据库,并将查询到的数据存入缓存;每次修改数据库的同时,修改缓存。流程图如下

出现的问题:
  • 多线程问题
    请求A更新数据库
    请求B更新数据库
    请求B更新缓存
    请求A更新缓存
    很明显可以看出,最后数据库数据为B,而最后更新的缓存数据为A,这时就出现了不一致问题;

  • 性能问题
    在写操作远大于读操作时,会频繁的更新数据库,而读只需要读取最新的一次,导致了很多不必要的更新操作,尤其是缓存是需要经过大量计算的数据时,性能消耗非常严重。

二、先删除缓存,再更新数据库
出现的问题:
  • 多线程问题
    请求A删除缓存,准备更新数据库,注意此时并未更新数据库
    请求B查询缓存为空,查询数据库获得旧数据,填入缓存
    请求A更新数据库
    这时出现的情况为,缓存为旧数据,数据库为新数据。在数据采用读写分离的情况下,从库同步主库存在延时,也会造成这种脏数据。所以在使用redis作为缓存时,一定要给key一个过期时间(这里还有别的好处)。
  • 解决办法
    双删策略: 删除缓存,更新数据库,异步或者等待一定时间再次删除缓存。这种解决办法我认为并不能很好的解决此类问题,应为异步或者等待的时间很难把控。
    过期时间: 必要手段,能解决最终一致性问题,但是过程中还是会产生脏数据。
三、先更新数据库,再删除缓存

该方式在并发情况下一样会出现问题:
首先缓存失效,
线程A查询缓存为空,从数据库获得一个旧值
线程B更新数据库,此时缓存为空,删不删都无所谓
线程B执行完删除操作后,线程A这时才将旧数据填会缓存
此时可以看到,缓存依旧是旧数据,数据库已经被B更新了。有看到其他博主对此类问题的分析,首先相比于写操作,读操作的执行速度是比较快的,出现上述问题的情况会大大降低。另外,双删策略和过期时间也可以很好的减少此类问题的发生

总结:

上面都只是提到了并发情况下的不一致问题,不管哪一种方式,当出现缓存删除失败的时候,一样会有不一致情况发生。采用异步重试机制,或者是在mysql读写分离场景下使用binlog订阅机制,可以参看链接中的解决方案(https://www.cnblogs.com/rjzheng/p/9041659.html)。以上为个人对缓存和数据库一致性问题的学习。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: