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.hqwc.cn/news/19473.html

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

相关文章

Linux--操作系统进程的状态

【Linux】进程概念 —— 进程状态_linux d状态进程_Hello_World_213的博客-CSDN博客 新建:字面意思,将你的task_struct创建出来并且还未入队列 运行:task_struct结构体在运行队列中排队,就叫做运行态 阻塞: 等待非C…

Python采集课堂视频教程, m3u8视频解密

前言 嗨喽,大家好呀~这里是爱看美女的茜茜呐 环境使用: Python 3.8 解释器 Pycharm 编辑器 模块使用: requests >>> pip install requests pycryptodome --> pip install pycryptodome re 第三方模块安装方法: win R 输…

Prompt本质解密及Evaluation实战与源码解析(三)

9.5 Evaluation for QA源码解析 如图9-4所示,我们看一下LangChain框架对问答评估的(Evaluation for QA)的源代码。 图9- 5 LangChain的evaluation qa目录 在eval_prompt.py文件里面,主要定义了三个类 PromptTemplate,它们都是用于生成题目的模板。 Gavin大咖微信:NLP_Mat…

python离线安装ibm_db

下载离线包ibm_db以及clidriver 下载imb_db 在pypi官方网站https://pypi.org/project/ibm-db/#files下载离线安装包ibm_db-3.0.2.tar.gz。下载clidriver 下载地址:https://public.dhe.ibm.com/ibmdl/export/pub/software/data/db2/drivers/odbc_cli/nt32_odbc_cli.…

java后端开发环境搭建 mac

在mac pro上搭建一套java 后端开发环境,主要安装的内容有:jdk、maven、git、tomcat、mysql、navicat、IntelliJ、redis。 本人mac pro的系统为mac OS Monterey 12.6.7,主机的硬件架构为x86_64。 左上角关于本机查看系统版本;终端…

Django搭建图书管理系统04:View视图初探

数据库虽然已经有了,但是用户通常只需要这个庞大数据库中的很小一部分进行查看、修改等操作。为此还需要代码来恰当的取出并展示数据,这一部分代码就被称为视图。 Django 中视图的概念是**「一类具有相同功能和模板的网页的集合」**。 Hello World! 首…

HarmonyOS/OpenHarmony应用开发-程序包多HAP机制(上)

一、多HAP机制设计目标 方便开发者模块化的管理应用,好的应用一般都是模块化管理,模块之间属于松耦合关系。多HAP方便了开发者将业务划分成多个模块,每个模块放到独立的HAP中。例如支付类应用,有统一的主界面,主界面管…

图论之寻找桥边

目录 ①基准法 ②并查集 ③逆向思维之标记环边 ④并查集压缩路径 ①基准法 在图论中,一条边被称为“桥”代表这条边一旦被删除,这张图的连通块数量会增加。等价地说,一条边是一座桥当且仅当这条边不在任何环上。一张图可以有零或多座桥。…

golang数据库操作相应内容--推荐【比较全】

Go为开发数据库驱动定义了一些标准接口,开发者可以根据定义的接口来开发相应的数据库驱动,只要是按照标准接口开发的代码, 以后需要迁移数据库时,不需要任何修改。 一、database/sql接口 1.1sql.Register 这个存在于database/s…

Java新特性:Lambda表达式

Java新特性:Lambda表达式 Lambda 表达式(Lambda expression),也可称为闭包(Closure),是 Java(SE)8 中一个重要的新特性。Lambda 表达式允许我们通过表达式来代替功能接口…

Django_haystack全文搜索

haystack是全文搜索的框架,支持whoosh、solr、Xapian、Elasticsearc四种全文检索引擎,点击查看官方网站。 whoosh是用纯Python编写的全文搜索引擎,虽然性能比不上sphinx、xapian、Elasticsearc等,但是无二进制包,程序…

一百二十八、Kettle——从Hive增量导入到ClickHouse

一、目标 用Kettle把Hive的DWS层数据增量导入到ClickHouse中 工具版本:Kettle:8.2 Hive:3.1.2 ClickHouse21.9.5.16 全量导入请访问拙作链接 http://t.csdn.cn/Rqvuvhttp://t.csdn.cn/Rqvuv 二、前提准备 (一)kettl…