缓存查询
- 先查询缓存
- 若失败,则查询数据库
- 重建缓存
缓存更新
缓存更新可以沿着
- 先更新数据库,再更新缓存
- 先更新缓存,再更新数据库
在常规场景下,两者均可以,不会出现不一致问题。
不过如果后更新的失败了,那么先更新数据库的情况会是更优的选择。因为只是缓存出现不一致,但是相应的操作逻辑与数据库数据保持了一致,并且缓存可以通过消息队列进行重试或者是订阅mysql binlog同步数据
考虑高并发情况
先更新数据库在高并发的情况下,可能会出现
- 线程t1更新数据库x=1
- 线程t2更新数据库x=2,并且更新缓存x=2
- 线程t1更新缓存x=1
先更新缓存在高并发情况下也是同理
为了应对这种情况,最直接的方法是对于高并发数据x加锁,只允许同一时间一个线程更新数据x;或者设置较短的缓存时间
另外一种方法则是不更新缓存而是删除缓存,而删除缓存也分为两种情况
- 先更新数据库,后删除缓存
- 先删除缓存,后更新数据库
如果采用先删除缓存,那么在考虑查询重建缓存的情况下,比如
- 线程t1删除缓存x=1
- 线程t2无法从缓存查到x,于是便查询数据库x=1后重建缓存
- 线程t1更新数据库x=2
此时数据库x值为x,缓存值为1,还是会出现不一致的情况
为了解决这个问题,存在延迟双删的手段来进行优化,具体就是
- 删除缓存
- 更新数据库
- 等待
- 再删除缓存
但这仅仅只是降低了出现不一致的概率
而若采用先更新数据库,那么即使
- 线程t1更新数据库x=2
- 线程t2查询到缓存x后返回
- 线程t1删除缓存
也只是线程t2查询到不是最新的缓存,但是缓存和数据库最终会保持一致性
不过先更新数据库,后删除缓存的cache aside模式在
- 线程t1读取缓存x没有命中,读取数据库x=1
- 线程t2更新x=2,并删除缓存x
- 线程t1更新缓存x=1
此时读线程还是会以数据库旧值重建缓存造成不一致
不过这种情况发生的概率不高,需要读线程读缓存恰好失效,写线程速度快于读线程
Ref
- https://xiaolincoding.com/redis/architecture/mysql_redis_consistency.html