关于java:亿级电商流量高并发下Redis与MySQL的数据一致性如何保证

59次阅读

共计 3700 个字符,预计需要花费 10 分钟才能阅读完成。

前言:

只有应用到缓存,无论是本地缓存还是应用 Redis 做缓存,那么就会存在数据同步不统一的问题。

  1. 先读取缓存,缓存数据有,则立刻返回后果
  2. 如果缓存中没有数据,则从数据库中读取数据
  3. 把读取到的数据同步到缓存中,提供下次读申请返回数据

这样的作法是大多数人应用缓存的形式,这样能无效加重数据库压力,然而如果批改删除数据,因为缓存无奈感知到数据在数据库中的批改。

这样就会造成数据库中的数据与缓存中数据不统一。

那么该如何解决呢?

有上面 4 种解决方案:

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

上面咱们一一来看下每个计划的可行性:

最近无意间取得一份阿里大佬写的刷题笔记,一下子买通了我的任督二脉,进大厂原来没那么难。

这是大佬写的,7701 页的 BAT 大佬写的刷题笔记,让我 offer 拿到手软
本文,已收录于,我的技术网站 aijiangsir.com,有大厂残缺面经,工作技术,架构师成长之路,等教训分享

注释

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

这个计划咱们个别不思考。起因是更新缓存胜利,然而更新数据库出现异常了。

会导致缓存数据与数据库数据齐全不统一,而且很难觉察,因为缓存中的数据始终都存在。

二、先更新 DB,再更新缓存

这个计划咱们个别也是不思考,起因跟计划 1 一样,数据库更新胜利了,缓存更新失败,同样会呈现数据不统一问题,且不容易被发现,因为缓存中始终存在数据。

三、先删除缓存,后更新 DB

这个计划再并发场景下也会出问题,具体呈现的起因如下:

两个并发申请:申请 A(更新操作)和申请 B(读取操作)

  1. 申请 A 会先删除 Redis 中的数据,而后去更新数据库
  2. 此时申请 B 看到 Redis 中的数据是空的,回去数据库中查问该值,补充到 Redis 缓存中
  3. 此时申请 A 并没有更新胜利,或者是事务还未提交(MySQL 的事务隔离级别,会导致未提交的事务数据不会被另一个线程看到),申请 B 去数据库查问失去旧值.

这时候就会产生数据库和 Redis 数据不统一的问题。

因而个别也不倡议这种形式

尽管不倡议,然而如果你是采纳了这种形式,该如何解决数据不统一的问题呢?

其实最简略的方法就是延时双删的策略:

  1. 先淘汰缓存
  2. 再写数据库
  3. 休眠 1s,再次淘汰缓存

这样做,能够将 1s 内所造成的缓存脏数据,再次删除。

然而,然而,这个 1s 怎么确定的,具体该休眠多久呢?

  1. 自行评估本人的我的项目的读数据业务逻辑的耗时(这个咱们能够利用 SkyWalking 等监控工具评估耗时)
  2. 评估写数据的休眠工夫(在读数据业务耗时的根底上,加几百 ms 即可)

这样做的目标,就是确保读申请完结,写申请能够删除读申请造成的缓存脏数据。

延时双删就能彻底解决不统一吗?如果面试官这样问你,你千万不能答复是的。

第一,咱们评估的延时工夫(读申请耗时 + 几百毫秒),并不能齐全代表理论运行过程中的耗时,运行过程如果因为零碎压力过大,咱们评估的耗时就是不精确,依然会导致数据不统一的呈现

第二,延时双删尽管在保障事务提交完当前再进行删除缓存,然而如果你应用的是 MySQL 的读写拆散的机构,主从同步之间其实也会有时间差。

此时该如何解决呢?

解决办法有两个:

  1. 还是应用延时双删策略,只是睡眠工夫改为在主从同步的延时工夫根底上,加几百毫秒(读接口耗时 + 主从延迟时间 + 几百毫秒)
  2. 对 Redis 进行填充数据查问(更新缓存时查询数据库),强制走主库查问,那么咱们延时双删就没必要减少主从延时工夫了(减少个主从延时工夫也会减少更大的不确定性,因为主从延时工夫也是不稳固的)

如果面试官持续深刻的问你,采纳这种同步延时双删的淘汰策略,接口的吞吐量升高怎么办?(数据变更时,更新接口都要多休眠一个延时工夫)

既然同步会升高吞吐量,那就同步改异步(性能优化的罕用伎俩)。

将第二次删除的操作,异步起一个线程,异步删除,这样写的申请就不必沉睡一段时间后能力返回了。

总的来说,先删除缓存,再更新数据库的形式,还是瑕疵较多,产生数据一致性的问题和性能问题的概率更大。比方:

  1. 先删除缓存可能导致读申请因缓存缺失而大量拜访数据库(尤其是高并发场景的电商,可能一瞬间就把数据库打挂了)
  2. 读申请接口的耗时和写缓存的工夫,估算不够精确,会导致提早双删中的 sleep 工夫不好设置

上面咱们来看最初一种解决方案,这个解决形式是 4 个计划中产生数据不一致性的概率最低的。

四、先更新 DB,后删除缓存

读的时候,先读缓存,缓存没有的话,就读数据库,而后取出数据后放入缓
存,同时返回响应。更新的时候,先更新数据库,而后再删除缓存。

