Redis---缓存双写一致性

目录

一、什么是缓存双写一致性呢?

 1.1 双检加锁机制

 二、数据库和缓存一致性的更新策略

2.1、先更新数据库,后更新缓存

 2.2 、先更新缓存,后更新数据库

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

延时双删的策略:

 2.4.先更新数据库,在删除缓存(常用)

2.5、实际中是不可能做到强一致性的,那么怎么做到最终一致性呢?

三、canal中间件

3.1   canal工作原理

3.2 MySQL的主从复制 


一、什么是缓存双写一致性呢?

  • 如果redis中有数据

    • 需要和数据库中的值相同
  • 如果redis中无数据

    • 数据库中的值是最新值,且准备回写redis

缓存按照操作分

  • 只读缓存    (就没有同步这一说法了)
  • 读写缓存
    • 同步直写策略   (比如比较紧急的事情,冲了vip得立即生效
      • 写数据库后也同步写 redis 缓存,缓存中的数据和数据库中的一致
      • 对于读写缓存来说,要想保证缓存和数据库中的数据一致
  • 异步缓写策略   (一般都是用这种)
    • 正常业务运行中,mysql数据变动了,但是可以在业务上容许出现一定时间后才作用于redis,比如仓库、物流系统
    • 异常情况出现了,不得不将失败的动作重新修补,有可能需要借助kafka或者RabbitMQ等消息中间件,实现重写重试

 1.1 双检加锁机制

加锁前从redis中查一次,加锁后再查一次。

多个线程同时去查询数据库的这条数据,那么我们可以在第一个查询数据的请求上使用一个 互斥锁来锁住它

其他的线程走到这一步拿不到锁就等着,等第一个线程查询到了数据,然后做缓存。

后面的线程进来发现已经有缓存了,就直接走缓存。 

 二、数据库和缓存一致性的更新策略

一般都是以MySQL为准。

给缓存设置过期时间,定期清理缓存并回写,是保证最终一致性的解决方案。

我们可以对存入缓存的数据设置过期时间,所有的写操作以数据库为准,对缓存操作只是尽最大努力即可。也就是说如果数据库写成功,缓存更新失败,那么只要到达过期时间,则后面的读请求自然会从数据库中读取新值然后回填缓存,达到一致性,切记,要以mysql的数据库写入库为准

2.1、先更新数据库,后更新缓存

异常一:        回写失败会出现脏数据

 异常二:       高并发下会出现数据覆盖

 2.2 、先更新缓存,后更新数据库

我们一般是不用这种的,因为我们一般都把MySQL作为根基

异常:       高并发下会出现数据覆盖

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

当有两个线程:一个线程负责删Redis,修改MySQL,  另一个来查找redis

 如果数据库更新失败或者不及时就会发生异常

(1)请求A进行写操作,删除redis缓存后,工作正在进行中,更新mysql......A还没有彻底更新完mysql,还没commit

(2)请求B开工查询,查询redis发现缓存不存在(被A从redis中删除了)

(3)请求B继续,去数据库查询得到了mysql中的旧值(A还没有更新完)

(4)请求B将旧值回写redis缓存

(5)请求A将新值写入mysql数据库 

上述情况就会导致不一致的情形出现。 

时间
线程A
线程B
出现的问题
t1
请求A进行写操作,删除缓存成功后,工作正在mysql进行中......
t2
1 缓存中读取不到,立刻读mysql,由于A还没有对mysql更新完,读到的是旧值
2 还把从mysql读取的旧值,写回了redis
1 A还没有更新完mysql,导致B读到了旧值
2 线程B遵守回写机制,把旧值写回redis,导致其它请求读取的还是旧值,A白干了。
t3
A更新完mysql数据库的值,over
redis是被B写回的旧值,
mysql是被A更新的新值。
出现了,数据不一致问题。

总结一下:

先删除缓存,再更新数据库
如果数据库更新失败或超时或返回不及时,导致B线程请求访问缓存时发现redis里面没数据,缓存缺失,B再去读取mysql时, 从数据库中读取到旧值,还写回redis,导致A白干了,o(╥﹏╥)o

改怎么解决呢?

延时双删的策略:

 这个删除该休眠多久呢?

 因为这种同步淘汰机制加上了sleep,导致MySQL吞吐量降低怎么办?

 

 2.4.先更新数据库,在删除缓存(常用)

 这一种方法的弊端相对比较少

时间
线程A
线程B
出现的问题
t1
更新数据库中的值......
t2
缓存中立刻命中,此时B读取的是缓存旧值。
A还没有来得及删除缓存的值,导致B缓存命中读到旧值。
t3
更新缓存的数据,over
先更新数据库,再删除缓存
假如缓存删除失败或者来不及,导致请求再次访问redis时缓存命中, 读取到的是缓存旧值。

2.5、实际中是不可能做到强一致性的,那么怎么做到最终一致性呢?

需要用到消息队列:kafka或者RabbitMQ

但是还是都需要是先更新数据库,再删除缓存,这样最多也就是数据暂时不一致,不会导致雪崩、击穿啥的出现。

1 可以把要删除的缓存值或者是要更新的数据库值暂存到消息队列中(例如使用Kafka/RabbitMQ等)。
2 当程序没有能够成功地删除缓存值或者是更新数据库值时,可以从消息队列中重新读取这些值,然后再次进行删除或更新。
3 如果能够成功地删除或更新,我们就要把这些值从消息队列中去除,以免重复操作,此时,我们也可以保证数据库和缓存的数据一致了,否则还需要再次进行重试
4 如果重试超过的一定次数后还是没有成功,我们就需要向业务层发送报错信息了,通知运维人员。

三、canal中间件

能够立刻感知到MySQL改变的有一个MySQL的binlog文件

我们需要一种技术来充当两者之前的吹哨人

这里有阿里研发的一种中间件canal

3.1   canal工作原理

1. canal 模仿MySQL的dump协议,假装自己是MySQL的slave,向MySQL发送dump协议

2. MySQLmaster收到dump请求之后,便会给canal推送自身bin  log 变化给canal

3. cannal收到bin  log 消息并解析。

3.2 MySQL的主从复制 

 

MySQL的主从复制将经过如下步骤:

1、当 master 主服务器上的数据发生改变时,则将其改变写入二进制事件日志文件中;

2、salve 从服务器会在一定时间间隔内对 master 主服务器上的二进制日志进行探测,探测其是否发生过改变,

如果探测到 master 主服务器的二进制事件日志发生了改变,则开始一个 I/O Thread 请求 master 二进制事件日志;

3、同时 master 主服务器为每个 I/O Thread 启动一个dump  Thread,用于向其发送二进制事件日志;

4、slave 从服务器将接收到的二进制事件日志保存至自己本地的中继日志文件中;

5、salve 从服务器将启动 SQL Thread 从中继日志中读取二进制日志,在本地重放,使得其数据和主服务器保持一致;

6、最后 I/O Thread 和 SQL Thread 将进入睡眠状态,等待下一次被唤醒;

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/649.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Matplotlib---3D图

1. 3D图 # 3D引擎 from mpl_toolkits.mplot3d.axes3d import Axes3D fig plt.figure(figsize(8, 5)) x np.linspace(0, 100, 400) y np.sin(x) z np.cos(x)# 三维折线图 axes Axes3D(fig, auto_add_to_figureFalse) fig.add_axes(axes) axes.plot(x,y,z) plt.savefi…

Runner 介绍

Runner 介绍 概述 Runner是用来批量调用collection里某个文件夹里的全部接口的。 (注意,我说的是文件夹内所有接口,可以是一级文件夹,也可是二级文件夹) 示意图 打开runner,如图所示 说明 历史记录 历史执行记录 2.导入 导入别人或之…

h5页面如何与原生交互

本文讲述h5页面跟原生通信,比如在app内,调用相机,获取相册内的图片,在app内拉起微信小程序等等,h5页面没有这么多权限能够直接调用移动端的原生能力,这个时候就需要与原生进行通讯,传递一个信号…

Go实现在线词典翻译(三种翻译接口,结合sync)

火山翻译 首先介绍用火山翻译英译汉。 package mainimport ("bufio""bytes""encoding/json""fmt""io""log""net/http""os""strings""unicode" )type DictRequestHS st…

单片机第一季:零基础6——按键

目录 1,独立按键 2,矩阵按键 (注意:文章中的代码仅供参考学习,实际使用时要根据需要修改) 1,独立按键 按键管脚两端距离长的表示默认是导通状态,距离短的默认是断开状态&#xf…

Web APIs

文章目录 1.Web APIs 和 JS 基础关联性1.1 JS 的组成 2. API 和 Web API2.1 API2.2 Web API 1.Web APIs 和 JS 基础关联性 1.1 JS 的组成 2. API 和 Web API 2.1 API **API(Application Programming Interface,应用程序编程接口)**是一些预先定义的函…

观察者模式(下):如何实现一个异步非阻塞的EventBus框架?

上一节课中,我们学习了观察者模式的原理、实现、应用场景,重点介绍了不同应用场景下,几种不同的实现方式,包括:同步阻塞、异步非阻塞、进程内、进程间的实现方式。 同步阻塞是最经典的实现方式,主要是为了…

SSH框架简介篇

文章目录 概述目录结构 strutsSpringHibernate总结 概述 SSH框架(Struts Spring Hibernate)是一种广泛应用的Java企业级开发框架组合,它将Struts、Spring和Hibernate三个优秀的框架有机地结合在一起,提供了一套完整的解决方案&…

Linux系统编程:文件系统和inode

目录 一. 磁盘的结构和读写数据的方式 1.1 磁盘级文件和内存级文件 1.2 磁盘的物理结构 1.3 访问磁盘数据的方式 二. 磁盘文件系统 2.1 磁盘的分区管理方法 2.2 文件名和inode的关系 三. 结合文件系统对文件创建和删除的相关问题的理解 3.1 文件创建时操作系统进行的工…

51单片机--DS1302时钟

文章目录 DS1302引脚定义和应用电路内部结构框图寄存器的定义时序定义BCD码DS1302时钟代码 DS1302 DS1302是美国DALLAS公司推出的一款实时时钟电路芯片。它具有高性能和低功耗的特点,可以通过SPI三线接口与CPU进行同步通信。DS1302能够提供秒、分、时、日、星期、月…

【SQL应知应会】表分区(一)• MySQL版

欢迎来到爱书不爱输的程序猿的博客, 本博客致力于知识分享,与更多的人进行学习交流 本文收录于SQL应知应会专栏,本专栏主要用于记录对于数据库的一些学习,有基础也有进阶,有MySQL也有Oracle 分区表 • MySQL版 一、分区表1.非分区表2.分区表2…

利用集合框架实现-超市会员管理系统

借助集合框架来实现超市会员管理系统,实现以下功能: 1.开卡 2.积分累计 3.查询剩余积分 4.积分兑换 5.修改密码 6.退出 -------------------------------------------------------------------------------------------------- 展示&#x…

【信号去噪和分类】基于小波的隐马尔可夫模型统计信号处理(Matlab代码实现)

💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…

C语言实现扫雷【经典】

前言   本篇文章要实现的是扫雷游戏,其代码实现与上一篇的三子棋游戏类同,都是在棋盘的基础上,与电脑进行对抗,不同的是,扫雷游戏一开始电脑就已经随机布置好了所有“雷”。 请戳 --->三子棋 扫雷游戏 1. 扫雷游…

MySQL每日一练——MySQL多表查询进阶挑战

目录 1、首先创建表 t_dept: t_emp: 2、插入数据 t_dept表: t_tmp表: 3、修改表 4、按条件查找 1、首先创建表 t_dept: CREATE TABLE t_dept (id INT(11) NOT NULL AUTO_INCREMENT,deptName VARCHAR(30) DEFAULT NULL,address VARCHAR(40) DEFAULT NULL,P…

Python结巴中文分词笔记

📚 jieba库基本介绍 🌐 jieba库概述 Jieba是一个流行的中文分词库,它能够将中文文本切分成词语,并对每个词语进行词性标注。中文分词是自然语言处理的重要步骤之一,它对于文本挖掘、信息检索、情感分析等任务具有重要…

Linux服务器丢包故障的解决思路及引申的TCP/IP协议栈理论

Linux服务器丢包故障的解决思路及引申的TCP/IP协议栈理论 我们使用Linux作为服务器操作系统时,为了达到高并发处理能力,充分利用机器性能,经常会进行一些内核参数的调整优化,但不合理的调整常常也会引起意想不到的其他问题&#x…

Elasticsearch原理剖析

一、 Elasticsearch结构 Elasticsearch集群方案由EsMaster、EsClient和EsNode1、EsNode2、EsNode3、EsNode4、EsNode5、EsNode6、EsNode7、EsNode8、EsNode9进程组成,如下图所示,模块说明如表下所示。 说明如表: 名称说明ClientClient使用H…

Android系统启动流程分析

当按下Android系统的开机电源按键时候,硬件会触发引导芯片,执行预定义的代码,然后加载引导程序(BootLoader)到RAM,Bootloader是Android系统起来前第一个程序,主要用来拉起Android系统程序,Android系统被拉起…

C# Linq 详解四

目录 概述 二十、SelectMany 二十一、Aggregate 二十二、DistinctBy 二十三、Reverse 二十四、SequenceEqual 二十五、Zip 二十六、SkipWhile 二十七、TakeWhile C# Linq 详解一 1.Where 2.Select 3.GroupBy 4.First / FirstOrDefault 5.Last / LastOrDefault C# Li…