缓存是目前解决高并发场景下的主流解决方案必不可少的工具之一。
在高并发或者多线程的情况下,如何保证缓存和持久化存储的数据一致是业界面临的一个普遍问题。
本文针对几种常见的数据库、缓存更新方式,分析一下并发场景下存在的问题。
缓存数据库更新的几种常见方式
- 先更新缓存,后更新数据库
- 先更新数据库,后更新缓存
- 先删除缓存,后更新数据库
- 先更新数据库,后删除缓存
我们考虑一下这种场景。假设有并发请求A和B,按一下情况顺序执行。然后我们依次说明。
第一种情况
先更新缓存,后更新数据库
考虑A/B执行顺序如下:
- A更新缓存
- B更新缓存
- B更新数据库
- A更新数据库
最终导致的结果就是:缓存里是B的结果,但数据库里是A的结果,二者不一致。
第二种情况
先更新数据库,后更新缓存
考虑A/B执行顺序如下:
- A更新数据库
- B更新数据库
- B更新缓存
- A更新缓存
最终导致的结果就是:缓存里是A的结果,但数据库是B的结果,二者不一致。
第三种情况
先删除缓存,后更新数据库
考虑A/B执行顺序如下:
- A删除缓存
- B查询不到缓存
- B从数据库中取出旧值
- B将旧值写入缓存
- A更新数据库
最终导致的结果就是:缓存里是旧值,但数据库是A更新后的新值,二者不一致。
那么这种情况有没有解决方案呢?
既然最后写入的就是最新的值,那么可以在写入新值后再次删除缓存里的旧值来解决。那再次删除缓存的时机就显得比较重要了,必须得在B将旧值写入缓存之后,可以取一次请求时间加上写入缓存的时间和作为延迟删除缓存的时间。
如果有数据库主从架构,需要考虑主从同步时间。
第四种情况
先更新数据库,后删除缓存
大厂推荐(facebook论文)
考虑A/B执行顺序如下:
- 缓存刚好失效
- 请求A查询数据库,得一个旧值
- 请求B将新值写入数据库
- 请求B删除缓存
- 请求A将查到的旧值写入缓存
我们发现这种情况下还是会产生数据的不一致。
这种情况发生的一个前提就是步骤3比步骤2的执行时间更长,从而导致步骤4比步骤5先执行。
但现实情况是,大概率情况下读比写要快,所以这种情况出现的概率较小。
另外,需要考虑的情况就是,删除缓存失败怎么办?
答案是:消息队列异步补偿机制,实现最终一致性。
数据库主从模式下改怎么处理呢?
推荐方案: