当业务逐渐变大的时候,需要引入缓存提高性能。
而这时候势必就会出现数据库和缓存的一致性问题。
那么怎么解决呢?
当收到写数据的请求时,不仅仅是修改数据库还要修改缓存,这里存在一个先后问题。
简而言之,分为两种情况:
1.先更新缓存,后更新数据库。
2.先更新数据库,后更新缓存。
但是这两个步骤可能会出现一个成功,一个异常的情况发生,下面来分析一下这样会发生什么


先更新缓存,后更新数据库
更新缓存成功,但是随着更新数据库失败,数据库中的数据得不到修改,随着缓存中数据过期时间到期,之后重新读到的是没有修改的数据。
先更新数据库,后更新缓存
更新数据库成功,缓存更新失败,这样会导致这一段时间内读请求读到的是错误数据,但保持最终一致性。

这两者都会对业务造成一定的影响,不仅仅操作顺序会导致一致性出现问题,并发操作也会导致一致性出现问题。

并发导致的一致性问题
第一种情况举例,缓存->数据库,有A和B两个线程,更新同一个数据情况下。
1.A线程更新缓存数据temp=1
2.B线程更新缓存数据temp=2
3.B线程更新数据库数据temp=2
4.A线程更新数据库数据temp=1
结果缓存和数据库数据不一致。

既然并发和操作步骤中可能会导致的异常都会使一致性出现问题,那么有什么解决方法呢?
缓存中数据存在到期时间,那么我们为什么不试试不更新数据库,而是删除缓存,从数据库中读取最新数据呢?下面我们分析一下能不能成功

删除缓存
显然删除缓存也存在两种方案
1.先删除缓存,后更新数据库
想一想,如果一个线程要修改,一个线程要读取,但是缓存删除成功,数据库还未更新,此时线程读到的值就是旧值,这样缓存就会存在一段时间的旧数据。
2.先更新数据库,再删除缓存。
还是两个读写线程,读线程取到旧值,写线程更新数据库,然后删除缓存,读线程再缓存中添加旧值,这样还是会导致不一致的情况。
但是这样的出现概率大吗?
读线程的执行时间要大于写线程,这其实是很少出现的。

那么解决了并发问题,如何解决出现异常呢?

保证操作执行成功
重试操作,将重试操作放入消息队列,或者订阅变更日志(canal)的方式去做,推荐使用先更新数据库再更新缓存。

还有一个问题,就是缓存可能会读到旧值,这样推荐使用缓存延迟双删策略,延迟时间一般大于主从复制的延迟时间,也要大于线程读取数据库加上写入缓存的时间。

缓存和数据库之间要想做到强一致性是不可能的,因为总需要在性能和一致性二者之间做出取舍。