缓存的双写一致性解决方案 解决redis与mysql数据一致性 看不懂的你来打我~
缓存的双写一致性
- 一. 何谓双写一致性
- 二. 解决方案
- 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数据一致性 看不懂的你来打我~相关推荐
- 【分布式】分布式环境下如何保证数据库和缓存的双写一致性?看完我明白了!!
写在前面 当今时代,互联网高速发展,已然从IT时代进入到DT时代.我们系统的架构也由原来的单体应用,转变为分布式.微服务的架构模式.从数据上来看,数据量越来越大,数据的查询性能越来越低.此时,就需要我 ...
- MySQL与Redis的双写一致性解决方案
文章目录 一.解决方案 1.1 先更新缓存,再更新数据库 1.2 先删除缓存,再更新数据库 1.3 先更新数据库,再更新缓存 二.总结 一.解决方案 1.1 先更新缓存,再更新数据库
- 数据库跟缓存的双写一致性
1 关于一致性 为加速系统性能一般都会引入缓存机制,比如 Redis.这种情况下当用户读数据时一般会按照如下流程: 读流程 关于读的流程大家是没有异议的,但是对于数据的更新呢,如何操作才算合理呢? 先 ...
- 数据库和缓存的双写一致性问题
https://www.cnblogs.com/ChenBingJie123/p/15463621.html
- 【Redis】--缓存双写一致性分析、解决方案
目录 一.缓存双写一致性场景 1.先更新数据库,再更新缓存 2.先更新数据库,再删除缓存 2.1.存在的问题一:删除缓存失败,导致Redis和mysql的数据不一致. 2.2.存在的问题二:删除缓存成 ...
- Redis面试 - 如何保证缓存与数据库的双写一致性?
Redis面试 - 如何保证缓存与数据库的双写一致性? 面试题 如何保证缓存与数据库的双写一致性? 面试官心理分析 你只要用缓存,就可能会涉及到缓存与数据库双存储双写,你只要是双写,就一定会有数据一致 ...
- Redis:缓存(双写)一致性问题
参考资料: <缓存更新的套路> <分布式之数据库和缓存双写一致性方案解析> <主从DB与cache一致性> <如何保证数据库和缓存双写一致性> < ...
- Redis缓存双写一致性、穿透(布隆)、雪崩、击穿的案例总结
目录 1.缓存的作用与成本 2.关于缓存双写一致性 2.1 四种更新策略 2.2 最佳实践方案 3.关于缓存穿透 3.1 使用 缓存空对象 方案 3.2 使用 布隆过滤器 方案 4.关于缓存雪崩 5. ...
- Redis第一讲:相关的基础知识/数据类型/缓存的过期策略/双写一致性/内存存储和持久化
Redis第一讲:相关的基础知识 摘要:本文是Redis(6.2.1)详解的第一讲,介绍Redis相关的基础知识,内存存储和持久化,Redis作缓存使用时的注意要点,常见的数据类型,缓存的过期策略,R ...
最新文章
- Jupyter Notebook学习笔记
- loganalyzer部署文档-(第一部分)
- RT-Thread助力同学们在嵌入式领域乘风破浪
- 【Network Security!】用户与组管理
- jQuery对表单元素的取值和赋值操作代码
- Java代码优化思路(JVM的角度)
- 计算机网络实验(华为eNSP模拟器)——第七章 远程终端协议(远程登陆)
- volatile指令重排_有多少人面试栽到Volatile上?面试问题都总结到这儿了
- win32开发(定时器)
- java web ssh jar_java web 汽车美容管理系统 ssh 毕设作品
- 敏捷开发中史诗故事与用户故事的颗粒度
- linux中sudo命令_Linux中的Sudo命令
- 有时我们经常碰到某目录下文件名乱码的情况,convmv可以批量转换文件名编码。...
- linux 正则查找email,linux的正则表达式查找
- hadoop2.8配置_hadoop2.8安装教程
- C++复合类型-引用变量
- 2013 Office安装aurora公式编辑器
- IDEA2019汉化教程,亲测有效,无内鬼,速
- Android音视频【七】H265硬编解码视频通话
- Steam注册遇到CAPTCHA问题,一直注册不了,一个简单的注册办法