1.缓存和数据库的数据不一致是如何发生的?
首先,我们得清楚“数据的一致性”具体是啥意思。其实,这里的“一致性”包含了两种情况:
- 缓存中有数据,那么,缓存的数据值需要和数据库中的值相同;
- 缓存中本身没有数据,那么,数据库中的值必须是最新值。
对于读写缓存来说,如果要对数据进行增删改,就需要在缓存中进行,同时还要根据采取的写回策略,决定是否同步写回到数据库中。
同步直写策略:写缓存时,也同步写数据库,缓存和数据库中的数据一致;
- 异步写回策略:写缓存时不同步写数据库,等到数据从缓存中淘汰时,再写回数据库。使用这种策略时,
- 如果数据还没有写回数据库,缓存就发生了故障,那么,此时,数据库就没有最新的数据了。
2. 如何解决数据不一致问题?
重试机制具体来说,可以把要删除的缓存值或者是要更新的数据库值暂存到消息队列中(例如使用Kafka 消息队列)。当应用没有能够成功地删除缓存值或者是更新数据库值时,可以从消息队列中重新读取这些值,然后再次进行删除或更新。
当有大量并发请求时,应用还是有可能读到不一致,按照不同的删除和更新顺序,分成两种情况来看。在这两种情况下,我们的解决方法也有所不同。
情况一:先删除缓存,再更新数据库。
假设线程 A 删除缓存值后,还没有来得及更新数据库(比如说有网络延迟),线程 B 就开始读取数据了,那么这个时候,线程 B 会发现缓存缺失,就只能去数据库读取。这会带来两个问题:
- 线程 B 读取到了旧值;
- 线程 B 是在缓存缺失的情况下读取的数据库,所以,它还会把旧值写入缓存,这可能会导致其他线程从缓存中读到旧值。
在线程 A 更新完数据库值以后,我们可以让它先 sleep 一小段时间,再进行一次缓存删除操作。
情况二:先更新数据库值,再删除缓存值。
如果线程 A 删除了数据库中的值,但还没来得及删除缓存值,线程 B 就开始读取数据了,那么此时,线程 B 查询缓存时,发现缓存命中,就会直接从缓存中读取旧值。不过,在这种情况下,如果其他线程并发读缓存的请求不多,那么,就不会有很多请求读取到旧值。而且,线程 A 一般也会很快删除缓存值,这样一来,其他线程再次读取时,就会发生缓存缺失,进而从数据库中读取最新值。所以,这种情况对业务的影响较小。
对于读写缓存来说,如果我们采用同步写回策略,那么可以保证缓存和数据库中的数据一致。只读缓存的情况比较复杂,我总结了一张表,以便于你更加清晰地了解数据不一致的问题原因、现象和应对方案。