来源 | moon聊技术

责编 | 寇雪芹

头图 | 下载于ICphoto

什么是双写?这个很好理解,双写就是说,一份数据在数据库存一份,在缓存中也存一份,给缓存一个过期时间,当读不到缓存时从数据库读出来然后写入缓存。

为什么需要双写呢?

当请求量越来越大的时候,系统会慢慢出现瓶颈,由于数据库的链接是有限的,无法支撑较高的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.再次删除缓存

这里加了一个延时的操作,目的是确保 修改数据库 -> 清空缓存前,其他事务的更改缓存操作已经执行完。所有的写操作以数据库为准,只要到达缓存过期时间,则后面的读请求自然会从数据库中读取新值然后回填缓存。

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

结语

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

60+专家,13个技术领域,CSDN 《IT 人才成长路线图》重磅来袭!

直接扫码或微信搜索「CSDN」公众号,后台回复关键词「路线图」,即可获取完整路线图!

更多精彩推荐
☞5G、射频、奥特曼,这仨有联系吗?☞再见 Nacos,我要玩 Service Mesh 了!☞用根因定位法,让运维效率再高一点!
点分享点收藏点点赞点在看

聊聊缓存机制:双写兜兜转转,又回到了串行化相关推荐

  1. 【分布式】分布式环境下如何保证数据库和缓存的双写一致性?看完我明白了!!

    写在前面 当今时代,互联网高速发展,已然从IT时代进入到DT时代.我们系统的架构也由原来的单体应用,转变为分布式.微服务的架构模式.从数据上来看,数据量越来越大,数据的查询性能越来越低.此时,就需要我 ...

  2. 读数据库遇到空就进行不下去_如何解决高并发场景下缓存+数据库双写不一致问题?...

    推荐阅读: 一只Tom猫:手撕分布式技术:限流.通讯.缓存,全部一锅端走送给你!​zhuanlan.zhihu.com 一只Tom猫:MySQL复习:20道常见面试题(含答案)+21条MySQL性能调 ...

  3. 缓存的双写一致性解决方案 解决redis与mysql数据一致性 看不懂的你来打我~

    缓存的双写一致性 一. 何谓双写一致性 二. 解决方案 2.1 先更新数据库,再更新缓存 2.1.1 问题1 每秒一万次改请求,一次读请求? 2.2 先删除缓存,再更新数据库 2.3 先更新数据库,再 ...

  4. MFC六大核心机制之四:永久保存(串行化)

    永久保存(串行化)是MFC的重要内容,可以用一句简明直白的话来形容其重要性:弄懂它以后,你就越来越像个程序员了! 如果我们的程序不需要永久保存,那几乎可以肯定是一个小玩儿.那怕我们的记事本.画图等小程 ...

  5. 数据库跟缓存的双写一致性

    1 关于一致性 为加速系统性能一般都会引入缓存机制,比如 Redis.这种情况下当用户读数据时一般会按照如下流程: 读流程 关于读的流程大家是没有异议的,但是对于数据的更新呢,如何操作才算合理呢? 先 ...

  6. Redis:缓存(双写)一致性问题

    参考资料: <缓存更新的套路> <分布式之数据库和缓存双写一致性方案解析> <主从DB与cache一致性> <如何保证数据库和缓存双写一致性> < ...

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

    在实际业务中,经常碰见数据库和缓存中数据不一致的问题,缓存作为抵挡前端访问洪峰的工具,用的好的话可以大大减轻服务端压力,但是在一些场景下,如果没有控制好很容易造成数据库和缓存的数据不一致性,尤其是在并 ...

  8. 数据库和缓存的双写一致性问题

    https://www.cnblogs.com/ChenBingJie123/p/15463621.html

  9. 《IT老外在中国》第29期:这位美籍华裔兜兜转转又回到了心心念念的故乡

    故土难离对于许多人来说只是一个简单的词语,但在真正离开过家乡的人心中,这个词语却有着不一样的重量.本期<IT老外在中国>的主角是一位美籍华人,十几岁移民美国,后来在机缘的驱使下又让他回到了 ...

最新文章

  1. boost program_options
  2. 一次性掌握ES6/ES7异步处理
  3. iOS单例创建的一点疑惑
  4. sqlplus登录Oracle时ORA-01017: invalid username/password; logon denied的错误
  5. android日历长按拖拽,CalendarView
  6. python 修改文件属性 macos_Python中用MacFSEvents模块监视MacOS文件系统改变一例
  7. 卷积神经网络创建模型
  8. python发音-Python如何实现文本转语音
  9. 3月20日 表单
  10. DSP SRIO接口设计
  11. PyQt5+opencv鼠标滚轮设置图片亮度
  12. ubuntu 屏幕亮度无法调节
  13. SpringCloud分布式架构演进
  14. CKEditor编辑器的详细使用
  15. FTT暴雷加密熊市雪上加霜?如何对抗系统风险
  16. CentOS 安装HTTP代理服务器Tinyproxy---配置简捷
  17. CSR8670学习笔记:OTA升级固件
  18. 小白重装系统教程_小白U盘重装原版win7系统教程
  19. html上传后门,网站查后门软件 WebShellKiller
  20. 访问网络中的计算机密码忘了怎么办,忘记wifi密码怎么办,用这招可以知道电脑中的wifi密码...

热门文章

  1. android 模拟长按菜单键_如何采用PLC梯形图实现单键启动程序
  2. java rpm包安装_rpm包安装java jar开机自启
  3. 方言大全_长沙人亲戚称呼大全!记得收藏以防失传!
  4. qlistview 自定义控件_是否可以在QListView中添加自定义窗口小部件?
  5. java单链表节点翻转_Java数据结构01-链表基础(讲解+代码+面试题)
  6. java resttemplate_java-通过resttemplate通过Spring Rest服务发送文...
  7. centos安装mysql5.6系统崩溃_CentOS7安装MySQL5.6冲突总结
  8. linux 磁盘扩容_记录一次ESXi Linux在线扩容,不重启系统
  9. 两教授吐槽:如今博士研究生的论文写作水平为何如此堪忧?
  10. 吉林大学不如温州大学,泰晤士最新世界大学排名引发争议