缓存数据库双写一致性问题

引言

在引入缓存系统的项目中,我们需要旧数据进行更新操作时,我们是先淘汰缓存,再更新数据库。还是先更新数据库,再淘汰缓存。亦或是更新数据库,再更新缓存呢?下面,将会讲讲小编对这三种方案的优缺点的一些想法。

目的

  1. 整理自己对这方面的知识;
  2. 分享自己的看法,和小伙伴们一起学习;
  3. 用初学者的角度来浅显的讲解这方面的内容。

缓存更新策略

  1. 先更新数据库,再更新缓存;
  2. 先更新数据库,再删除缓存;
  3. 先删除缓存,再更新数据库;

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

这套方案,小编认为大多数场景不合适。为什么呢?主要从以下几个原因讲解:

一、资源浪费

在一些大型的信息网站中(博客、贴吧),我们引入缓存主要是对热数据(请求频繁的)进行缓存,而这时候,如果很多用户对于冷数据(长时间没人访问,或者访问量很少)进行更新,然后再去更新缓存,这就造成了缓存资源的大量浪费(因为访问量少,导致这些缓存命中低,浪费缓存资源)。

二、脏数据

这是由于出现了并发操作的原因导致的,如:同时有两个请求A和B对数据进行了更新操作,由于网络原因,可能存在以下情况:

  1. 请求A更新了数据库;
  2. 请求B更新了数据库;
  3. 请求B更新了缓存;
  4. 请求A更新了缓存。

这就出现了A数据覆盖了B数据的情况,此时就产生了脏数据,如果没有缓存定时过期机制,此时的脏数据需要等待下一次的更新,才会对缓存进行更新,虽然用户看到数据出现问题,会再重新更新一次,但这已经有多了一次不必要的请求了,写请求量大的时候,容易造成众多不必要的更新请求。

三、请求时间

如果缓存不是一种简单的数据缓存,而是需要经过较为复杂的运算,才能得出缓存值,这时候,请求将会在计算缓存值上,耗费一部分时间,而这就导致了请求的响应时间变长,增加系统的负担,降低了系统的处理能力。

四、频繁写入

在写请求很多,而读请求很少的场景下,缓存没起到多大的作用,就给频繁更新了,造成了资源浪费,如:

  1. 对数据A进行了一次修改,生成了缓存A;
  2. 此时没有读取数据A的请求;
  3. 对数据A进行了一次修改,更新了缓存A;
  4. 此时没有读取数据A的请求;
  5. 对数据A进行了一次修改;
  6. 此时有了读取数据A的请求。

这时就会造成缓存的不必要更新操作(没有人读取缓存),用户量大的时候,会造成大量的不必要操作,造成系统资源的浪费。

适合场景

当然,这种也不是说就不能使用这些,既然存在,就有其存在的道理。一下的场景就适合:

  1. 读请求占据网站的总流量的99%;
  2. 网站数据量不大(几十万的文章数据);
  3. 很少会去更新数据(一般文章写好后,不会去更新)。

案例

  1. 个人博客
  2. 手册网站(w3cschool、菜鸟教程等)

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

这种策略比较多平台使用,如:Facebook。但是这种策略也存在一些问题,如:

一、脏数据

造成脏数据的原因主要由并发引起,如:

  1. 用户A请求数据A
  2. 数据A缓存失效
  3. 用户A从数据库中得到旧数据数据A
  4. 用户B更新了数据A(新数据)
  5. 用户B删除了缓存
  6. 用户A将查到旧数据写入了缓存

此时就产生了脏数据,虽然这种概率非常小,但对于更新不频繁的网站来说,此时的脏数据就是个很严重的错误。

二、缓存删除失败

  1. 用户A更新了数据A
  2. 用户A删除数据A的缓存失败
  3. 用户B读到数据A缓存的旧数据

此时就产生了数据不一致的问题。

解决方案

1、设置缓存的有效时间(最简单的方案)

优点:

  • 简单
  • 易操作

缺点:

  • 会存在短时间内的旧数据
  • 如果数据量太多,缓存有效时间短,容易发生一段时间内缓存大量失效,此时的数据库压力突然剧增,引发缓存雪崩现象(缓存有效时间为随机值减少发生缓存雪崩的可能性)

2、消息队列(比较复杂,需要引入消息队列系统)

