Mysql 、Redis 数据双写一致性 更新策略与应用

零、important point

1. 缓存双写一致性问题

2. java实现逻辑(对于  QPS <= 1000  可以使用)

public class UserService {public static final String CACHE_KEY_USER = "user:";@Resourceprivate UserMapper userMapper;@Resourceprivate RedisTemplate redisTemplate;/*** 业务逻辑没有写错,对于小厂中厂(QPS《=1000)可以使用,但是大厂不行* @param id* @return*/public User findUserById(Integer id){User user = null;String key = CACHE_KEY_USER+id;//1 先从redis里面查询,如果有直接返回结果,如果没有再去查询mysqluser = (User) redisTemplate.opsForValue().get(key);if(user == null){//2 redis里面无,继续查询mysqluser = userMapper.selectByPrimaryKey(id);if(user == null){//3.1 redis+mysql 都无数据//你具体细化,防止多次穿透,我们业务规定,记录下导致穿透的这个key回写redisreturn user;}else{//3.2 mysql有,需要将数据写回redis,保证下一次的缓存命中率redisTemplate.opsForValue().set(key,user);}}return user;}

其中存在的问题是:在高并发的场景下,(加入redis中没有)会有大量请求打在mysql上。

解决策略:

        (多个线程同时查询数据库某条数据时)

===》在第一个数据的请求上(加上一个互斥锁)

===》等待第一个线程查询到了数据 , 并做了缓存

===》后面的线程进来发现已经有缓存了 

===》直接走缓存

/*** 加强补充,避免突然key失效了,打爆mysql,做一下预防,尽量不出现击穿的情况。* @param id* @return*/public User findUserById2(Integer id){User user = null;String key = CACHE_KEY_USER+id;//1 先从redis里面查询,如果有直接返回结果,如果没有再去查询mysql,// 第1次查询redis,加锁前user = (User) redisTemplate.opsForValue().get(key);if(user == null) {//2 大厂用,对于高QPS的优化,进来就先加锁,保证一个请求操作,让外面的redis等待一下,避免击穿mysqlsynchronized (UserService.class){//第2次查询redis,加锁后user = (User) redisTemplate.opsForValue().get(key);//3 二次查redis还是null,可以去查mysql了(mysql默认有数据)if (user == null) {//4 查询mysql拿数据(mysql默认有数据)user = userMapper.selectByPrimaryKey(id);if (user == null) {return null;}else{//5 mysql里面有数据的,需要回写redis,完成数据一致性的同步工作redisTemplate.opsForValue().setIfAbsent(key,user,7L,TimeUnit.DAYS);}}}}return user;}

3. 数据一致性的理解

(1)如果 redis 中有数据   ===》  需要和  数据库中的值相同

(2)如果 redis 中没有数据  ===》  数据库中的值的是最新值,  回写到redis中

(3)缓存按照操作来分

        1.只读缓存(没有回写操作,少数情况下)

        2.读写缓存

                2.1 同步直写策略

                写数据库后也同步写redis缓存(热点数据、VIP重要数据 ==》这一秒填写、下一秒更新)

                2.2 异步缓写策略

                mysql数据变动了,可以允许业务上一定时间后作用于redis(仓库、物流系统、积分变更等 ==》 允许一定时延后缓存更新)

                可能会出现异常,借助kafka 或者 RabbitMQ 等消息中间件 ,实现重试重写

4. 数据库和缓存一致性的  几种策略

目的 :  达到最终的一致性

做法 :  给缓存设置过期时间   定期清理缓存并回写    ==》 保证最终一致性

1.停机

        (eg)凌晨升级  先往mysql灌入10000条数据, 在解决与mysql同步问题

2. 4种 更新策略

        (1)先更新数据库,在更新缓存

        (2)先更新缓存,在更新数据库

        (3)先删除缓存,在更新数据库

        (4)先更新数据库,在删除缓存

一、4种 更新策略

(1)先更新数据库,在更新缓存

Q1:redis回写失败,读到的是redis的脏数据

        1.先更新mysql的某商品的库存,当前商品的库存是100,更新为99个。
        2.先更新mysql修改为99成功,然后更新redis。
        3.此时假设异常出现,更新redis失败了,这导致mysql里面的库存是99而redis里面的还是100
        4.上述发生,会让数据库里面和缓存redis里面数据不一致,读到redis脏数据

Q2: 多线程对于同一份数据update, 回写redis出岔子,数据的写入覆盖

最终导致 mysql 80 , redis 100

(2)先更新缓存,在更新数据库(不太推荐)

Q1: 不太推荐 ==》 业务上一般把 mysql 作为底单数据库,保证最后的解释

Q2:多线程对于同一份数据update, 写入mysql出岔子,数据的写入覆盖

(3)先删除缓存,在更新数据库

Q1: 会出现延时

Q2: 此时redis里面的数据是空的,B线程来读取,先去读redis里数据(已经被A线程delete掉了),此处出来2个问题:

  2.1     B从mysql获得了旧值

       B线程发现redis里没有(缓存缺失)马上去mysql里面读取,从数据库里面读取来的是旧值

  2.2     B会把获得的旧值写回redis 

     获得旧值数据后返回前台并回写进redis(刚被A线程删除的旧数据有极大可能又被写回了)。

Q3:A线程更新完mysql,发现redis里面的缓存是脏数据,A线程直接懵逼了,o(╥﹏╥)o

        两个并发操作,一个是更新操作,另一个是查询操作,

        A删除缓存后,B查询操作没有命中缓存,B先把老数据读出来后放到缓存中,然后A更新操作更新了数据库。

        于是,在缓存中的数据还是老的数据,导致缓存中的数据是脏的,而且还一直这样脏下去了。

解决策略

1. 采用延时双删策略

(4)先更新数据库,在删除缓存

1.异常问题

 2.业务指导思想

2.1 微软云

2.2 阿里巴巴cache

 3.解决方案

1 可以把要删除的缓存值或者是要更新的数据库值暂存到消息队列中(例如使用Kafka/RabbitMQ等)。

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

 4.经典分布式事务问题

最终一致性体现案例:

1.流量充值,先下发短信,实际充值滞后5min

2.电商下单,先下发短信,具体物流明天见

 (0)如何选择方案

大多数业务场景:

        优先使用先更新数库,在删除缓存的方案


四、面试题

1.使用缓存  会涉及到  redis缓存与数据库  双存储双写

双写  ==》 数据库一致性问题  ==》 如何解决呢?

2. 双写一致性, 先去操作 redis or mysql,   why?  

3.  延时双删  怎么说?

        应用在需要更新数据时,先删除缓存再更新mysql数据库的策略下,所发生A线程需要更新数据,第一次删除缓存,更新完数据后,再次删除缓存,再将更新后的数据写入缓存。

延时双删会遇到一些问题:

Q1:这个删除需要睡眠多久呢?

         一般来说,线程Asleep的时间,就需要大于线程B读取数据再写入缓存的时间。
        第一种方法:
        在业务程序运行的时候,统计下线程读数据和写缓存的操作时间自行评估自己的项目的读数据业务逻辑的耗时,以此为基础来进行估算。然后写数据的休眠时间则在读数据业务逻辑的耗时基础上加百毫秒即可。
        这么做的目的,就是确保读请求结束,写请求可以删除读请求造成的缓存脏数据。
        第二种方法:
        新启动一个后台监控程序,比如后面讲解的WatchDog监控程序,去加时

Q2:这种同步策略,吞吐量降低如何解决?

        启动一个线程来监听mysql是否更新完毕

4. 微服务查询  redis无  mysql有, 为保证数据 双写一致性 回写redis  需要注意什么?

==》 双检 加锁 策略

==》 避免 缓存击穿

5. redis 和 mysql 双写100%   会出纰漏,  做不到强一致性,  如何保证 最终一致性?

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

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

相关文章

C++中的queue(容器适配器)

目录 一、成员函数 一、构造函数 二、入栈 push 三、出栈 pop 四、判空 empty 五、队列大小 size 六、取队头元素 front 七、取队尾元素 back 八、入栈 emplace 九、交换函数 swap 二、非成员函数重载 一、关系运算符重载 二、交换函数 swap C中的queue不再是容…

AWD线下攻防万字最完整战术(记第一届“长城杯”半决赛战术)

目录 准备阶段 1.登录比赛平台&#xff08;获取资产&#xff09; 查看账号账号修改 服务器SSH口令mysqlWEB服务口令(后台密码)数据库后台管理员密码 账号用户检查 2.dump源码&#xff08;方便应急响应恢复靶机&#xff09; 网站源码备份 压缩文件解压文件备份到服务器本地上传…

Google Ads广告为Demand Gen推出生成式AI工具,可自动生成广告图片

谷歌今天宣布在Google Ads广告中为Demand Gen活动推出新的生成人工智能功能。 这些工具由谷歌人工智能提供支持&#xff0c;广告商只需几个步骤即可使用文本提示创建高质量的图片。 这些由人工智能驱动的创意功能旨在增强视觉叙事能力&#xff0c;帮助品牌在YouTube、YouTube…

Webpack-

定义 静态模块&#xff1a;指的是编写代码过程中的html&#xff0c;css&#xff0c;js&#xff0c;图片等固定内容的文件 打包&#xff1a;把静态模块内容压缩、整合、翻译等&#xff08;前端工程化&#xff09; 1&#xff09;把less/sass转成css代码 2&#xff09;把ES6降级…

1.基于Springboot对SpringEvent初步封装

一&#xff1a;前置知识 Spring Event是Spring框架提供的一种事件机制&#xff0c;用于处理组件之间的通信。在复杂的系统中&#xff0c;模块或组件之间的通信是必不可少的。Spring Event可以用于以下场景&#xff1a; 1.系统间解耦&#xff1a;模块或组件之间通过事件进行通…

.gitignore语法及配置问题

语法及配置 前言.gitignore语法Git 忽略规则优先级gitignore规则不生效Java项目中常用的.gitignore文件c项目中常用的.gitignore注意事项 前言 在工程中&#xff0c;并不是所有文件都需要保存到版本库中&#xff0c;例如“target”目录及目录下的文件就可以忽略。在Git工作区的…

NLP大模型的训练

NLP模型的训练主要分成两步&#xff1a; 1.先进行通用任务的训练&#xff1b;无监督的样本是无穷无尽的&#xff1b; 这里列举两种&#xff1a;MLM和NSP,NSP由于在某些论文中被证明是无效的&#xff0c;所以用的少&#xff1b; MLM: 接下来会在特定任务上进行finetune>su…

夜神、雷电、android studio手机模拟器资源占用情况

夜神、雷电、android studio手机模拟器内存资源占用情况 由于开发电脑只有16G内存&#xff0c;出于开发需要和本身硬件资源的限制&#xff0c;对多个手机模拟器进行了机器资源占用&#xff08;主要是内存&#xff09;的简单比较。 比较的模拟器包括&#xff1a; 1. Android S…

[Kotlin]引导页

使用Kotlin Jetpack Compose创建一个左右滑动的引导页, 效果如图. 1.添加依赖项 androidx.compose.ui最新版本查询:https://maven.google.com/web/index.html com.google.accompanist:accompanist-pager最新版本查询:https://central.sonatype.com/ 确保在 build.gradle (M…

C语言——内存函数的实现与模拟

1. memcpy 函数 与strcpy 函数类似 1.头文件 <string.h> 2.基本格式 • 函数memcpy从source的位置开始向后复制num个 字节 的数据到destination指向的内存位置。 • 这个函数在遇到 \0 的时候并不会停下来。 • 如果source和destination有任何的重叠&#xff0…

数据分析_数据分析思维(1)

数据分析_数据分析思维(1) 这篇文章具体的给大家介绍数据分析中最为核心的技术之一: 数据分析思维的相关内容。 一、数据分析的三种核心思维 作为新手数据分析师或数据运营, 在面对数据异常的时候, 好多小伙伴都会出现: “好像是A引起的”, “好像也和B渠道有关”, “也可能…

3D MINS 多模态影像导航系统

3D MINS多模态影像导航系统&#xff08;Multimodal Image Navigation System&#xff09;是SunyaTech研发的建立在DICOM&#xff08;Digital Imaging and Communications in Medicine&#xff09;图像基础之上的多模态影像导航系统&#xff0c;集二维影像PACS管理、三维影像层级…