你知道怎么解决DB读写分离,导致数据不一致问题吗?

蚀刻
• 阅读 2603

目录

  1. 前言
  2. 先更新数据库,再更新缓存
  3. 先更新缓存,再更新数据库
  4. 先删除缓存,再更新数据库
  5. 先更新数据库,再删除缓存
  6. 删除缓存失败,导致不一致
  7. 读写分离,导致不一致

前言

在项目中缓存是经常用到的,为了减少和数据库的交互,小伙伴们利用缓存的思路如下:

你知道怎么解决DB读写分离,导致数据不一致问题吗?

缓存设计思路

我们小伙伴们有没有考虑到缓存更新的问题,小伙伴们肯定会说肯定用过啊,有数据更新时,把缓存清空掉就行了啊,下一次访问的时候服务就会把新值设置到缓存中了。这样不就行了吗?对的,在一般项目中,这样的使用就够了。那老顾带着大家看看在高并发场景下,会有什么问题?

我们举例说明,就拿商品的库存作为缓存。那现在我们要更新缓存中的库存值,怎么进行操作,我们看下面几个场景:

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

存在的问题场景:请求A更新值为99,请求B更新值为98

你知道怎么解决DB读写分离,导致数据不一致问题吗?

上图流程:

  1. 请求A先发起,更新数据库为99,但还没有来得及更新缓存
  2. 请求B发起,更新数据库为98,又更新了缓存值为98
  3. 请求A这时才更新缓存的值为99

这样数据库的值为98,但缓存的值为99,数值不一致。(不推荐)

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

这个流程跟上面很类似,出现的问题也很类似

  1. 请求A先更新缓存为99,但还没有来得及更新数据库
  2. 请求B更新缓存为98,又更新了数据库为98
  3. 请求A这时更新数据库为99

这样就缓存的值为98,数据库为99导致不一致。(不推荐)

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

存在的问题场景:请求A更新值为99,请求B获取值

你知道怎么解决DB读写分离,导致数据不一致问题吗?

上图中请求流程:

  1. 请求A更新值,先把缓存中的值删除,但还没有来得及更新数据库
  2. 此时请求B过来查询此值,发现缓存中不存在,就到数据库中查询
  3. 请求B在数据库中获取到值,在把值设置到缓存中。
  4. 请求A这时才更新数据库里面的值为99

这样就导致了缓存和数据库的不一致问题,缓存中的值一直是旧数据。(不推荐)

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

这个方案也是老外提出的《Cache-Aside pattern》更新缓存的策略。这种策略先保证了源头的数据一定是正确的。这种策略是不是万无一失呢,有一种非常特殊的场景

你知道怎么解决DB读写分离,导致数据不一致问题吗?

上图流程:建立中缓存突然失效了

  1. 请求A发起查询请求,直接到数据库查询到100,但还没有来得及去设置缓存
  2. 请求B更新值,先更新数据库,在删除缓存
  3. 请求A这时才设置缓存为100

这种情况发生的不一致,是因为缓存突然失效了。而且还要保证请求B更新操作 比 请求A的查询操作还要快;才会导致不一致。这种情况概率会很少。一般要求不高的项目可以采用此方式(推荐)。

缓存删除失败,导致不一致

这种先更新数据库,再删除缓存的策略中,因为要删除缓存,但如果缓存删除失败,就会导致数据库与缓存不一致。这个问题怎么办?我们正常想到的是利用我们MQ中间件去实现。

你知道怎么解决DB读写分离,导致数据不一致问题吗?

上图的流程,如果删除缓存失败,发送消息投递到消息中间件中,进入消息队列。也许有小伙伴就会问,如果消息投递失败怎么办?我们可以利用消息中间件那边的保证100%消息投递的机制(这个以后再讲)。这样就保证了即使删除消息失败,我们也会重试。

不过这个方案有个问题,就是和我们应用服务的业务代码耦合的比较厉害。代码业务不清晰。

那我们有没有别的方案呢,对业务没有侵入呢?

你知道怎么解决DB读写分离,导致数据不一致问题吗?

上图中其实是利用了mysql的底层机制,binlog日志进行删除缓存,这样就不需要和业务关联,删除缓存服务是独立的。我们可以利用阿里开源的canal去操作。

读写分离,导致不一致

这种先更新数据库,再删除缓存的策略是不是就没有问题呢?我们来看一下另一个场景,数据库的读写分离的场景。一般中大型项目都会用到数据库的读写分离。写请求在一个库,读请求在另一个库。读写分离会有个问题,就是库与库之间会存在数据延迟,因为存在数据同步。

那我们再看一下上面的场景流程,就会有问题,因为请求B更新数据 在一个库上面,请求A去读取数据时是另一个库。

  1. 请求B更新值99,删除缓存
  2. 请求A查询值100(读库数据还没有同步),在更新到缓存中(值为100)

这样就导致不一致,这个场景是经常出现的,不是小概率事件。那我们如何处理呢?老顾下次再介绍。

总结:整个导致不一致的原因就是因为高并发情况下各个请求执行的顺序是无法确定的,不知道哪个请求先执行,哪个后执行导致。

点赞
收藏
评论区
推荐文章
blmius blmius
4年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
Wesley13 Wesley13
4年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
3A网络 3A网络
3年前
从一条更新 SQL 的执行过程窥探 InnoDB 之 REDOLOG
从一条更新SQL的执行过程窥探InnoDB之REDOLOG1前言数据库为了取得更好的读写性能,InnoDB会将数据缓存在内存中(InnoDBBufferPool),对磁盘数据的修改也会落后于内存,这时如果进程或机器崩溃,会导致内存数据丢失,为了保证数据库本身的一致性和持久性,InnoDB维护了REDOLOG。修改Page之前需要
Easter79 Easter79
4年前
sql注入
反引号是个比较特别的字符,下面记录下怎么利用0x00SQL注入反引号可利用在分隔符及注释作用,不过使用范围只于表名、数据库名、字段名、起别名这些场景,下面具体说下1)表名payload:select\from\users\whereuser\_id1limit0,1;!(https://o
Stella981 Stella981
4年前
Django之Django模板
1、问:html页面从数据库中读出DateTimeField字段时,显示的时间格式和数据库中存放的格式不一致,比如数据库字段内容为2012082616:00:00,但是页面显示的却是Aug.26,2012,4p.m.答:为了页面和数据库中显示一致,需要在页面格式化时间,需要添加<td{{dayrecord.p\_time|date:
Wesley13 Wesley13
4年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Stella981 Stella981
4年前
Redis之缓存雪崩、缓存穿透、缓存预热、缓存更新、缓存降级
\TOC\Redis之缓存雪崩、缓存穿透、缓存预热、缓存更新、缓存降级1、缓存雪崩  发生场景:当Redis服务器重启或者大量缓存在同一时期失效时,此时大量的流量会全部冲击到数据库上面,数据库有可能会因为承受不住而宕机  解决办法:    1)随机均匀设置失效
Stella981 Stella981
4年前
Redis缓存穿透、缓存雪崩、并发问题分析与解决方案
(一)缓存和数据库间数据一致性问题分布式环境下(单机就不用说了)非常容易出现缓存和数据库间的数据一致性问题,针对这一点的话,只能说,如果你的项目对缓存的要求是强一致性的,那么请不要使用缓存。我们只能采取合适的策略来降低缓存和数据库间数据不一致的概率,而无法保证两者间的强一致性。合适的策略包括合适的缓存更新策略,更新数
Python进阶者 Python进阶者
2年前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这