1. 先删缓存,再更新数据库
对于保持缓存与数据库的数据一致性,有一种常见的解决方法就是先删除缓存,再更新数据库。这种方案在一般情况下不会出现问题,但在读写并发的情况下就可能出现问题。
线程A先去删除缓存,但还没来得及去更新数据库。这时候线程B来了,先去缓存中查,缓存不存在,就去数据库中查。这样的话查询到的值就是旧值。
2. 延时双删
对于这个问题相对来讲比较好的解决方案就是延时双删。
延时双删的方案的思路是,为了避免更新数据库的时候,其他线程从缓存中读取不到数据,就在更新完数据库之后,再sleep一段时间,然后再次删除缓存。
sleep的时间要对业务读写缓存的时间做出评估,sleep时间大于读写缓存的时间即可。
流程如下:
- 线程1删除缓存,然后去更新数据库
- 线程2来读缓存,发现缓存已经被删除,所以直接从数据库中读取,这时候由于线程1还没有更新完成,所以读到的是旧值,然后把旧值写入缓存
- 线程1,根据估算的时间,sleep,由于sleep的时间大于线程2读数据 + 写缓存的时间,所以缓存被再次删除
- 如果还有其他线程来读取缓存的话,就会再次从数据库中读取到最新值
其实就是将在这sleep时间内缓存可能产生的脏数据删除(确保写请求能删除掉读请求可能产生的缓存脏数据)
3. 消息队列
先更新数据库,成功后往消息队列发消息,消费到消息后再删除缓存,借助消息队列的重试机制来实现,达到最终一致性的效果。但是这种解决方案又会带来消息队列的一系列问题,如怎么保证消息不丢失、消息的堆积问题等等。而且采用消息队列的话必然会带来一定的延迟,这就会带来短暂的不一致性。
4. 设置过期时间
每次放入缓存的时候设置一个过期时间,接下来的操作只修改数据库,不操作缓存,等到缓存过期了再去数据库中读取。对于一致性要求不高的情况,可以使用这种方案。
总结:先删除缓存,再更新数据库,解决方案是延时双删。先更新数据库,再删除缓存,解决方案是引入消息队列或者其他的binlog同步。对于一致性要求不高的情况下,可以使用设置过期时间的方案。
网友评论