Mysql/Redis缓存一致性

如何保证MySQL和Redis的缓存一致。从理论到实战。总结6种来感受一下。

理论知识

不好的方案

1.先写MySQL,再写Redis

图解说明:

这是一幅时序图,描述请求的先后调用顺序;

黄色的线是请求A,黑色的线是请求B;

黄色的文字是MySQL和Redis最终不一致的数据;

数据是从10更新为11;

后面的图为此规定

请求A、B都是先写MySQL,然后再写Redis,在高并发的情况下,如果请求A在写Redis时卡了一会,请求B已经一次完成了数据的更新,就会出现图中描述的问题。

图表述很清楚了,不过这里有个前提,就是对于读请求,先去读Redis,如果没有,再去读DB,但是读请求不会再写回Redis。就是读请求不会更新Redis。

2.先写Redis,再写MySQL

同1描述一样,秒懂。

3.先删除Redis,再写MySQL

和上面不一样的是,前面的请求A和B都是更新请求,这里的请求·A是跟新请求,但B请求是读请求,并且B的读请求会写回Redis。

请求A先删除缓存,可能因为卡顿,数据一直没有更新到MySQL,导致数据不一致。

这种情况出现的概率比较大,因为请求A更新MySQL可能会耗时比较长,而请求B的前两者都是查询,会比较快。

好的方案

4.先删除Redis,再写MySQL,再删除Redis

对于“先删除Redis,再写MySQL” ,如果要解决最后的不一致问题,其实再对Redis重新删除即可,这个就是“缓存双删”。

这个方案看看就行。

更好的方案是,异步串行化删除,即删除请求入队列

异步删除除对线上业务无影响,串行化处理保障并发情况下正确删除。

5.先写MySQL,再删除Redis

对于上面这种情况,对于第一次查询,请求B查询的数据10,但是MySQL的数据是11,只存在这一次不一致的情况,对于不是强一致的情况,对于不是强一致性要求的业务,可以容忍。对秒杀,库存就不行。

当请求B进行第二次查询时,因为没命中Redis,会重新擦汗一次DB,然后再回写到Redis。

这里需要满足两个条件:

        缓存刚好自动失效;

        请求B从数据库查10,回写缓存的消耗,比请求A写数据库,并且删除缓存的还长。

对于第二个条件,我们都知道更新DB肯定比查询耗时要长,所以出现这个情况的概率很小,同时满足上述条件情况更小。

6.先写MySQL,通过Binlog,异步更新Redis

这个方案,主要是监听MySQL的Binlog,然后通过异步的方式,将数据更新到Redis,这种方案有个前提,查询的请求,不会写回Redis。

这个方案,保证MySQL和Redis的最终一致性,但是如果中途请求B需要查询数据,如果缓存无数据,就直接查DB;如果缓存有数据,查询的数据也会存在不一致的情况。

所以这个方案,是实现最终一致性的终极方案,但是不能保证实时性。

几种方案比较

我们对比上述讨论的6种方案:‘

1.先写Redis,再写MySQL

这种方案,坑定是不会用,万一DB挂了,你把数据写到缓存,DB无数据,这个是灾难性的;

如果写DB失败,对Redis进行逆操作,那如果逆向操作失败,是不是得又搞个重试?

2.先写MySQL,再写Redis

对于并发量、一致性要求不高的项目,很多就是这么用的,我之前也经常这么搞

但是不建议这么做;

当Redis瞬间不可用的情况,需要报警出来,然后线下处理。

3.先删除Redis,再写MySQL

有懂得回答?

4.先删除Redis,再写MySQL,再删除Redis

这种方式虽然可行,但是感觉复杂,还要搞个消息队列去异步删除Redis。

5.先写MySQL,再删除Redis

比较推荐这总方案,删除Redis如果失败,可以再多重试几次,否则报警出来;

这个方案,是实时性最好的方案,在一些高并发场景种,推荐。

6.先写MySQL,通过Binlog。异步更新Redis

对于异地容灾,数据汇总,建议用这种,比如binlog+kafka,数据得一致性也可以达到秒级;

纯粹得高并发场景,不建议这种方案,入抢购,秒杀等。

