如何保证缓存与数据库一致性?

131 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第20天,点击查看活动详情

我们为什么要使用缓存?

我们使用缓存的原因,毋庸置疑是因为其读写快。

使用缓存的优点:

  1. 提供系统吞吐量,提升用户体验。
  2. 能够缩短系统响应时间,提升用户体验。
  3. 减低数据库压力,防止压力过大,数据库宕机。

哪类数据适合放缓存?

  • 热点数据。访问频率高的。
  • 不经常更新且量大的。即读多写少。
  • 数据一致性要求低。

如何保证缓存与数据库一致性

不更新缓存,而是删除缓存

先详细说一说这个操作是什么意思。

当系统接收到请求时,首先读取缓存,当缓存不存在时,则读取数据库,并写入缓存。 当读取缓存时,存在缓存,则把缓存删除。下个请求则读取数据库,然后写入缓存。

流程如下: image.png

这里仅仅是数据的读操作,数据的写操作呢?

那么问题就来了,我们是先更新数据库,再删除缓存?还是先删除缓存,再更新数据库呢?

先更新数据库,再删除缓存

这种情况会并发问题么?答案是会。

我们先假设两个请求,同时有一个请求A去更新数据,一个请求B去查询(读)数据。

  1. 请求A在更新数据(读写)。
  2. 请求B去查询数据(读),而这时的缓存是一个旧的值(请求A未删除缓存)。
  3. 请求B删除缓存,并返回读取数据。
  4. 请求A更新完数据库,然后删除缓存。

这情况在高并发下请求B读取缓存的旧数据,造成脏数据。

解决方案:消息队列

先更新数据库,成功后往消息队列发消息,消费者到消息后再删除缓存,借助消息队列的重试机制来实现,达到最终一致性的效果。

先删除缓存,再更新数据库

还是先假设有并发两个请求,一个请求A更新数据,一个请求B查询数据。

  1. 请求A先删除缓存,然后更新数据库。
  2. 请求B读取缓存不存在,然后读取数据库,并写入缓存。

此方式也会造成脏数据,即步骤2 请求B在请求A未更新数据库时,读到旧数据。

解决方案:延迟双删

延迟双删:先删除缓存,再更新数据库,延迟N秒之后再删除一次缓存,这样就不用担心缓存中的数据和数据库中的数据不一致。

这个延迟N秒,N是多少合适呢?一般来说,N要大于一次读写缓存的时间,N具体是多少,需要结合业务来评估这个数值。

总结:

缓存不是更新,而是删除。

删除缓存的两种方式:

  1. 先删除缓存,再更新数据库。解决方案是使用延迟双删。
  2. 先更新数据库,再删除缓存。解决方案是消息队列。