Redis学习——高级篇③

Redis学习——高级篇③

    • = = = = = = Redis7高级之缓存双写一致性之更新策略探讨(三)= = = = = =
    • 1.缓存双写一致性
    • 2.数据库和缓存一致性的几种更新策略
      • 2.1 可停机的情况
      • 2.2 不可停机的情况,四种更新策略(推荐最后一种,看场景)
        • 1.❌先更新数据库,再更新缓存
        • 2.❌先更新缓存,再更新数据库
        • 3.❌先删除缓存,再更新数据库
        • 4.⚠️先更新数据库,再删除缓存
    • 3. 总结

在这里插入图片描述

在这里插入图片描述

= = = = = = Redis7高级之缓存双写一致性之更新策略探讨(三)= = = = = =

在这里插入图片描述

还是先放面试题,当一整遍看完,再回头看看 这个面试题题,自己会不会。

在这里插入图片描述

1.缓存双写一致性

  • 如果redis中有数据

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

    • 数据库中的值是最新值,且准备回写redis
  • 缓存按照操作分

    • 只读缓存
    • 读写缓存
      • 同步直写策略
        • 写数据库后也同步写 redis 缓存,缓存中的数据和数据中的一致
        • 对于读写缓存来说,要想保证缓存和数据库中的数据一致
      • 异步缓写策略
        • 正常业务运行中,mysql数据变动了,但是可以在业务上容许出现一定时间后才作用于redis,比如仓库、物流系统
        • 异常情况出现了,不得不讲失败的动作重新修补,有可能需要借助kafka或者RabbitMQ等消息中间件,实现重写重试
  • 在这里插入图片描述

    • 采用双检加锁策略

      • 多个线程同时去查询数据库的这条数据,就在第一个查询数据的请求上使用一个互斥锁来锁住他。
      • 其他线程获取不到锁就一直等待,等第一个线程查询到了数据,然后做了缓存
      • 后面的线程进来发现已经有了缓存,就直接走缓存