步骤:

  1. 更新数据库;
  2. 删除缓存失败;
  3. 将需要删除的Key发送到消息队列;
  4. 隔断时间从消息队列中拉取要删除的key;
  5. 继续删除,直至成功为止。

优点:

  • 不会引发缓存雪崩
  • 只删除需要删除的缓存

缺点:

  • 引入了消息系统(增加了系统的复杂性)

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

这种方法也是比较多人使用的,但是也会出现脏数据的问题:

原因

  1. 用户A删除缓存失败
  2. 用户A成功更新了数据

  或者

  1. 用户A删除了缓存;
  2. 用户B读取缓存,缓存不存在;
  3. 用户B从数据库拿到旧数据;
  4. 用户B更新了缓存;
  5. 用户A更新了数据。

以上两种情况都能造成脏数据的产生。

解决方案

1、设置缓存的有效时间(最简单的方案)

优点:

  • 简单
  • 易操作

缺点:

  • 会存在短时间内的旧数据
  • 如果数据量太多,缓存有效时间短,容易发生一段时间内缓存大量失效,此时的数据库压力突然剧增,引发缓存雪崩现象(缓存有效时间为随机值减少发生缓存雪崩的可能性)

2、消息队列

  1. 先淘汰缓存;
  2. 更新数据库;
  3. 将需要淘汰的缓存Key发送到消息队列;
  4. 另起一程序拉取消息队列的数据;
  5. 对需要删除的key进行删除,直至删除为止。

优点:

  • 保证了缓存的删除
  • 不会增加更新的处理时间
  • 不会引发缓存雪崩

缺点:

  • 会增加一次缓存miss(可以忽略不计)
  • 引入了消息系统(增加了系统的复杂性)

总结

  1. 上述的方法根据自己的系统业务来选择;
  2. 既然存在,就有其道理;
  3. 使用缓存有效时间时,需要注意缓存雪崩问题;
  4. 可以引入消息系统来避免脏数据;
  5. 还存在分析binlog来异步删除缓存(小编未研究这个,所以小编没写);

原文地址:https://www.cnblogs.com/kafeixiaoluo/p/9123360.html

时间: 2024-10-20 00:27:45

缓存数据库双写一致性问题的相关文章

K:缓存数据库双写数据一致性方案

对于缓存和数据库双写,其存在着数据一致性的问题.对于数据一致性要求较高的业务场景,我们通常会选择使用分布式事务(2pc.paxos等)来保证缓存与数据库之间的数据强一致性,但分布式事务的复杂性与对资源的占用问题,使得该处理方式会造成系统性能的降低.对于数据一致性要求没那么高的业务场景,选择分布式事务的处理方式就会显得不是那么必要.为此,在一般情况下,对于数据一致性要求没那么高的业务场景,会选择使用cache-aside-pattern方案来保证缓存与数据库之间,数据的最终一致性,以下文章便是介绍

高并发场景下缓存+数据库双写不一致问题分析与解决方案设计

Redis是企业级系统高并发.高可用架构中非常重要的一个环节.Redis主要解决了关系型数据库并发量低的问题,有助于缓解关系型数据库在高并发场景下的压力,提高系统的吞吐量(具体Redis是如何提高系统的性能.吞吐量,后面会专门讲). 而我们在Redis的实际使用过程中,难免会遇到缓存与数据库双写时数据不一致的问题,这也是我们必须要考虑的问题.如果还有同学不了解这个问题,可以搬小板凳来听听啦. 一.数据库+缓存双写不一致问题引入 要讲数据库+缓存双写不一致的问题,就需要先讲一下这个问题是怎么发生的

Java进阶面试必问:如何保证缓存与数据库的双写一致性?

面试题 如何保证缓存与数据库的双写一致性? 面试官心理分析 你只要用缓存,就可能会涉及到缓存与数据库双存储双写,你只要是双写,就一定会有数据一致性的问题,那么你如何解决一致性问题? 面试题剖析 一般来说,如果允许缓存可以稍微的跟数据库偶尔有不一致的情况,也就是说如果你的系统不是严格要求 "缓存+数据库" 必须保持一致性的话,最好不要做这个方案,即:读请求和写请求串行化,串到一个内存队列里去. 串行化可以保证一定不会出现不一致的情况,但是它也会导致系统的吞吐量大幅度降低,用比正常情况下多

