前提说明
关于这个问题,主要发生在数据需要更新时,是先更新数据库还是先更新redis。以及不同的更新方式可能产生哪些影响。
对于缓存,我们可以操作为删除与更新,对于持久化的数据库,则只能选择更新,因此,总共有一下4中操作组合。
- 更新缓存 -> 更新数据库
- 更新数据库 -> 更新缓存
- 删除缓存 -> 更新数据库
- 更新数据库 -> 删除缓存
接下来分别分析不同的场景。
先更缓存,再更数据库
左侧图:如果再更新完缓存,再更新数据库时,失败了。这个会导致缓存中的数据变为了脏数据。
右侧图:存在多个线程在更新数据,那么线程1先更新缓存后写进数据库,会导致缓存中的数据与数据库中数据不一致。
先更数据库,再更缓存
左侧图:如果再更新完数据库,再更新缓存时,失败了。这个会导致缓存中的数据变为了脏数据。
右侧图:存在多个线程在更新数据,那么线程1先更新数据库后写进缓存,会导致缓存中的数据与数据库中数据不一致。
先删缓存,再更数据库
左侧图:针对于先删除缓存,之后更新数据库失败了,那么不影响数据库与缓存的一致性问题。
右侧图:针对于线程1,先删除了缓存,但是同时,线程2,读取缓存发现数据不存在,则读取数据库更新缓存,之后线程1又更新了数据库,将导致数据库与缓存的不一致。
先更数据库,再更缓存
左侧图:针对于先更新数据库,之后更新数据库失败了,那么数据库需要进行数据回滚,考虑到数据库回滚到成熟很高,可以认为不影响最终一致性
右侧图:线程1,更新数据库结束后,将缓存删除后;线程3开始读取缓存,发现缓存不存在,之后从数据库读取更新缓存,在读取完数据库后;线程2开始更新数据库,之后删除缓存;线程3将读取到数据刷新到缓存,此时数据与缓存中的数据不一致。
综上,可以发现,其实先更新数据库,再删除数据库与缓存不一致的概率是最小的,毕竟这是需要三个及其以上的线程配合才能完成。
那么有没有更加完美的解决方案呢?其实想完全解决缓存一致性问题,是没有办法100%保证的,但是可以选择延迟双删(如下图)来进一步降低不一致发生的概率。关于延迟双删可以起异步线程、也可通过MQ节藕消费处理等方式均可实现。但是这个就没有问题吗?想想我们使用缓存的目的是什么?提高性能、降低数据库压力,但是双删的结果时,将更多的流量走了数据库。所以每个方案其实都有优缺利弊,按照实际的业务,根据每种不同的方案的优缺点,进行选择最佳解决方案。