这种计划下就不存在数据不一致性的问题了么?

其实是仍然存在的,尤其是在大型互联网电商,高并发零碎中,并发问题导致的数据一致性的数据量十分大。

假如两个申请,申请 A 和申请 B,申请 A 做查问操作(读申请),申请 B 做更新操作(写申请)

当高并发场景下,会有如下情景呈现:

  1. 缓存刚好生效
  2. 申请 A 查询数据库,失去一个旧值
  3. 申请 B 将新值写入数据库
  4. 申请 B 删除缓存
  5. 申请 A 将查到的旧值写入缓存

高并发场景下,的确有可能会产生上述的状况,产生脏数据。

然而,产生这种的概率又有多少呢?

产生上述情况的一个先天性条件,就是步骤(3)的写数据库操作比步骤(2)的读数据库操作耗时更短,才有可能使得步骤(4)先于步骤(5)。

可是,大家想想,数据库的读操作的速度远快于写操作的(不然做读写拆散干嘛,做读写拆散的意义就是因为读操作比拟快,耗资源少)。

因而步骤(3)耗时比步骤(2)更短,这一情景很难呈现。

然而,如果面试官问你:如果我的业务属性要求肯定要解决怎么办?那么如何解决上述并发问题?

首先,给缓存设置过期工夫是一种无效的计划。

如果你的业务数据对实时性要求不是很高,能够承受数据的短时间数据不统一的场景,咱们此种计划就能够解决了(比方商品详情中的形容、属性等)

其次,仍能够采纳异步延时删除的策略。

参考计划 3 中的异步延时删除策略计划,删除的计划其实还有问题,这个咱们放在前面说

个别采纳这些伎俩简直就曾经把 Redis 缓存和数据库数据不统一的概率降到了极低。

如果非要强一致性,极低的数据不统一的概率都不能承受,那么该如何解决呢?

其实也有解决方案:那就是加锁,在读申请加一个读锁,所有的读申请不阻塞,在写申请加一个写锁,一旦有写申请,则临时阻塞读,等写申请解决完,删除完缓存再放开读。

如果你的业务并发要求不高,读多写少,且对数据一致性有很高的要求,能够采纳这种计划,然而保障强一致性的同时,就会损失一些性能,所以该不该用这种计划,大家能够依据本人业务的属性做好衡量。

计划补充(重要)

3、4 都属于删除缓存类,其实删除缓存类都会有一个独特的问题,那就是在删除缓存的阶段出错了怎么办?此时再读取缓存的时候每次都是谬误的数据了。

此时解决方案有两个:

一、利用音讯队列进行删除失败的弥补

具体的业务逻辑如下:

  1. 申请 A 先对数据库进行更新操作
  2. 在对 Redis 进行删除操作的时候发现报错,删除失败
  3. 此时将 Redis 的 key 作为音讯体发送到音讯队列中
  4. 零碎接管到音讯队列发送的音讯后
  5. 再次对 Redis 进行删除操作

然而这个计划会有一个毛病,就是会对业务代码造成大量的侵入,深深的耦合
在一起。

所以还有一个优化的计划

二、订阅 MySQL 的 binlog 日志,异步删除

咱们晓得对 Mysql 数据库更新操作后,在 binlog日志中咱们都可能找到相应的操作,那么咱们能够订阅 Mysql 数据库 的 binlog日志对缓存进行操作,这样就达到了一个解耦的目标了。

业务代码流程如下:

  1. 更新数据库,更新实现后,触发 binlog 音讯
  2. 常常 B(消费者)订阅 binlog 音讯,执行缓存删除操作
  3. 缓存删除失败,将删除工作丢到音讯队列中
  4. 过程 B 获取删除失败工作
  5. 执行二次删除 redis 缓存

说到底就是通过数据库的 binlog 来异步淘汰 key,利用工具 (canal) 将 binlog
日志采集发送到 MQ 中,而后通过 ACK 机制确认解决删除缓存。
先更新 DB,后删除缓存,这种形式,被称为 Cache Aside Pattern,属于缓存更新的 经典设计模式 之一。

所以如果大家做缓存与数据库的同步,举荐大家抉择这一种形式。

总结

至此,亿级电商流量,高并发下 Redis 与 MySQL 的数据一致性如何保障的计划,十分圆满了。以上的内容,如果大家能烂熟于心、对答如流、一五一十,基本上 面试官会被你 震惊到、吸引到。

最终,让面试官爱到“不能自已、口水直流”。offer,也就来了。

文末留个问题给大家:

文中提到第 4 种计划(先更新数据库,后删除缓存)被称为 Cache Aside Pattern,属于缓存 更新的经典设计模式之一。

问题: 那大家还晓得其余缓存更新的经典设计模式吗?

说在最初

求一键三连:点赞、分享、珍藏

点赞对我真的十分重要!在线求赞,加个关注我会非常感激!

最近无意间取得一份阿里大佬写的刷题笔记,一下子买通了我的任督二脉,进大厂原来没那么难。

这是大佬写的,7701 页的 BAT 大佬写的刷题笔记,让我 offer 拿到手软

本文,已收录于,我的技术网站 aijiangsir.com,有大厂残缺面经,工作技术,架构师成长之路,等教训分享

求一键三连:点赞、分享、珍藏

点赞对我真的十分重要!在线求赞,加个关注我会非常感激!

正文完
 0