• redis探索之缓存一致性


    什么是一致性?

            一致性是指同一时刻的请求,在缓存中的数据是否与数据库中的数据相同的。

            强一致性:数据库更新操作与缓存更新操作是原子性的,缓存与数据库的数据在任何时刻都是一致的,这是最难实现的一致性。

            弱一致性:当数据更新后,缓存中的数据可能是更新前的值,也可能是更新后的值,因为这种更新是异步的。

            最终一致性:一种特殊的弱一致性,在一定时间后,数据会达到一致的状态。最终一致性是弱一致性的理想状态,也是分布式系统的数据一致性解决方案上比较推崇的。

    更新策略

            我们更新的策略对应的就是4种:

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

                    (2) 先更新数据库,再更新缓存。

                    (3) 先删除缓存,再更新数据库。

                    (4) 先更新数据库,再删除缓存。

    更新缓存还是删除缓存?

            更新缓存

                    优点:缓存中的数据会一直有效,相对删除缓存,会提高缓存命中率。

                    缺点:频繁更新缓存开销较大,遇到写多读少的业务场景并不适用。

            删除缓存

                    优点:逻辑简单,直接删除缓存中的数据就可以了。

                    缺点:删除缓存会导致下次请求缓存未命中。

            考虑到更新缓存的时间成本和代码难度,删除缓存时一个更经济实惠的方式。

    先动缓存还是先动数据库?

            我们来把4种实现方式会遇到的场景都画一遍

    先更新缓存再更新数据库-双写场景

            ①线程B更新缓存

            ②线程A更新缓存

            ③线程A更新数据库

            ④线程B更新数据库

            在这种双写的场景下,如果出现一个线程两个操作之间覆盖了另一个线程的更新操作,必然会产生一致性问题。因为如果线程A想让字段+1,线程B也想让字段+1,经过两个线程的独立计算,两个线程各计算出+1的结果,然后线程A将缓存更新为+1的结果,造成数据库与缓存不一致。

             

    先更新缓存再更新数据库-读写场景

            ①线程B查询缓存,但是缓存未命中

            ②线程B查询数据库

            ③线程A更新缓存

            ④线程A更新数据库

            ⑤线程B查到数据,更新缓存

            这种情况也会产生一致性问题。这个一致性问题就很好理解了,线程B接收到查询请求,去缓存中查找结果,但是缓存未命中,再去查数据库。线程A先去更新缓存,再去更新数据库。反过来线程B才去更新缓存,这时缓存里是线程A更新之前的结果,所以数据不一致。但是由于操作缓存比操作数据库更快,所以这种情况出现的概率较低。

    先删除缓存再更新数据库-双写场景

            ①线程B删除缓存

            ②线程A删除缓存

            ③线程A更新数据库

            ④线程B更新数据库

            在与先更新缓存的双写场景下,面临相同的状况,但是先删除缓存显然不会受困于一致性问题,因为无论什么顺序缓存都是要被删除的。

    先删除缓存再更新数据库-读写场景

            ①线程B查询缓存,未命中

            ②线程B查询数据库

            ③线程A删除缓存

            ④线程A更新数据库

            ⑤线程B更新缓存

            这种情况也不能保证一致性。线程B接收到查询请求,去缓存中查找结果,但是缓存未命中,再去查数据库。线程A先去删除缓存,再去更新数据库。反过来线程B才去更新缓存,这时缓存里是线程A更新之前的结果,所以数据不一致。

    先更新数据库再更新缓存-双写场景

            ①线程B更新数据库

            ②线程A更新数据库

            ③线程A更新缓存

            ④线程B更新缓存

            这种情况不能保证一致性。线程B先更新数据库拿到结果,线程A紧随其后更新数据库并更新缓存,线程B再去更新缓存必然导致缓存不一致。

    先更新数据库再更新缓存-读写场景

            ①线程B查询缓存,未命中

            ②线程B查询数据库

            ③线程A更新数据库

            ④线程A更新缓存

            ⑤线程B更新缓存

            这种情况无法保证一致性。线程B接收到查询请求,去缓存中查找结果,但是缓存未命中,再去查数据库。线程A先去更新缓存,再去更新数据库。反过来线程B才去更新缓存,这时缓存里是线程A更新之前的结果,所以数据不一致。但是由于操作缓存比操作数据库更快,所以这种情况出现的概率较低。

    先更新数据库再删除缓存-双写场景

            ①线程B更新数据库

            ②线程A更新数据库

            ③线程A删除缓存

            ④线程B删除缓存

            这种情况可以保证一致性。因为删除缓存的双写场景并不在乎你是先删还是后删,因为没区别,所以必然一致。

            先更新数据库再删除缓存-读写场景

            ①线程B查询缓存,未命中

            ②线程B查询数据库

            ③线程A更新数据库

            ④线程A删除缓存

            ⑤线程B更新缓存

            这种情况无法保证一致性。线程B接收到查询请求,去缓存中查找结果,但是缓存未命中,再去查数据库。线程A先去更新数据库,再去删除缓存。反过来线程B才去更新缓存,这时缓存里是线程A更新之前的结果,所以数据不一致。但是由于操作缓存比操作数据库更快,所以这种情况出现的概率较低。

    等等~秋豆麻袋,怎么四种策略都无法保证一致性。你在搞什么?

    别急,我们来回顾一下这四种情况:

            1.先更新缓存再更新数据库:在双写场景下,很容易出现一致性问题,在读写场景下,小概率出现一致性问题,所以Pass。

            2.先删除缓存再更新数据库:在双写场景下,不会出现一致性问题,在读写场景下,很容易出现一致性问题,所以Pass。

            3.先更新数据库再更新缓存:在双写场景下,很容易出现一致性问题,在读写场景下,小概率出现一致性问题,所以Pass。

            4.先更新数据库再删除缓存:在双写场景下,不会出现一致性问题,在读写场景下,小概率出现一致性问题,所以暂时保留。

    四种策略中的三种已经被Pass了,只剩下一个哥们,但是还是无法完全满足一致性,所以我们需要加点技术。

    延迟双删

            我们先知道了延迟双删的流程,再来解释一下流程。

    为什么要先删一次缓存?

            先解决更新数据库时,查询请求走到缓存,获取到过时数据问题。

    为什么第二次删除需要延时?

            为了规避线程A的第二次删除缓存操作,早于线程B更新缓存操作,从而依然存在的不一致问题。

    这个延时怎么定义呢,定义多长时间比较合适?

            这个只能根据系统的查询性能做一个具体的评估,没有一个普适的值。

    另外,延迟双删只能保障最终一致性,没法保障强一致性。

  • 相关阅读:
    Linux——进程控制之替换
    JSP SSH车间生产管理系统myeclipse开发mysql数据库MVC模式java编程网页设计
    无货源操作方式
    全员全域安全守护,蔚来ET7获Euro NCAP五星安全评级背后的硬核实力
    Ant-Design-Vue动态表头并填充数据
    这是啥SQL,室友看了人傻了
    记录一次rust浮点数计算没有java速度快的例子
    Copilot免费时代结束!正式版67元/月,学生党和热门开源项目维护者可白嫖
    【无标题】
    DQL查询数据库
  • 原文地址:https://blog.csdn.net/qq_22156459/article/details/125496995