专栏首页moon聊技术双写兜兜转转,又回到了串行化的方式

双写兜兜转转,又回到了串行化的方式

什么是双写?


我们开门见山,这个很好理解,双写就是说,一份数据在数据库存一份,在缓存中也存一份,给缓存一个过期时间,当读不到缓存时从数据库读出来然后写入缓存

为什么需要双写呢?


当请求量越来越大的时候,系统会慢慢出现瓶颈,由于数据库的链接是有限的,无法支撑较高的QPS,所以我们要想一个办法分担数据库的压力,于是就有了双写,将数据写入缓存,客户端读取数据直接从缓存中读取,这样就可以提高系统的性能

但是如果要使用双写,那么不管是先更新缓存还是先更新mysql,总会有时间间隔,那么就要保证你的业务在一定程度上允许短暂的数据不一致的情况出现,否则,还是不建议使用的.

那么就有人问了?双写一定不能保证强一致性吗?

答案是可以,只要把所有与其相关的读写请求用队列串行化,这样就可以保证双写的强一致性了,但是这样会极大的降低系统的QPS,非常不推荐这种做法。

既然要双写,那么肯定会出现数据库和缓存数据不一致的情况,要怎样去避免呢?

双写不一致问题要怎么解决


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

这种情况会有什么问题呢?我们看下图:

首先a先更新数据库,按照正常流程来走,紧接着要a线程删除缓存,可是突然后面来了个b线程,并且a线程因为各种业务原因卡住了,导致b线程先完成了,之后a线程才更新缓存。这时突然有其他线程进来读数据,就会读到a的数据,但是按照业务流程来走,应该读到b的数据,此时,就出现了数据错乱的问题。

  • 1.线程a更新数据库
  • 2.线程b更新数据库
  • 3.线程b更新缓存
  • 4.线程a更新缓存
  • 5.其他线程读数据(读错了)

到这里我们会发现,直接更新缓存是有很大的问题的,而且很多时候,在复杂点的缓存场景,缓存不单单是数据库中直接取出来的值,有可能是联合其他的很多数据结合计算出来的一个值。

而且可能会有一种场景,我们经常在更新数据库后直接更新缓存,但是在此之间并没有缓存被访问的需求,这样我们就做了很多无用功,付出了很多代价。

大家应该对单例模式有所了解,其中有一种懒加载的思想,就是说,在你需要的时候再去加载,用在双写的情况下非常合适,也就有了下面这种先更新数据库,再删除缓存的模式。

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

这种情况又会有什么问题呢?

当然,这还是一种有问题的方案,我们来跟着图盘一盘。

  • 1:线程a更新数据库
  • 2:程序挂了,没来的及删除缓存
  • 3.其他线程来读数据(全都是错的)

这种方案的问题一目了然,只要程序挂了,就会出现数据读错的情况,真实的业务你是应该读到a线程的值,却一直在读之前的值。

那这种方案有没有优化呢?

当然也有了,其实我们可以每次写入都记录日志,然后修改结束后也记录日志,通过日志状态来判断是否写入成功,

  • 如果没有写入成功后续并且没有新的写入请求,就补写,
  • 否则不做处理。

但是这种情况也会出现不一致的问题,就是如果写数据库程序断了,到下次恢复数据之前这段时间,还会出现数据不一致的情况。

并且如果是频繁写入的情况,很有可能日志机制没有发挥作用,就有新数据写入覆盖,并且日志系统还要占用额外的资源。

我懂了!应该先删除缓存再更新数据库,这样就可以了!

三.先删除缓存 再更新数据库

来来来,继续贴图,是不是很熟悉?

这种方案会有问题吗??当然有,继续盘道:

  • 1:线程a删除缓存
  • 2:线程b删除缓存
  • 3:线程a卡了
  • 4:线程b更新数据库
  • 5:线程a更新数据
  • 6:其他线程读数据,读到了a的(又错了)

完了,这种情况居然也有问题,线程a到底行不行,每次都是你出事。

这种情况中间会有一段数据乱掉,但是随着下次的更新数据还是会恢复正确。

难道终极方案是先删除缓存,再更新数据库,再更新缓存??

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

继续贴图

  • 1.线程a删除缓存
  • 2.其他线程读取数据,读到的是a之前的数据
  • 3.线程a更新数据库
  • 4.线程a删除缓存
  • 5.其他线程设置缓存数据,是a之前的数据(此时应该是a的)

大家是不是又发现了,这种设计方案还是会有问题的,直到下次数据更新才有可能将数据恢复正确。

来吧,最后一种大家经常讨论的延时双删方案,我们一起盘一盘。

五.延时双删

go on

  • 1.先删除缓存
  • 2.再写数据库
  • 3.休眠一段时间(根据具体的业务时间来定)
  • 4.再次删除缓存

这里加了一个延时的操作,目的是确保 修改数据库 -> 清空缓存前,其他事务的更改缓存操作已经执行完。

所有的写操作以数据库为准,只要到达缓存过期时间,则后面的读请求自然会从数据库中读取新值然后回填缓存。

这其中难免还是会大量的查询到旧缓存数据的,因为延时时间是根据业务自己定义的,时间太长和太短在高并发情况下都会有查询到脏数据的情况产生。

这样最差的情况就是在超时时间内数据存在不一致

结语


