缓存的双写一致性

  • 一. 何谓双写一致性
  • 二. 解决方案
    • 2.1 先更新数据库,再更新缓存
      • 2.1.1 问题1 每秒一万次改请求,一次读请求?
    • 2.2 先删除缓存,再更新数据库
    • 2.3 先更新数据库,再删除缓存
    • 2.4 最终问题
  • 制作不易,转载都请标注 ~ ~你看不懂,你也打不着我啊~

前言

此篇章为分析如何将redis的缓存与mysql数据同步的解决方案,本人参照各路大神的解决方案汇聚而成,难免会有错误的地方,肯定各路大神评论区无情鞭挞~~

一. 何谓双写一致性

你只要用缓存,就可能会涉及到缓存与数据库双存储双写,你只要是双写,就一定会有数据一致性的问题,那么你如何解决一致性问题?

我想,已经有小伙伴有了好的方案了,为什么我们不能直接将其中读请求与写请求串行化,全部整到一个队列中去消费,不就一定不会出现不一致的情况了吗?确实,但是这样做会严重降低系统的高可用型,你想想你的程序每秒能跑多少,你放到队列中又能跑多少呢

二. 解决方案

目前,已知三种解决方案,让我们来好好看看这其中的区别

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

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

首先,当我们一个改数据的请求进来,我们先更新数据库的值,然后再把缓存中的值去更新,当我们下一次请求进来的时候保证数据是最新值

这其中有问题吗?

2.1.1 问题1 每秒一万次改请求,一次读请求?

试想如上情况,每秒一万次改请求,那我数据库会一直更新,而且当我们数据库更新的时候也会更新缓存值,那我其实只有一次读请求,那我更新这一万次缓存是不是没意义? 而且会造成我们系统很大开销,而缓存也没被访问几次


那不如我们…

删除缓存如何? 试想一下,要是我们直接每次更新数据库的时候,不去更新缓存,而是直接删除缓存,是不是情况就简单很多,每秒一万次请求,改数据库然后删除缓存,那我们的缓存并没有持续更新,也不存在大开销,并且下次我们查请求进来,也只不过多了一次生成缓存的操作

这点其实用了懒加载的思想,我们只有用到的时候采取生成,不用的时候就一直不用,是不是很好? 下面我们来讨论这种方案

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

唉,既然上面这样分析了,其实还是又出现了新的问题,那我们是先删除缓存还是先更新数据库呢?


我们设想有如下情景

  • (1)请求A进行写操作,删除缓存
  • (2)请求B查询发现缓存不存在
  • (3)请求B去数据库查询得到旧值
  • (4)请求B将旧值写入缓存
  • (5)请求A将新值写入数据库

如上,我们来分析一下,当请求A进行写操作,刚刚删除缓存还没来得及更新数据库,突然cpu任务调度停了,B请求查询后没缓存,就去数据库查询旧值,查到后缓存这个旧值,那我们这时A请求又进行了,去把数据库的值更新了,这时候会出现什么情况?缓存是旧值,但是数据库是新值,出现数据不一致,而且每次有新请求进来,在缓存没失效的时候,永远查的都是旧值.

这样会一直出现脏数据,那我们怎么解决呢?

延时双删策略

例如在我们A请求进行写操作的时候,第一次不是删除了缓存吗,但容易被B请求更新成脏数据,那我们就等一段时间(休眠一段时间)再进行删除缓存,是不是可以把之前B请求的脏数据删除掉,下次新请求进来查询的时候查不到缓存,会去数据库缓存最新值

但是,又抛出一些问题,如何确定这个睡眠时间是多少呢?根据自己的业务可以调整休眠时间,也有不确定性,其次要是你的数据库是读写分离架构会出现如下问题

  • (1)请求A进行写操作,删除缓存
  • (2)请求A将数据写入数据库了,
  • (3)请求B查询缓存发现,缓存没有值
  • (4)请求B去从库查询,这时,还没有完成主从同步,因此查询到的是旧值
  • (5)请求B将旧值写入缓存
  • (6)数据库完成主从同步,从库变为新值

这样也会出现数据不一致的情况,解决的策略还是延时双删策略,适当将休眠时间延长,在主从同步后进行双删,于是这样加的时间值以及牺牲系统吞吐量来解决是不可取的,所以我们开另外一个线程进行异步删除,不用再休眠系统

但是问题最终解决了吗?


如果我们删除失败怎么办?

比如在上面延时双删的情况下,我们第二次删除失败,那前面一切不都是白做了?瓶颈口就在这里,只要第二次删除缓存失败,一切都白给了~~

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

老外提出了一个缓存更新套路,名为 Cache-Aside pattern 。

另外,知名社交网站facebook也在论文《Scaling Memcache at Facebook》中提出,他们用的也是先更新数据库,再删缓存的策略。

更新:先把数据存到数据库中,成功后,再让缓存失效。

首先,我们再来分析一下这种模式会出现什么问题

  • (1)缓存刚好失效
  • (2)请求A查询数据库,得一个旧值
  • (3)请求B将新值写入数据库
  • (4)请求B删除缓存
  • (5)请求A将查到的旧值写入缓存

也会出现缓存与数据库不一致的情况,但其实这种情况出现的概率很低

