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

2024-04-23 07:52

本文主要是介绍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%   会出纰漏,  做不到强一致性,  如何保证 最终一致性?

这篇关于Mysql 、Redis 数据双写一致性 更新策略与应用的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/928203

相关文章

Redis 基本数据类型和使用详解

《Redis基本数据类型和使用详解》String是Redis最基本的数据类型,一个键对应一个值,它的功能十分强大,可以存储字符串、整数、浮点数等多种数据格式,本文给大家介绍Redis基本数据类型和... 目录一、Redis 入门介绍二、Redis 的五大基本数据类型2.1 String 类型2.2 Hash

深入理解Mysql OnlineDDL的算法

《深入理解MysqlOnlineDDL的算法》本文主要介绍了讲解MysqlOnlineDDL的算法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小... 目录一、Online DDL 是什么?二、Online DDL 的三种主要算法2.1COPY(复制法)

Redis中Hash从使用过程到原理说明

《Redis中Hash从使用过程到原理说明》RedisHash结构用于存储字段-值对,适合对象数据,支持HSET、HGET等命令,采用ziplist或hashtable编码,通过渐进式rehash优化... 目录一、开篇:Hash就像超市的货架二、Hash的基本使用1. 常用命令示例2. Java操作示例三

Redis中Set结构使用过程与原理说明

《Redis中Set结构使用过程与原理说明》本文解析了RedisSet数据结构,涵盖其基本操作(如添加、查找)、集合运算(交并差)、底层实现(intset与hashtable自动切换机制)、典型应用场... 目录开篇:从购物车到Redis Set一、Redis Set的基本操作1.1 编程常用命令1.2 集

Linux下利用select实现串口数据读取过程

《Linux下利用select实现串口数据读取过程》文章介绍Linux中使用select、poll或epoll实现串口数据读取,通过I/O多路复用机制在数据到达时触发读取,避免持续轮询,示例代码展示设... 目录示例代码(使用select实现)代码解释总结在 linux 系统里,我们可以借助 select、

Redis中的有序集合zset从使用到原理分析

《Redis中的有序集合zset从使用到原理分析》Redis有序集合(zset)是字符串与分值的有序映射,通过跳跃表和哈希表结合实现高效有序性管理,适用于排行榜、延迟队列等场景,其时间复杂度低,内存占... 目录开篇:排行榜背后的秘密一、zset的基本使用1.1 常用命令1.2 Java客户端示例二、zse

mysql8.0.43使用InnoDB Cluster配置主从复制

《mysql8.0.43使用InnoDBCluster配置主从复制》本文主要介绍了mysql8.0.43使用InnoDBCluster配置主从复制,文中通过示例代码介绍的非常详细,对大家的学习或者... 目录1、配置Hosts解析(所有服务器都要执行)2、安装mysql shell(所有服务器都要执行)3、

Redis中的AOF原理及分析

《Redis中的AOF原理及分析》Redis的AOF通过记录所有写操作命令实现持久化,支持always/everysec/no三种同步策略,重写机制优化文件体积,与RDB结合可平衡数据安全与恢复效率... 目录开篇:从日记本到AOF一、AOF的基本执行流程1. 命令执行与记录2. AOF重写机制二、AOF的

前端缓存策略的自解方案全解析

《前端缓存策略的自解方案全解析》缓存从来都是前端的一个痛点,很多前端搞不清楚缓存到底是何物,:本文主要介绍前端缓存的自解方案,文中通过代码介绍的非常详细,需要的朋友可以参考下... 目录一、为什么“清缓存”成了技术圈的梗二、先给缓存“把个脉”:浏览器到底缓存了谁?三、设计思路:把“发版”做成“自愈”四、代码

k8s中实现mysql主备过程详解

《k8s中实现mysql主备过程详解》文章讲解了在K8s中使用StatefulSet部署MySQL主备架构,包含NFS安装、storageClass配置、MySQL部署及同步检查步骤,确保主备数据一致... 目录一、k8s中实现mysql主备1.1 环境信息1.2 部署nfs-provisioner1.2.