经典好文--如何保证缓存和数据库的双写一致性

面试题如何保证缓存与数据库的双写一致性? 面试官心理分析你只要用缓存,就可能会涉及到缓存与数据库双存储双写,你只要是双写,就一定会有数据一致性的问题,那么你如何解决一致性问题? 面试题剖析一般来说,如果允许缓存可以稍微的跟数据库偶尔有不一致的情况,也就是说如果你的系统不是严格要求 “缓存+数据库” 必须保持一致性的话,最好不要做这个方案,即:读请求和写请求串行化,串到一个内存队列里去. 串行化可以保证一定不会出现不一致的情况,但是它也会导致系统的吞吐量大幅度降低,用比正常情况下多几倍的机器去支撑

9.如何保证缓存与数据库的双写一致性?

作者:中华石杉 面试题 如何保证缓存与数据库的双写一致性? 面试官心理分析 你只要用缓存,就可能会涉及到缓存与数据库双存储双写,你只要是双写,就一定会有数据一致性的问题,那么你如何解决一致性问题? 面试题剖析 一般来说,如果允许缓存可以稍微的跟数据库偶尔有不一致的情况,也就是说如果你的系统不是严格要求 “缓存+数据库” 必须保持一致性的话,最好不要做这个方案,即:读请求和写请求串行化,串到一个内存队列里去. 串行化可以保证一定不会出现不一致的情况,但是它也会导致系统的吞吐量大幅度降低,用比正常情

一个经典面试题:如何保证缓存与数据库的双写一致性?

只要用缓存,就可能会涉及到缓存与数据库双存储双写,你只要是双写,就一定会有数据一致性的问题,那么你如何解决一致性问题? 面试题剖析 一般来说,如果允许缓存可以稍微的跟数据库偶尔有不一致的情况,也就是说如果你的系统不是严格要求 “缓存+数据库” 必须保持一致性的话,最好不要做这个方案,即:读请求和写请求串行化,串到一个内存队列里去. 串行化可以保证一定不会出现不一致的情况,但是它也会导致系统的吞吐量大幅度降低,用比正常情况下多几倍的机器去支撑线上请求. Cache Aside Pattern 最经

Java面试常被问到这道题:如何保证缓存与数据库的双写一致性?

面试原题 如何保证缓存与数据库的双写一致性? 面试官心理分析 你只要用缓存,就可能会涉及到缓存与数据库双存储双写,你只要是双写,就一定会有数据一致性的问题,那么你如何解决一致性问题? 面试题剖析 一般来说,如果允许缓存可以稍微的跟数据库偶尔有不一致的情况,也就是说如果你的系统不是严格要求?"缓存+数据库" 必须保持一致性的话,最好不要做这个方案,即:读请求和写请求串行化,串到一个内存队列里去. 串行化可以保证一定不会出现不一致的情况,但是它也会导致系统的吞吐量大幅度降低,用比正常情况下

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

一般来说,如果允许缓存可以稍微的跟数据库偶尔有不一致的情况,也就是说如果你的系统不是严格要求 “缓存+数据库” 必须保持一致性的话,最好不要做这个方案,即:读请求和写请求串行化,串到一个内存队列里去. 串行化可以保证一定不会出现不一致的情况,但是它也会导致系统的吞吐量大幅度降低,用比正常情况下多几倍的机器去支撑线上的一个请求. Cache Aside Pattern 最经典的缓存+数据库读写的模式,就是 Cache Aside Pattern. 读的时候,先读缓存,缓存没有的话,就读数据库,然后

保证缓存与数据库双写时的数据一致性

缓存与数据库双写时的数据一致性问题:只要用缓存,就可能会涉及到缓存与数据库双存储双写,你只要是双写,就一定会有数据一致性的问题,那么你如何解决一致性问题? 一般来说,就是如果你的系统不是严格要求缓存+数据库必须一致性的话,缓存可以稍微的跟数据库偶尔有不一致的情况,最好不要做这个方案,读请求和写请求串行化,串到一个内存队列里去,这样就可以保证一定不会出现不一致的情况. 串行化之后,就会导致系统的吞吐量会大幅度的降低,用比正常情况下多几倍的机器去支撑线上的一个请求. 原文地址:https://www