首先,这种情况出现在读请求穿插写请求中,且读请求用时比写请求长,从上面情况来分析不难得出.其次数据库写操作时长是远远大于读操作时长的,这也是为什么要数据库读写分离的原因,所以出现上述条件是非常苛刻的,再来说说为什么先删除缓存再写数据库这种情况的意外发生概率高,主要是因为写操作中穿插读操作,而读操作时长非常短, 极易造成数据不一致

毕竟我们是优秀的程序员嘛,但其实如果要是真发生上述情况的话

开玩笑~ 那我们还是采用延时双删策略,保证读请求后删除缓存即可

2.4 最终问题

那我们就是会出现删除缓存失败的时候嘛,那你怎么解决呢?

这里借鉴孤独烟大神的做法,只能怪自己太菜了唉~

方案一

流程如下所示
(1)更新数据库数据;
(2)缓存因为种种问题删除失败
(3)将需要删除的key发送至消息队列
(4)自己消费消息,获得需要删除的key
(5)继续重试删除操作,直到成功

然而,该方案有一个缺点,对业务线代码造成大量的侵入。于是有了方案二,在方案二中,启动一个订阅程序去订阅数据库的binlog,获得需要操作的数据。在应用程序中,另起一段程序,获得这个订阅程序传来的信息,进行删除缓存操作。

方案二

流程如下图所示:
(1)更新数据库数据
(2)数据库会将操作信息写入binlog日志当中
(3)订阅程序提取出所需要的数据以及key
(4)另起一段非业务代码,获得该信息
(5)尝试删除缓存操作,发现删除失败
(6)将这些信息发送至消息队列
(7)重新从消息队列中获得该数据,重试操作。

借鉴大神操作 附上大神博客

制作不易,转载都请标注 ~ 你看不懂,你也打不着我啊

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

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

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

  2. MySQL与Redis的双写一致性解决方案

    文章目录 一.解决方案 1.1 先更新缓存,再更新数据库 1.2 先删除缓存,再更新数据库 1.3 先更新数据库,再更新缓存 二.总结 一.解决方案 1.1 先更新缓存,再更新数据库

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

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

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

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

  5. 【Redis】--缓存双写一致性分析、解决方案

    目录 一.缓存双写一致性场景 1.先更新数据库,再更新缓存 2.先更新数据库,再删除缓存 2.1.存在的问题一:删除缓存失败,导致Redis和mysql的数据不一致. 2.2.存在的问题二:删除缓存成 ...

  6. Redis面试 - 如何保证缓存与数据库的双写一致性?

    Redis面试 - 如何保证缓存与数据库的双写一致性? 面试题 如何保证缓存与数据库的双写一致性? 面试官心理分析 你只要用缓存,就可能会涉及到缓存与数据库双存储双写,你只要是双写,就一定会有数据一致 ...

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

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

  8. Redis缓存双写一致性、穿透(布隆)、雪崩、击穿的案例总结

    目录 1.缓存的作用与成本 2.关于缓存双写一致性 2.1 四种更新策略 2.2 最佳实践方案 3.关于缓存穿透 3.1 使用 缓存空对象 方案 3.2 使用 布隆过滤器 方案 4.关于缓存雪崩 5. ...

  9. Redis第一讲:相关的基础知识/数据类型/缓存的过期策略/双写一致性/内存存储和持久化

    Redis第一讲:相关的基础知识 摘要:本文是Redis(6.2.1)详解的第一讲,介绍Redis相关的基础知识,内存存储和持久化,Redis作缓存使用时的注意要点,常见的数据类型,缓存的过期策略,R ...

最新文章

  1. Jupyter Notebook学习笔记
  2. loganalyzer部署文档-(第一部分)
  3. RT-Thread助力同学们在嵌入式领域乘风破浪
  4. 【Network Security!】用户与组管理
  5. jQuery对表单元素的取值和赋值操作代码
  6. Java代码优化思路(JVM的角度)
  7. 计算机网络实验(华为eNSP模拟器)——第七章 远程终端协议(远程登陆)
  8. volatile指令重排_有多少人面试栽到Volatile上?面试问题都总结到这儿了
  9. win32开发(定时器)
  10. java web ssh jar_java web 汽车美容管理系统 ssh 毕设作品
  11. 敏捷开发中史诗故事与用户故事的颗粒度
  12. linux中sudo命令_Linux中的Sudo命令
  13. 有时我们经常碰到某目录下文件名乱码的情况,convmv可以批量转换文件名编码。...
  14. linux 正则查找email,linux的正则表达式查找
  15. hadoop2.8配置_hadoop2.8安装教程
  16. C++复合类型-引用变量
  17. 2013 Office安装aurora公式编辑器
  18. IDEA2019汉化教程,亲测有效,无内鬼,速
  19. Android音视频【七】H265硬编解码视频通话
  20. Steam注册遇到CAPTCHA问题,一直注册不了,一个简单的注册办法

热门文章

  1. 程序人生 - 在德国,炒房是要坐牢的
  2. hexo教程:搜索SEO+阅读量统计+访问量统计+评论系统
  3. 2018 年终总结 2019 年度计划
  4. 【springboot微服务】Lucence实现Mysql全文检索
  5. 使用bat脚本启动指定目录下的jar包
  6. 91 案例——图片放大特效
  7. 保定街道电表箱墙绘案例欣赏
  8. 逐浪软件智图->全网发布∞面向企业级的智能图库
  9. 主元素问题—蒙特卡罗算法
  10. 同一纬度,为什么大兴安岭是森林,呼伦贝尔是草原?