package com.lv.service.impl;import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lv.User;
import com.lv.mapper.UserMapper;
import com.lv.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;@Slf4j
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {public static final String CACHE_KEY_USER = "user:";@Resourceprivate UserMapper userMapper;@Resourceprivate RedisTemplate redisTemplate;/***  业务逻辑没有写错,对于小厂中厂(QPS《=1000)可以使用,但是大厂不行* @param id* @return*/public User findUserById1(Long 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.selectById(id);if (user == null){// 3.1 redis + mysql 都无数据// 具体细化,防止多次穿透,业务规定,记录下导致穿透的这个key回写redisreturn user;}else {// 3.2 mysql有,需要回写到redis,保证下一次的缓存命中率redisTemplate.opsForValue().set(key,user);}}return user;}/*** 加强补充,避免突然key失效了,打爆mysql,做一下预防,尽量不出现击穿的情况* @param id* @return*/public User findUserById2(Long id){User user = null;String key = CACHE_KEY_USER + id;// 1.先从redis里面查询,如果有直接返回结果,如果没有再去查询mysql// 第一次查询redis,加锁前user = (User) redisTemplate.opsForValue().get(key);if (user == null){// 2.对于高QPS的优化,进来就先加锁,保证一个请求操作,让外面的redis等待一下,避免击穿mysqlsynchronized (UserServiceImpl.class){// 第二次查询redis,加锁后user = (User) redisTemplate.opsForValue().get(key);// 3. 二次查redis还是null,可以去查mysql了(mysql默认有数据)if (user == null) {//4 查询mysql拿数据(mysql默认有数据)user = userMapper.selectById(id);if (user == null) {return null;} else {// 5. mysql里面有数据的,需要回写redis,完成数据一致性的同步工作redisTemplate.opsForValue().setIfAbsent(key, user, 7L, TimeUnit.DAYS);}}}}return user;}
}

在这里插入图片描述

2.数据库和缓存一致性的几种更新策略

目的

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

上述方案和后续落地案例是调研后的主流+成熟的做法,但是考虑到各个公司业务系统的差距,不是100%绝对正确,不保证绝对适配全部情况,请同学们自行酌情选择打法,合适自己的最好。

2.1 可停机的情况

基本上怎么处理都可以

  • 挂牌报错
  • 凌晨升级
  • 服务降级
  • 温馨提示
  • 最好单线程操作(对于重量级的数据操作)

2.2 不可停机的情况,四种更新策略(推荐最后一种,看场景)

1.❌先更新数据库,再更新缓存

异常问题1

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

异常问题2

[先更新数据库,再更新缓存] , A、B两个线程发起调用

[正常逻辑]

  1. A update mysql 100
  2. A update redis 100
  3. B update mysql 80
  4. B update redis 80

[异常逻辑]多线程环境下,A、B两个线程有快有慢,有前有后有并行

  1. A update mysql 100
  2. B update mysql 80
  3. B update redis 80
  4. A update redis 100

最终结果,mysq|和redis数据不一致,/(ㄒoㄒ)/~~

mysql80,redis100

在这里插入图片描述

2.❌先更新缓存,再更新数据库
  • 不推荐:一般业务会将mysql作为底单数据库,有最终解释权

  • 异常问题

[先更新缓存,再更新数据库] , A、B两个线程发起调用

[正常逻辑]

  1. A update redis 100
  2. A update mysql 100
  3. B update redis 80
  4. B update mysql 80

[异常逻辑]多线程环境下,A、B两个线程有快有慢,有前有后有并行

  1. A update redis 100
  2. B update redis 80
  3. B update mysql 80
  4. A update mysql 100

最终结果,mysq|和redis数据不一致,/(ㄒoㄒ)/~~

mysql80,redis100

在这里插入图片描述

3.❌先删除缓存,再更新数据库

异常问题

在这里插入图片描述

在这里插入图片描述
3

  • A线程更新完mysq|,发现redis 里面的缓存是脏数据,A线程直接懵逼了
  • 两个并发操作,一个是更新操作,另一个是查询操作,
  • A删除缓存后,B查询操作没有命中缓存,B先把老数据读出来后放到缓存中,然后A更新操作更新了数据库。
  • 于是,在缓存中的数据还是老的数据,导致缓存中的数据是脏的,而且还一直这样脏下去了。

4总结流程:

  1. 请求A进行写操作,删除redis缓存后,工作正在进行中,更新mys…A还么有彻底更新完mysql,还没commit
  2. 请求B开工查询,查询redis发现缓存不存在(被A从redis中删除了)
  3. 请求B继续,去数据库查询得到了mysq|中的旧值(A还没有更新完)
  4. 请求B将旧值写回redis缓存

在这里插入图片描述
在这里插入图片描述

解决方案(延时双删策略)

注意关键点,我更新完数据库的时间 + sleep的时间 大于 读取数据并写入换的时间 即可(多个100ms即可)
在这里插入图片描述

加上sleep的这段时间,就是为了让线程B能够先从数据库读取数据,再把缺失的数据写入缓存,然后,线程A再进行删除。所以,线程A sleep的时间,就需要大于线程B读取数据再写入缓存的时间。这样一来,其它线程读取数据时,会发现缓存缺失,所以会从数据库中读取最新值。因为这个方案会在第一次删除缓存值后,延迟一段时间再次进行删除,所以我们也把它叫做“延迟观删”。

关于延时双删的细节问题

  • 这个线程休眠时间(线程A sleep的时间,就需要大于线程B读取数据再写入缓存的时间)

    • 第一种 :在业务程序运行的时候,统计下线程读数据和写缓存的操作时间,自行评估自己的项目的读数据业务逻辑的耗时,以此为基础来进行估算。然后写数据的休眠时间则在读数据业务逻辑的耗时基础上加百毫秒即可。
    • 第二种: 新启动一个后台监控程序,比如WatchDog监控程序,会加时
      这种同步淘汰策略,吞吐量降低怎么办
  • 第二次删除缓存使用 异步删除
    在这里插入图片描述

在这里插入图片描述

4.⚠️先更新数据库,再删除缓存

异常问题

在这里插入图片描述

订阅binlog程序再mysql中有现成的中间件叫canal,可以完成订阅binlog日志的功能(下一章具体实现)

解决方法

在这里插入图片描述

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

分布式的事务问题一定要遵守最终一致性,可以允许短暂的信息滞后

多补充一句:如果使用先更新数据库,再删除缓存的方案

如果业务层要求必须读取一致性的数据,那么我们就需要在更新数据库时,先在Redis缓存客户端暂停并发读请求,等数据库更新完、缓存值删除后,再读取数据,从而保证数据一致性,这是理论可以达到的效果,但实际,不推荐,因为真实生产环境中,分布式下很难做到实时一致性,一般都是最终一致性,请大家参考。

3. 总结

策略高并发多线程条件下问题现象解决方案
先删除redis缓存,再更新mysql缓存删除成功但数据库更新失败Java程序 从数据库中读到旧值再次更新数据库,重试
先删除redis缓存,再更新mysql缓存删除成功但数据库更新中。。。有并发读请求并发请求从数据库读到旧值并回写到redis,导致后续都是从redis读取到旧值延迟双删
先更新mysql,再删除redis缓存数据库更新成功,但缓存删除失败Java程序从redis中读到旧值再次删除缓存,重试
先更新mysql,再删除redis缓存数据库更新成功但缓存删除中。。。有并发读请求并发请求从缓存读到旧值等待redis删除完成,这段时间有数据不一致,短暂存在

在大多数业务场景下,阳哥建议是 优先使用先更新数据库,再删除缓存的方案(先更库,后删存)。理由如下:

先删除缓存值再更新数据库

  • 有可能导致请求因缓存缺失而访问数据库,给数据库带来压力导致打满mysql
  • 如果业务应用中读取数据库和写缓存的时间不好估算,那么,延迟双删中的等待时间就不好设置

先更新数据库,再删除缓存

  • 如果业务层要求必须读取一致性的数据,那么我们就需要在更新数据库时,先在Redis缓存客户端暂停并发读请求,等数据库更新完、缓存值删除后,再读取数据,从而保证数据一致性,这是理论可以达到的效果,但实际,不推荐,因为真实生产环境中,分布式下很难做到实时一致性,一般都是最终一致性。

在这里插入图片描述

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

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

相关文章

Java8-Stream 流基本应用-groupBy进行分组

groupBy进行分组 Testpublic void testStreamGroupBy(){List<UserInfoModel> resultnew ArrayList<>();for (int i 0; i < 10; i) {UserInfoModel usernew UserInfoModel();user.setUserId(i"");user.setUserName("kangshihang");result.a…

【代码随想录-链表】移除链表元素

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学习,不断总结,共同进步,活到老学到老导航 檀越剑指大厂系列:全面总结 jav…

Kafka(九)跨集群数据镜像

目录 1 跨集群镜像的应用场景1.1 区域集群和中心集群1.2 高可用(HA)和灾备(DR)1.3 监管与合规1.4 云迁移1.5 聚合边缘集群的数据 2 多集群架构2.1 星型架构2.2 双活架构2.2 主备架构2.2.1 如何实现Kafka集群的故障转移2.2.1.1 故障转移包括的内容1. 灾难恢复计划2. 非计划内的故…

DELMIAWORKS核心优势深度探析

一&#xff1a;制造BOM从产品结构到制造场景 BOM是所有制造系统的核心&#xff0c;DELMIAWORKS作为专业的制造运营系统&#xff0c;在理解制造BOM方面的能力已经超越了传统系统的范畴。其与传统系统的最大区别在于DELMIAWORKS将焦点放在制造场景的描述上&#xff0c;而非产品结…

259:vue+openlayers: 显示海量多边形数据,10ms加载完成

第259个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+openlayers项目中通过WebGLVectorLayerRenderer方式加载海量多边形数据。这里相当于将海量的数据放在同一个层的source中,然后通过webglTile的方式渲染出这一层。 本示例数据为5000个多边形,加载速度超级快。 直接…

数据库性能问题分析优化

客户反应应用频繁卡住&#xff0c;只能通过重启应用才能恢复&#xff0c;一天发生若干次。 问题初步分析处理 从最近得到的三个awr报告看&#xff0c;等待事件基本在于“DB CPU”&#xff0c;“db file sequential read”&#xff0c;“log file sync”&#xff0c;“log fil…

leetcode 26.删除有序数组中的重复项(python版)

需求 给你一个 非严格递增排列 的数组 nums &#xff0c;请你 原地 删除重复出现的元素&#xff0c;使每个元素 只出现一次 &#xff0c; 返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums 中唯一元素的个数。 考虑 nums 的唯一元素的数量为 k &#x…

浅谈扬州晶澳-年产3GW高性能太阳能光伏组件项目Acrel-3000WEB电能管理系统的设计及应用

摘要&#xff1a;在信息时代&#xff0c;电力信息系统的应用促迚了电力企业的収展&#xff0c;增强了电力系统运行的安全性与稳定性&#xff0c;对满足用户需求其有重要意义。随着国家电网改革政策的逐步推进和落实&#xff0c;Acrel-3000WEB电能管理系统运用互联网和大数据技术…

游戏开发丨基于Pygame的AI版贪吃蛇小游戏

文章目录 写在前面需求分析程序设计程序分析运行结果系列文章写在后面 写在前面 本期内容 基于pygame的AI版贪吃蛇小游戏 所需环境 pythonpycharm或anacondapygame 下载地址 https://download.csdn.net/download/m0_68111267/88789665 需求分析 本游戏使用Pygame模块开…

Spark入门01

1 Spark是什么 Spark是用于大规模数据处理的统一分析引擎。对任意类型的数据进行自定义计算。 可以计算&#xff1a;结构化、非结构化&#xff0c;半结构化的数据结构&#xff0c;支持使用Python&#xff0c;Java&#xff0c;Scala、Sql语言开发应用程序计算数据。 计算框架&a…

前端Vue select 下拉框详解以及监听事件

目录 简介 使用详解 演示示例 :key"option.value" :value"option.value" 区别 监听事件 简介 在 Vue 中&#xff0c;下拉框通常通过 <select> 元素与一系列的 <option> 元素来创建。Vue 的数据绑定和指令&#xff08;如 v-model 和 v-for…

ArcGIS雨涝风险模拟

所谓雨涝模拟分析&#xff0c; 就是模拟降雨量达到一定强度&#xff0c; 城市的哪些区域容易被淹没形成内涝。 雨涝模拟更重要的是提前预测&#xff0c; 可在预测结果的基础上进行实地勘察&#xff0c; 为项目规划、风险防控等工作提供指导作用。 雨涝模拟的原理和思想多种…

IT行业含金量较高的证书

在IT行业中&#xff0c;一些含金量较高的证书包括&#xff1a; 1. Cisco认证&#xff1a;如CCNA&#xff08;思科认证网络工程师&#xff09;、CCNP&#xff08;思科认证专业级网络工程师&#xff09;和CCIE&#xff08;思科认证专家级网络工程师&#xff09;等。这些认证证书…

基于springboot+vue的医院管理系统(前后端分离)

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容&#xff1a;毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 研究背景…

Nginx负载均衡下的webshell连接

一、上传AntSword-Labs-master搭建负载均衡实验环境 搭建好docker环境&#xff0c;并且配置好docker-compose 我的Redhat的docker版本&#xff1a; 查看当前环境下的文件是否正确&#xff1a; 接着执行docker compose up -d 拉取环境 访问成功页面&#xff1a; 进入docker容器…

【RT-DETR有效改进】反向残差块网络EMO | 一种轻量级的CNN架构(轻量化网络,参数量下降约700W)

👑欢迎大家订阅本专栏,一起学习RT-DETR👑 一、本文介绍 本文给大家带来的改进机制是反向残差块网络EMO,其的构成块iRMB在之前我已经发过了,同时进行了二次创新,本文的网络就是由iRMB组成的网络EMO,所以我们二次创新之后的iEMA也可以用于这个网络中,再次形成二次…

【UEFI实战】Redfish的BIOS实现——生成EDK数据

生成Redfish文件 Redfish数据的表示形式&#xff0c;最常用的是JSON。将JSON表示的数据转换成C语言可以操作的结构体&#xff0c;是必不可少的步骤。当然如果手动转换的话&#xff0c;需要浪费大量的时间&#xff0c;因此DMTF组织开发了一个工具&#xff0c;用于将JSON数据快速…

MySQL的SQL MODE

目录 举例&#xff1a; --常见SQL mode --mysql8 sql_mode 官方文档 https://dev.mysql.com/doc/refman/8.0/en/sql-mode.html --查看全局的SQL MODE select global.sql_mode; --查看当前会话的SQL MODE select session.sql_mode; --运行时修改全局的SQL mode set gl…

Python笔记14-实战小游戏飞机大战(上)

文章目录 功能规划安装pygame绘制游戏窗口添加玩家飞机图像屏幕上绘制飞船代码重构驾驶飞船全屏模式射击 本示例源码地址 点击下载 功能规划 玩家控制一艘最初出现在屏幕底部中央的飞船。玩家可以使用箭头键左右移动飞船&#xff0c;还可使用空格键射击。游戏开始时&#xff…

敲黑板啦!CSGO游戏搬砖项目操作注意事项

CSGO游戏搬砖项目怎么赚钱的&#xff0c;利润在哪&#xff1f; 1.两个平台之间币种不一样&#xff0c;就存在一个汇率差&#xff0c;两平台装备价格也不一样&#xff0c;汇率差-价格差利润。 CSGO游戏搬砖项目具体有哪些操作步骤&#xff1f; 1、准备一台电脑&#xff0c;配置…