前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >面试如何保证数据一致性问题

面试如何保证数据一致性问题

作者头像
小土豆Yuki
发布2022-12-01 21:23:38
8670
发布2022-12-01 21:23:38
举报
文章被收录于专栏:洁癖是一只狗洁癖是一只狗

面试redis和DB数据一致性问题,也是经常被问到的,只要你建立写了redis,如果面试官想问一些场景问题,都会扯到数据一致性问题,今天我们就解读一下这个问题,按照以下思路解读

  1. 有哪些缓存模式
  2. 都有哪些优点和缺点
  3. 如何解决数据不一致

一,有哪些缓存模式

缓存模式常见的有三种,Cache-Aside Pattern(旁路缓存模式),(Read-Through/Write-Through)读写穿透,Write behind(异步缓存写入)

Cache-Aside Pattern(旁路缓存模式),读请求和写请求流程,如下图,写请求:更新的时候,先更新数据库,然后再删除缓存,读请求:读的时候:先读缓存,缓存命中的话,直接返回数据;缓存没有命中的话,就去读数据库,从数据库取出数据,放入缓存后,同时返回响应

Read-Through(读穿透),基本和旁路缓存模式一致,但是不同是多了一层缓存服务(cache provider)

Write-Through(写穿透),同时更新缓存和数据库

Write behind(异步缓存写入),他的读操作和读穿透一样,但是写操作和穿透有很大一点不同,就是他不直接更新数据库,仅仅更新缓存,等到一定时间再去异步更新数据库,他对于一致性要求很低,但是适合修改操作频繁的操作,由于内存操作,所以性能很高

二,都有哪些优点和缺点

  • 旁路缓存模式:实现简单,但是要维护数据库和缓存两个存储数据存储
  • 读写穿透模式,实现比较复杂,要多维护一个缓存服务(cache provider)
  • 读写异步模式,实现比较复杂,有数据不一致问题,但是性能好

Cache-Aside Pattern(旁路缓存模式)的一些问题,首先我们为什么要删除缓存而不是更新缓存,那肯定是有原因的其实他有三点不好的原因

  • 在并发情况下,线程A比线程B先更新数据库,但是由于某些原因,线程A比线程B晚更新缓存,就会导致缓存的数据还是老的数据,有了脏数据,而删除就不会有这种情况
  • 对于频繁写的场景,缓存就会频繁更新,浪费性能,
  • 对于频繁写的场景,缓存的值经过大量计算得到的,但是没有用几次,就会被更新的话,也是一种性能浪费

但是就有人问了,那为什么不先删除缓存,再更新数据库呢,我们来一个读写并发的操作,看图说话

我们看到,当线程A发起写操作,首先删除了缓存,线程B来时读操作,然后发现缓存没有数据,就会读取数据库,然后更新缓存,此时线程A,又过来更新了数据库,就会导致缓存和数据库不一致了,

此时可能面试官就会问,那先更新数据库,再删除缓存,不会有问题吗,其实也有问题,如下图

线程A读取数据A,发现缓存没有数据,就会读取数据库,此时还没有更新缓存,但是线程B,先更新了数据库,由于缓存没有数据,就不涉及删除缓存,但是此时线程A,把刚才读的旧数据,更新到了缓存,就会导致数据不一致问题.但是这也是概率问题,因为写入缓存的速度远远大于更新数据库的速度.

三.如何解决数据不一致

基本上使用Cache-Aside Pattern模式可以解决大部分场景,但是我们其实还可以优化,达到弱一致性,以及最终一致性的效果

  • 延时双删策略
  • 删除缓存重试机制
  • 读取binlog异步删除缓存

延迟双删策略,先删除缓存,再更新数据库,然后等待1秒,再次删除缓存,这个等待时间要根据业务处理时间适当调整,这样就是为了读请求带来的脏数据,可以再第二次删除掉,清除脏数据

删除缓存重试机制,不管是使用双删策略,还是Cache-Aside Pattern模式,如果第二步删除失败,都可能带来数据不一致问题,

因此我们就可以在删除的时候重复删除,当我们删除失败的时候,我们可以把删除的key放入到消息队列中,然后消费消息队列的key,直到删除操作成功,

取binlog异步删除缓存,使用消息队列虽然可以,但是他会浸入我们的业务逻辑,因此我们可以采用binlog日志进行解耦重复删除,如下图

我们可以使用某种机制采集数据库的binlog日志,放入到消息队列,然后搞一个简单的消费者,消费队列的消息,然后删除缓存,这样就可以保证数据最终异性了,

有人就可能问,如果是主从数据库呢,主备的操作可能有一定的延迟,数据可能还没有到从库,读请求就到达了从库,从而获取到了旧值,因此我们可以使用从库进行binlog采集,从而达到删除缓存的效果,如下图

综上的问题,我们最终得出最完美的方案,如下

  1. 读出缓存是否有数据
  2. 如果有,直接返回
  3. 如果没有,则从数据库获取,再更新缓存,然后返回
  4. 是更新操作,先更新数据库,再删除缓存
  5. 为了保证上一步删除成功,采用binlog异步删除
  6. 如果是主从数据库,则使用从库采集binlog异步删除
  7. 如果是一主多从,采集所有的从库,可以收到一条消息,删除一次,也可以收到最后一台机器的binlog,再进行删除
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2022-09-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 洁癖是一只狗 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
消息队列 CMQ 版
消息队列 CMQ 版(TDMQ for CMQ,简称 TDMQ CMQ 版)是一款分布式高可用的消息队列服务,它能够提供可靠的,基于消息的异步通信机制,能够将分布式部署的不同应用(或同一应用的不同组件)中的信息传递,存储在可靠有效的 CMQ 队列中,防止消息丢失。TDMQ CMQ 版支持多进程同时读写,收发互不干扰,无需各应用或组件始终处于运行状态。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档