如何保证缓存和数据库数据的⼀致性?

根据CAP理论,在保证可用性和分区容错性的前提下,无法同时实现强一致性,因此缓存和数据库之间的绝对一致性是不可能实现的,只能追求最终一致性。

选择适当的缓存更新策略

删除缓存而不是更新缓存

当一个线程对缓存中的键进行写操作时,如果其他线程同时从数据库读取数据,就有可能读取到脏数据,导致数据不一致的问题。

相比之下,删除缓存的速度比更新缓存的速度快得多,所需时间也要少得多,读取脏数据的概率也较小。

先更新数据,再删除缓存

先更新数据库可能比删除缓存耗时更长。当缓存中不存在对应的键,且数据库更新尚未完成时,如果有线程读取数据并将其写入缓存,那么在数据库更新成功后,该键就成为脏数据。

毫无疑问,先删除缓存,再更新数据库,缓存中不存在键的时间更长,产生脏数据的概率更大。

目前最流行的缓存读写策略是"Cache-Aside Pattern",采用先更新数据库,再删除缓存的方式。

处理缓存不一致性

如果并发不是特别高且对缓存高度依赖性不强,一定程度的不一致是可以接受的。

但是,如果对一致性要求较高,则需要采取措施确保缓存和数据库中的数据一致。

缓存和数据库数据不一致的常见原因有两种:

  • 缓存键删除失败
  • 并发写入脏数据

使用消息队列确保键被删除

可以引入消息队列,将要删除的键或删除失败的键放入消息队列中,并利用消息队列的重试机制重试删除对应的键。

这种方案看起来不错,但对业务代码有一定的侵入性。

数据库订阅 + 消息队列确保键被删除

可以使用一个服务(如阿里的Canal)来监听数据库的binlog,获取需要操作的数据。

然后,使用一个公共服务获取订阅程序传来的信息,并执行缓存删除操作。

这种方式降低了对业务的侵入,但整个系统的复杂性也增加了,适用于基础设施完善的大型公司。

延时双删防止脏数据

还有一种情况是,在缓存不存在时写入了脏数据,这种情况在先删除缓存,再更新数据库的缓存更新策略下更容易发生,解决方案是延

时双重删除。

简而言之,即在第一次删除缓存后,经过一段时间再次删除缓存。

设置延时时间需要仔细考虑和测试。

设置缓存过期时间作为兜底方案

这是一种朴素但有效的方法,给缓存设置一个合理的过期时间,即使发生缓存数据不一致的问题,它也不会持续不一致,因为在缓存过期时,将会重新达到一致状态。

标签: java, Java面试题, Redis, Java问题合集, Java编程, Java问题精选, Java常见问题, Redis面试题