到这里大家应该会发现,除了串行化这种方式以外,其他无论哪种方式大大小小都会有数据不一致的现象发生,有时为了维护数据一致性问题还要做很多额外很重的操作,比如加一些日志来做状态处理双写问题,具体的方案选择还是要根据业务的敏感度来定的。

本文分享自微信公众号 - moon聊技术(onetraveller_llxz),作者:moon聊技术

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2021-04-14

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 5. 穿过拥挤的人潮,Spring已为你制作好高级赛道

    上篇文章 大篇幅把Spring全新一代类型转换器介绍完了,已经至少能够考个及格分。在介绍Spring众多内建的转换器里,我故意留下一个尾巴,放在本文专门撰文讲解...

    YourBatman
  • 如何巧妙的解决问题

    这种问题解决方法有很多,比如:可以使用递归,我们写一个函数,功能如下:使用表2中的上手编号在表2中的档案号中进行查找;判断该档案号是否有上手编号;如果有继续调用...

    数据处理与分析
  • “律兜”开放API接口,打造智能互联网法律平台

    在互联网浪潮下,“律兜”一直坚持发展创新,不断升级产品和服务,尤其是自开放其API接口,进行跨平台合作以来,“律兜”获得了前所未有的成绩,跻身于我国互联网法律行...

    BestSDK
  • springClound --- 中级篇(1)

    本系列笔记涉及到的代码在GitHub上,地址:https://github.com/zsllsz/cloud

    贪挽懒月
  • STM32F103的GPIO与DMA的终极(没啥用)玩法

    最近在玩STM32的DMA,各种玩法都想试试。突发奇想,DMA能否连接GPIO与内存?也就是说通过DMA直接把一个数组的值快速发送到GPIO,或者通过DMA读取...

    MCU起航
  • 链表看这一篇真的就够了!

    有的小伙伴说没有学过数据结构,对链表不是特别了解,所以今天我们就来对链表进行一个系统的总结,另外大家如果想提高算法思想的话,我建议还是要系统的学一下数据结构的。

    公众号袁厨的算法小屋
  • 消息队列解耦是骗小孩儿的

    想法很简单,在业务状态流转时,如果没有 MQ,那么其它系统想要知道状态变了,那就需要核心流程系统去主动做通知。

    KevinYan
  • 数据迁移与一致性思考与实践

    在上一篇中我们讲了通用优惠券系统的设计,这篇主要是以优惠券重构后,我们现有系统接入到该通用优惠券系统过程中遇到的数据迁移与一致性问题相关的思考与实践。我们早期的...

    榴莲其实还可以
  • 【Vulnhub】DC-8

    john 密码文件 –wordlist ="字典" 不加字典路径的话就用默认的字典

    yichen
  • AI自动化测试就这样被实现了(四)完结

    前面整体介绍了AI自动化简单原来和要实现的功能,接下来,我们来聊一聊,这个routers功能给我们带来的哪些好处,来帮助我们提高测试效率和保证项目质量 提高效...

    厦门-安仔
  • Lampiao靶机渗透

    众所周知计算机开放的最大端口数为 65535,但是nmap默认扫描的端口范围只有 1-1000所以扫描 1-65535端口开还有什么端口开放的

    Elapse
  • 摸索出来的chrom调试前后台数据(Java&&Ajax)交互的方法分享一下咯!!!

    1:开始没想分享的,后来看到有大佬分享如何使用Chrom的工具进行调试,哈哈哈哼,我就借着他的博客写一下我摸索的如何进行前后台数据交互吧(注:反正是自己瞎  捣...

    别先生
  • 【Vue原理】Compile - 源码版 之 从新建实例到 compile结束的主要流程

    【Vue原理】Compile - 源码版 之 从新建实例到 compile结束的主要流程

    神仙朱
  • TkMybatis 是什么?

    Tkmybatis 是基于 Mybatis 框架开发的一个工具,通过调用它提供的方法实现对单表的数据操作,不需要写任何 sql 语句,这极大地提高了项目开发效率...

    JMCui
  • 兜兜转转一个圈,闲聊一下What is all you need?

    最近读论文、看文章发现了两件有意思的事情,今天有时间分享闲聊一下,其一是各种MLP的论文频出,从各个方面对Transformer进行“围攻”,这让人有种“大...

    炼丹笔记
  • vivo 应用商店推荐系统探索与实践

    商店的应用数据主要来源于运营排期、CPD、游戏、算法等渠道,成立推荐项目之后也没有变化,发生变化的是由推荐系统负责和数据源进行对接,商店服务端只需要和应用推荐系...

    2020labs小助手
  • 为实习准备的数据结构(2)-- 详尽链表篇

    链表在C语言的数据结构中的地位可不低。后面很多的数据结构,特别是树,都是基于链表发展的。

    看、未来
  • APP Https双向认证抓包

    在一次测试中偶然遇到一个https双向认证的手机app(fiddler抓包提示需要提供客户端证书),平时一梭子能搞定地抓包姿势没有效果了,本着所有客户端发出的数...

    安恒网络空间安全讲武堂
  • 被 leeder 摆了一道,哭笑不得!

    上一周我写一了篇,数据库和缓存双写一致性的文章「老板真爱画大饼!」,故事的主人公是程序员阿旺。

    小林coding

扫码关注云+社区

领取腾讯云代金券