缓存与数据库一致性系列-01

Posted by Kido on 2018-12-01

今天,我们来分析一下,缓存与数据库被使用次数最多的一种使用方法

写流程:

第一步先删除缓存,删除之后再更新DB,之后再异步将数据刷回缓存

读流程:

第一步先读缓存,如果缓存没读到,则去读DB,之后再异步将数据刷回缓存

方案分析

优点剖析

1. 实现起来简单

What Should I Say ?

2. “先淘汰缓存,再写数据库” 合理

为什么说这也算优点呢?试想一下

如果把写流程改一下:先更新缓存,再更新DB。 如果我们更新缓存成功,而更新数据库失败,就会导致缓存中的数据是错误的,而我们大部分的业务一般能忍受数据延迟,但是数据错误这是无法接受的,所以先淘汰缓存是比较合理的。 如果把写流程改一下:不删缓存,先更新DB,再更新缓存。 如果我们更新DB成功,而更新缓存失败,则会导致缓存中就会一直是旧的数据(也算是一种错误数据),所以先淘汰缓存是比较合理的。

3. 异步刷新,补缺补漏

在很多业务场景中,缓存只是辅助,所以在很多业务中,缓存的读写失败不会影响主流程,啥意思呢?就是说很多情况下,即使操作缓存失败(比如步骤1.1中的’DEL缓存失败’),程序还是会继续往下走(继续步骤1.2 更新数据库),所以这个时候异步刷新就能在一定程度上,对1.1的失败进行错误数据的修补

说完优点,我们再来看看缺点

缺点剖析

1. 容灾不足

在分布式领域,“Everything will fails”,任何可能出现问题的地方都会出现问题

我们来分析一下写流程,第一步,’DEL缓存失败’怎么办?流程是否还继续走?如果继续执行,那么从’更新完DB’到异步’刷新缓存’缓存期间,数据处于滞后状态。而且如果缓存处于不可写状态,那么异步刷新那步也可能会失败,那缓存就会长期处于旧数据,问题就比较严重了

2. 并发问题

写写并发:试想一下,同时有多个服务器的多个线程进行’步骤1.2更新DB’,更新DB完成之后,它们就要进行异步刷缓存,我们都知道多服务器的异步操作,是无法保证顺序的,所以后面的刷新操作存在相互覆盖的并发问题,也就是说,存在先更新的DB操作,反而很晚才去刷新缓存,那这个时候,数据也是错的

读写并发:再试想一下,服务器A在进行’读操作’,,在A服务器刚完成2.2时,服务器B在进行’写操作’,假设B服务器1.3完成之后,服务器A的1.3才被执行,这个时候就相当于更新前的老数据写入缓存,最终数据还是错的

方案总结

今天介绍的这个方案呢,适合大部分的业务场景,很多人都在用,香还是很香的,实现起来也简单。
适合使用的场景:并发量、一致性要求都不是很高的情况
我觉得这个方案有一个比较大的缺陷在于刷新缓存有可能会失败,而失败之后缓存中数据就一直会处于错误状态,所以它并不能保证数据的最终一致性,那怎么解决这个问题呢,篇幅有限,我们后续文章再继续分享