首先说明一个观点:如果对数据一致性要求很高的话就不要存放缓存了,因为只要涉及到数据库和缓存双写就会造成一致性的问题。
最简单一个一致性保持方法就是依赖缓存的一个过期时间去击穿保证数据一致性,但是这会导致在缓存失效到缓存重新存入这段时间内数据库一个压力(因为缓存一个场景就是环境数据库的压力),这里介绍几种不单单依赖失效时间来解决数据库和缓存数据一致性的方法及其分析。
1.先更新数据库,再更新缓存
这个策略是肯定有问题的,从两个角度去分析。
A.线程安全问题
假设现在有两个线程去执行一致性操作
(1)请求1更新数据库
(2)请求2更新数据库
(3)请求2更新缓存
(4)请求1更新缓存
这里请求1比请求2先去更新数据库,但是请求1更新缓存比请求2更新缓存要晚,这里就会出现数据不一致情况,缓存中出现了脏数据,且如果没有失效时间控制,缓存中脏数据删除只能通过下一次更新操作,这之前的读操作都将读到错误的数据。
B.业务场景分析
(1)一般业务只是去要求更新数据库操作,更新缓存只是我们为了数据一致性去做的,这里加大了业务复杂性和影响了性能。
(2)频繁更新缓存会造成和缓存中间件之间的IO操作,产生不必要的网络开销
2.先删除缓存,再更新数据库
这种解决方案存在的问题:一个读线程和同步线程
(1)同步线程1去删除了缓存
(2)读数据线程2读到缓存不存在,此时去数据库中读取数据,此时线程1还没更新数据库的值读到了旧值
(3)线程2设置新的缓存
(4)线程1更新了数据库中的值
这样也造成了缓存中的脏数据。
针对这种场景,可以采用延时双删的策略。在请求1更新完数据库中的值后,延迟一段时间,然后再进行缓存删除,这样可以保证再次写入缓存的值是正确的数据库中的值。具体延迟多少时间要根据业务场景而定,这里太久会影响正常更新业务的时间。且这种方案不能解决第二次删除缓存的时候失败的问题。
3.先更新数据库,再删除缓存
这种也会存在一种读线程和同步线程并发的问题,
(1)线程1读到缓存失效,查询数据库拿到旧值
(2)线程2更新数据库并且删除缓存
(3)线程1写入缓存旧值
不过几率很低,因为数据库的读操作比写操作快的多。非要解决的话也可以加一次删除操作。
后两种方案中存在的问题:
1.都有可能用到了双删除,这里删除失败就会有分析场景中造成缓存脏数据的可能。可以加个mq和重试机制。
2.都去在业务代码中耦合了这些缓存一致性代码,影响了正常业务,其中一些等待,双删都会造成性能和IO开销。可以加入binlog监听缓存一致性操作 和 消息队列异步解耦缓存删除这一动作,不影响正常业务逻辑。