如何保证缓存数据的一致性?

缓存查询

  1. 先查询缓存
  2. 若失败,则查询数据库
  3. 重建缓存

缓存更新

缓存更新可以沿着

  • 先更新数据库,再更新缓存
  • 先更新缓存,再更新数据库

在常规场景下,两者均可以,不会出现不一致问题。

不过如果后更新的失败了,那么先更新数据库的情况会是更优的选择。因为只是缓存出现不一致,但是相应的操作逻辑与数据库数据保持了一致,并且缓存可以通过消息队列进行重试或者是订阅mysql binlog同步数据

考虑高并发情况

先更新数据库在高并发的情况下,可能会出现

  1. 线程t1更新数据库x=1
  2. 线程t2更新数据库x=2,并且更新缓存x=2
  3. 线程t1更新缓存x=1

先更新缓存在高并发情况下也是同理

为了应对这种情况,最直接的方法是对于高并发数据x加锁,只允许同一时间一个线程更新数据x;或者设置较短的缓存时间

另外一种方法则是不更新缓存而是删除缓存,而删除缓存也分为两种情况

  • 先更新数据库,后删除缓存
  • 先删除缓存,后更新数据库

如果采用先删除缓存,那么在考虑查询重建缓存的情况下,比如

  1. 线程t1删除缓存x=1
  2. 线程t2无法从缓存查到x,于是便查询数据库x=1后重建缓存
  3. 线程t1更新数据库x=2

此时数据库x值为x,缓存值为1,还是会出现不一致的情况

为了解决这个问题,存在延迟双删的手段来进行优化,具体就是

  1. 删除缓存
  2. 更新数据库
  3. 等待
  4. 再删除缓存

但这仅仅只是降低了出现不一致的概率

而若采用先更新数据库,那么即使

  1. 线程t1更新数据库x=2
  2. 线程t2查询到缓存x后返回
  3. 线程t1删除缓存

也只是线程t2查询到不是最新的缓存,但是缓存和数据库最终会保持一致性

不过先更新数据库,后删除缓存的cache aside模式在

  1. 线程t1读取缓存x没有命中,读取数据库x=1
  2. 线程t2更新x=2,并删除缓存x
  3. 线程t1更新缓存x=1

此时读线程还是会以数据库旧值重建缓存造成不一致

不过这种情况发生的概率不高,需要读线程读缓存恰好失效,写线程速度快于读线程

Ref

  1. https://xiaolincoding.com/redis/architecture/mysql_redis_consistency.html

你可能感兴趣的:(db,哲学与架构,数据库,缓存)