db与缓存不一致的问题

db和缓存的先后问题在日常代码中是很常见的,现在来分析一下每种情景的细节

首先排除定时删除缓存的这种操作,因为这样不会有双写问题。

因为不管有多少线程,

(1)缓存没失效的时候都是走的缓存;

(2)然后缓存在某一时刻失效时,线程A先到,发现缓存失效,从DB里面去读,此时读的都是最新的值。

(3)即使在(2)中,正好遇到别的线程B更新db的值,若A在更新之前读,并写入缓存,那这一波仍然用的老值;若A在更新之后读,则赶上了更新这一波,全部用的新值。

 

一般有四种策略

 

更新缓存:

1.先更新数据库,再更新缓存。(2个更新线程)

     (1)A先更新db

     (2)B更新了db

     (3)B更新了缓存

     (4)A更新了缓存

   显然,这会导致后续的读缓存都是读的脏数据。

2.先更新缓存,再更新数据库。(2个更新线程)

    (1)A先更新缓存

    (2)B再更新缓存。

    (3)B更新了db

    (4)A更新db

    不管(3)(4)谁先走,缓存都是脏数据,显然是不合适的

 

删除缓存:

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

(2个更新线程)

    (1)A先更新db

    (2)B更新了db

    (3)A删除了缓存

    (4)B删除了缓存

 这种模式下是不会有问题的,不管(3)(4)谁先走。

还有一种: 一个写,一个读

    (1)A先更新db

    (2)B读了缓存

    (3)A删除缓存

  这种情况,因为更新DB的时间较长,有可能有限的若干线程读了缓存脏数据。之后的线程发现缓存失效后,读db新的值然后写到缓存中。然后一致读缓存新值。所以某种意义上讲不算大问题。

 

有问题的情况:(读、写)

    (1)缓存失效了

    (2)B读了db的旧值

    (3)A更新了db

    (4)A删除了缓存

    (5)B用旧值写入了缓存

这种情况是类似1,2的严重不一致问题。之后的线程读的都是旧缓存。

  但是这种情况,除非(2)的读操作比(3)的更新操作还慢,才能导致(4)在(5)前面,所以概率比较小。

 

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

有一种极端情况(可以忽略):

    (1)A删除了缓存

    (2)B删除了缓存

    (3)B更新了db

    (4)A更新了db

 此时db更新的是旧值,这种情况基本不存在,因为缓存操作时间较短,而且(1)(2)都是删缓存操作,A一般情况下可定是比B先更新db。

 

有问题的情况:

    (1)A删除了缓存

    (2)B查询发现缓存失效

    (3)然后B查询db旧值

    (4)写旧值到缓存中

    (5)A更新了db的值 

    因为更新的时间比查询的时间长,所以A更新db之前,缓存已经被写了旧值,会导致严重的不一致性。

 

解决方法:

1.延时双删策略,延时几秒后再次删除缓存。

(1)删除缓存  (2)更新数据 (3)sleep(1000)(4)删除缓存

  方式3中的问题也可以用这种思路解决,保证缓存是被删掉的。如果觉得延时影响了吞吐量,可以再启一个线程异步删除。

 

2.如果删除缓存失败了怎么办?

(1)  可以设定次数,循环 删除

(2)用消息中间件,如果删除失败就把key发送到消息队列中,然后自己消费自己的消息,确定缓存被删除。

(3)用mysql的canal中间件去订阅binlog日志,从binlog日志中提取需要的数据以及key,然后进行删除。

(4)可以把(2)和(3)结合起来,先去查binlog,然后还失败的发到队列里面去。

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值