缓存中的数据和数据库中的不一致
缓存和数据库的数据不一致是如何发生的?
- 缓存中有数据,那么缓存的数据值需要和数据库中的值相同
- 缓存中本身没有数据,那么,数据库中的值必须是最新值。
不符合这两种情况,就属于缓存和数据库的数据不一致的问题了。
- 缓存可以分成读写缓存和只读缓存
对于读写缓存来说,如果要对数据进行增删改,就需要在缓存中进行,同时还要根据写回策略,决定是否同步写回数据库呢。
- 对于多读少写的场景,即使同步写一些信息也是可以接受的。
- 但对于多读多写的场景,同步写就存在较大的瓶颈了。
- 同步直写策略: 写缓存时,也同步写数据库,缓存和数据库中的数据一致。
- 异步写回策略: 写缓存时不同步写数据库,等到数据从缓存淘汰时,再写回数据库。
-
对于读写缓存,要想保证缓存和数据库中的数据一致,采用同步直写策略。要保证缓存和数据库的原子性。
-
对于数据准确性要求不高。也可以使用异步写回策略。如果一段时间不被阅读,使用上LRU、LFU等策略,可以在数据被淘汰前将脏数据写回数据库。
-
对于只读缓存,只读缓存一致性:请求写数据库,删缓存。下次读数据时发现缓存没有值就会直接进入数据库然后再把数据写入缓存。
-
删除数据: 如果必须要采用两者其一,推荐使用先更新DB后删除缓存,同时给缓存给设置合适的过期时间,利用Redis自身的过期机制,来达到最终一致性,降低业务影响~
如何解决数据不一致问题 (最终一致性)
-
重试机制
- 删除缓存值或者将要更新的数据库值暂存到消息队列。当应用没有能够成功删除缓存或者是更新数据库,可以从消息队列中重新读取,然后再操作。
-
先删除缓存,再更新数据库 假设线程 A 删除缓存值后,还没有来得及更新数据库(比如说有网络延迟),线程 B 就开始读取数据了,那么这个时候,线程 B 会发现缓存缺失,就只能去数据库读取。这会带来两个问题:线程 B 读取到了旧值;线程 B 是在缓存缺失的情况下读取的数据库,所以,它还会把旧值写入缓存,这可能会导致其他线程从缓存中读到旧值。等到线程 B 从数据库读取完数据、更新了缓存后,线程 A 才开始更新数据库,此时,缓存中的数据是旧值,而数据库中的是最新值,两者就不一致了。 在第一次删除缓存值后,延迟一段时间再次进行删除,所以我们也把它叫做“延迟双删”。
-
先更新数据库值,再删除缓存值。
总结
删除缓存值或更新数据库失败而导致数据不一致,你可以使用重试机制确保删除或更新操作成功。
在删除缓存值、更新数据库的这两步操作中,有其他线程的并发读操作,导致其他线程读取到旧值,应对方案是延迟双删。