• 缓存与数据库的一致性思考


    时隔两年,重新启动这个博客。熟悉又有点陌生,这两年我的技术方向有了很大改变,但由于一直在使用为知笔记,因此这些改变没有提现在本博客上。之所以重启这个博客,主要是因为博客是一个开放的东西,可以带来一些交流,而笔记则是个人的东西,缺少思维碰撞。闲话少叙,这就开始。

    问题:怎么保持缓存与数据库一致?

    要解答这个问题,我们首先来看不一致的几种情况。我将不一致分为三种情况:

    1. 数据库有数据,缓存没有数据;

    2. 数据库有数据,缓存也有数据,数据不相等;

    3. 数据库没有数据,缓存有数据。

    在讨论这三种情况之前,先说明一下我使用缓存的策略,也是大多数人使用的策略,叫做 Cache Aside Pattern。酷壳里的 缓存更新的套路 一文,很值得一读,我的策略也是从他那学来的。

    简而言之,就是

    1. 首先尝试从缓存读取,读到数据则直接返回;如果读不到,就读数据库,并将数据会写到缓存,并返回。

    2. 需要更新数据时,先更新数据库,然后把缓存里对应的数据失效掉(删掉)。

    读的逻辑大家都很容易理解,谈谈更新。如果不采取我提到的这种更新方法,你还能想到什么更新方法呢?大概会是:先删除缓存,然后再更新数据库。这么做引发的问题是,如果A,B两个线程同时要更新数据,并且A,B已经都做完了删除缓存这一步,接下来,A先更新了数据库,C线程读取数据,由于缓存没有,则查数据库,并把A更新的数据,写入了缓存,最后B更新数据库。那么缓存和数据库的值就不一致了。

    另外有人会问,如果采用你提到的方法,为什么最后是把缓存的数据删掉,而不是把更新的数据写到缓存里。这么做引发的问题是,如果A,B两个线程同时做数据更新,A先更新了数据库,B后更新数据库,则此时数据库里存的是B的数据。而更新缓存的时候,是B先更新了缓存,而A后更新了缓存,则缓存里是A的数据。这样缓存和数据库的数据也不一致。

    按照我提到的这种更新缓存的策略,理论上也是有不一致的风险的,酷壳的文章有提到,只不过概率很小,我们暂时可以不考虑,后面我们有其他手段来补救。

    讨论完使用缓存的策略,我们再来看这三种不一致的情况。

    1. 对于第一种,在读数据的时候,会自动把数据库的数据写到缓存,因此不一致自动消除

    2. 对于第二种,数据最终变成了不相等,但他们之前在某一个时间点一定是相等的(不管你使用懒加载还是预加载的方式,在缓存加载的那一刻,它一定和数据库一致)。这种不一致,一定是由于你更新数据所引发的。前面我们讲了更新数据的策略,先更新数据库,然后删除缓存。因此,不一致的原因,一定是数据库更新了,但是删除缓存失败了。

    3. 对于第三种,情况和第二种类似,你把数据库的数据删了,但是删除缓存的时候失败了。

    因此,最终的结论是,需要解决的不一致,产生的原因是更新数据库成功,但是删除缓存失败。

    我想出的解决方案大概有以下几种:

    1. 对删除缓存进行重试,数据的一致性要求越高,我越是重试得快。

    2. 定期全量更新,简单地说,就是我定期把缓存全部清掉,然后再全量加载。

    3. 给所有的缓存一个失效期。

    第三种方案可以说是一个大杀器,任何不一致,都可以靠失效期解决,失效期越短,数据一致性越高。但是失效期越短,查数据库就会越频繁。因此失效期应该根据业务来定。

  • 相关阅读:
    查看yarn当前执行任务列表
    小白学开发(iOS)OC_ 字符串的获取 (2015-08-11)
    【leetcode】Search in Rotated Sorted Array (hard)
    【leetcode】Spiral Matrix II (middle)
    【leetcode】Spiral Matrix(middle)
    【leetcode】Reverse Bits(middle)
    【leetcode】Number of 1 Bits (easy)
    【leetcode】Set Matrix Zeroes(middle)
    【leetcode】Reorder List (middle)
    【leetcode】Reverse Linked List II (middle)
  • 原文地址:https://www.cnblogs.com/johnsblog/p/6426287.html
Copyright © 2020-2023  润新知