redis探索之缓存一致性

什么是一致性?

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

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

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

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

更新策略

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

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

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

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

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

更新缓存还是删除缓存?

        更新缓存

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

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

        删除缓存

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

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

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

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

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

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

redis探索之缓存一致性_第1张图片

        ①线程B更新缓存

        ②线程A更新缓存

        ③线程A更新数据库

        ④线程B更新数据库

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

         

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

redis探索之缓存一致性_第2张图片

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

        ②线程B查询数据库

        ③线程A更新缓存

        ④线程A更新数据库

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

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

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

redis探索之缓存一致性_第3张图片

        ①线程B删除缓存

        ②线程A删除缓存

        ③线程A更新数据库

        ④线程B更新数据库

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

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

redis探索之缓存一致性_第4张图片

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

        ②线程B查询数据库

        ③线程A删除缓存

        ④线程A更新数据库

        ⑤线程B更新缓存

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

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

redis探索之缓存一致性_第5张图片

        ①线程B更新数据库

        ②线程A更新数据库

        ③线程A更新缓存

        ④线程B更新缓存

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

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

redis探索之缓存一致性_第6张图片

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

        ②线程B查询数据库

        ③线程A更新数据库

        ④线程A更新缓存

        ⑤线程B更新缓存

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

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

redis探索之缓存一致性_第7张图片

        ①线程B更新数据库

        ②线程A更新数据库

        ③线程A删除缓存

        ④线程B删除缓存

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

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

redis探索之缓存一致性_第8张图片

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

        ②线程B查询数据库

        ③线程A更新数据库

        ④线程A删除缓存

        ⑤线程B更新缓存

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

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

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

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

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

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

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

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

redis探索之缓存一致性_第9张图片

延迟双删

redis探索之缓存一致性_第10张图片

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

为什么要先删一次缓存?

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

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

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

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

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

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

你可能感兴趣的:(redis,redis)