个人结论:

实时性一致方案:采用“先写MySQL ,再删除Redis”的策略,这种情况下虽然也会存在两者不一致,但是需要满足的条件有点苛刻,所以是满足实时性条件下,能尽量满足一致性的最优解。

最终一致性方案:采用“先写MySQL,通过Binlog,异步更新Redis“,可以通过Binlog,结合消息队列异步更新Redis,是最终一致性的最优解。

项目实战

数据更新

因为项目对实时性要求高,所以采用方案5,先写MySQL,再删除Redis方式。

下面是一个示例,我们将文章的标签放入MySQL之后,在删除Redis,所有涉及到DB更新的操作都需要按照这种方式处理。

这里加了一个事务,如果Redis删除失败,MySQL的更新操作也要回滚,避免查询读取到脏数据。

    @Override@Transactional(rollbackFor = Exception.class)public void saveTag(TagReq tagReq) {TagDO tagDO = ArticleConverter.toDO(tagReq);//先写MySQLif (NumUtil.nullOrZero(tagReq.getTagId())) {tagDao.save(tagDO);} else {tagDO.setId(tagReq.getTagId());tagDao.updateById(tagDO);}//再删除RedisString redisKey = CACHE_TAG_PRE + tagDO.getId();RedisClient.del(redisKey);}@Override@Transactional(rollbackFor = Excetion.class)public void deleteTag(Integer tagId) {TagDO tagDO = tagDao.getById(tagId);if (tagDO != null){//先写MySQLtagDao.removeById(tagId);//再删除RedisString redisKey = CACHE_TAG_PRE + tagDO.getId();RedisClien.del(redisKey);}}@Overridepublic void operateTag(Integer tagId, Integer pushStatus) {TagDO tagDO = tagDao.getById(tagId);if (tagDO != null){//先写MySQLtagDO.setStatus(pushStatus);tagDao.updateById(tagDO);//再删除RedisString redisKey = CACHE_TAG_PRE + tagDO.getId();RedisClient.del(redisKey);}}

获取数据

也比较简单,先查缓存,如果有就直接返回;如果未查询到,需要先查询DB,再写入缓存。

我们放入缓存时,加了一个过期时间,用于兜底,万一两者不一致,缓存过期后,数据会重新更新到缓存。

    @Overridepublic TagDTO getTagById(Long tagId) {String redisKey = CACHE_TAG_PRE + tagId;// 先查询缓存,如果有就直接返回String tagInfoStr = RedisClient.getStr(redisKey);if (tagInfoStr != null && !tagInfoStr.isEmpty()) {return JsonUtil.toObj(tagInfoStr, TagDTO.class);}// 如果未查询到,需要先查询 DB ,再写入缓存TagDTO tagDTO = tagDao.selectById(tagId);tagInfoStr = JsonUtil.toStr(tagDTO);RedisClient.setStrWithExpire(redisKey, tagInfoStr, CACHE_TAG_EXPRIE_TIME);return tagDTO;}

测试用例

@Slf4j
public class MysqlRedisService extends BasicTest {@Autowiredprivate TagSettingService tagSettingService;@Testpublic void save() {TagReq tagReq = new TagReq();tagReq.setTag("Java");tagReq.setTagId(1L);tagSettingService.saveTag(tagReq);log.info("save success:{}", tagReq);}@Testpublic void query() {TagDTO tagDTO = tagSettingService.getTagById(1L);log.info("query tagInfo:{}", tagDTO);}
}

我们看一下Redis:

结果输出:

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

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

相关文章

php对接谷歌admob广告收益reporting api分享

今天收到需求,需要对接reporting api接口,拉取广告收益回来。网上找到文档开始对接,对接完成了,今天分享给大家一些心得 文档地址:https://developers.google.com/admob/api/v1/reporting?hlzh-cn#php-client-library 因为接口使用的google…

基于YOLOv8/YOLOv7/YOLOv6/YOLOv5的危险物品检测系统(深度学习模型+PySide6界面+训练数据集+Python代码)

摘要:本文深入介绍了一个采用深度学习技术的危险物品识别系统,该系统融合了最新的YOLOv8算法,并对比了YOLOv7、YOLOv6、YOLOv5等早期版本的性能。该系统在处理图像、视频、实时视频流及批量文件时,能够准确识别和分类各种危险物品…

聊聊测试左移到开发阶段

这是鼎叔的第九十一篇原创文章。行业大牛和刚毕业的小白,都可以进来聊聊。 欢迎关注本公众号《敏捷测试转型》,星标收藏,大量原创思考文章陆续推出。本人新书《无测试组织-测试团队的敏捷转型》已出版(机械工业出版社&#xff09…

两个笔记本如何将一个笔记本作为另一个笔记本的拓展屏

需求是有两个笔记本,一个笔记本闲置,另一个笔记本是主力本。想将另一个闲置的笔记本连接到主力本上作为拓展屏使用。网上搜了好久,有一些人提到了,也有一些视频但是文章比较少。简单总结一下吧 上述需求有两种方式 第一种&#x…

2024.3.13

1、 #include <iostream>using namespace std; class Per { private:string name;int age;double *hight;double *weight; public:void show(){cout << "姓名&#xff1a;" << name << endl;cout << "年龄&#xff1a;" &l…

改进沙猫群优化的BP神经网络ISCSO-BP(时序预测)的Matlab实现

改进沙猫群优化的BP神经网络&#xff08;ISCSO-BP&#xff09;是一种结合了改进的沙猫群优化算法&#xff08;Improved Sand Cat Swarm Optimization, ISCSO&#xff09;和反向传播&#xff08;Back Propagation, BP&#xff09;神经网络的模型&#xff0c;旨在提高时序预测的准…

python的函数与类的定义

目录 1.函数 1.函数的定义 2.输入参数与输出参数的类型 3.输入和输出多个参数 1.普通参数 2.含有任意数量的参数 3.关键字参数 4.普通参数与多个参数的结合 2.类 1.类的定义 2.类的实例化 3.继承 1.函数 1.函数的定义 def 函数名(输入参数): 文档字符串 函数体 …

brew安装node和nvm切换和管理node版本

Homebrew是一款Mac OS平台下的软件包管理工具&#xff0c;拥有安装、卸载、更新、查看、搜索等很多实用的功能。简单的一条指令&#xff0c;就可以实现包管理&#xff0c;而不用你关心各种依赖和文件路径的情况&#xff0c;十分方便快捷。简单来说&#xff0c;Homebrew提供 App…

C#无法给PLC写入数据原因分析

一、背景 1.1 概述 C#中无法给PLC写入数据的原因有很多&#xff0c;这里分享网络端口号被占用导致无法写入的确认方法 1.2 环境 ①使用三菱PLC ②C#通过网口与PLC进行通讯 二、现象 1.1 代码 通过HslCommunication连接PLC时&#xff0c;连接返回成功&#xff0c;写入返回失败 …

(003)SlickEdit Unity的补全

文章目录 步骤XML知识点 附录 步骤 1.下载 unity 源码。 2.将自定义文件 MonoBehaviour.cs 放到解压后的项目里面&#xff1a; using System;namespace UnityEngine {public partial class MonoBehaviour{public virtual void Awake(){throw new NotImplementedException();…

AI知识库也太方便了吧,中小型企业都要知道它!

生活在这个信息爆炸的时代&#xff0c;信息的获取变得前所未有的方便&#xff0c;但随之而来的却是信息筛选和管理的难题。对于中小型企业来说&#xff0c;如何有效运用自身积累的各类信息&#xff0c;直接影响着企业的运营效率和市场竞争力。而这&#xff0c;正是AI知识库可以…

Java毕业设计-基于spring boot开发的实习管理系统-毕业论文+答辩ppt(附源代码+演示视频)

文章目录 前言一、毕设成果演示&#xff08;源代码在文末&#xff09;二、毕设摘要展示1.开发说明2.需求分析3、系统功能结构 三、系统实现展示1、前台功能模块2、后台功能模块2.1 管理员功能2.2 教师功能2.3 学生功能2.4 实习单位功能 四、毕设内容和源代码获取总结 Java毕业设…