解决 Redis 缓存穿透 / 击穿 / 雪崩以及数据一致性的方案

Redis缓存穿透、缓存击穿解决方案及工具类 阅读详情

无论是在开发过程中还是在准备跑路的面试过程中,有关redis相关的,难免会涉及到四个特殊场景:缓存穿透、缓存雪崩、缓存击穿以及数据一致性。如果在开发中不注意这些场景的话,在高并发场景下有可能会导致系统崩溃,数据错乱等情况。现在,结合实际的业务场景来复现并解决这些问题。

相关技术:springboot2.2.2+mybatisplus3.1+redis5.0+hutool5.8

缓存穿透

缓存穿透是指查询缓存和数据库中都不存在的数据,导致所有的查询压力全部给到了数据库。

比如查询一篇文章信息并对其进行缓存,一般的逻辑是先查询缓存中是否存在该文章,如果存在则直接返回,否则再查询数据库并将查询结果进行缓存。

@Slf4j
@Service
public class DocumentInfoServiceImpl extends ServiceImpl<DocumentInfoMapper, DocumentInfo> implements DocumentInfoService {

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public DocumentInfo getDocumentDetail(int docId) {
        String redisKey = "doc::info::" + docId;
        String obj = stringRedisTemplate.opsForValue().get(redisKey);
        DocumentInfo documentInfo = null;
        if (StrUtil.isNotEmpty(obj)) { //缓存命中
            log.info("==== select from cache ====");
            documentInfo = JSONUtil.toBean(obj, DocumentInfo.class);
        } else {
            log.info("==== select from db ====");
            documentInfo = this.lambdaQuery().eq(DocumentInfo::getId, docId).one();
            if (ObjectUtil.isNotNull(documentInfo)) { // 缓存结果
                stringRedisTemplate.opsForValue().set(redisKey, JSONUtil.toJsonStr(documentInfo), 5L, TimeUnit.SECONDS);
            }
        }
        return documentInfo;
    }
}
复制代码
@GetMapping("/doc/queryById")
public Result<DocumentInfo> queryById(@RequestParam(name = "docId") Integer docId) {
    return Result.success(documentInfoService.getDocumentDetail(docId));
}
复制代码

如果项目的并发量不大,这样写的话几乎没啥问题。如果项目的并发量很大,那么这就存在一个隐藏问题,如果在访问了一个不存在的文章(这个文章已经被分享出去,但是在后台可能是被删除或者下线状态),那么就会导致所有的请求全部需要到数据库中进行查询,从而给数据库造成压力,甚至造成宕机。http://127.0.0.1:8081/doc/queryById?docId=不存在的id

2023-01-05 10:18:57.954  INFO 19692 --- [nio-8081-exec-8] c.g.r.s.impl.DocumentInfoServiceImpl     : ==== select from db ====
2023-01-05 10:18:58.121  INFO 19692 --- [nio-8081-exec-5] c.g.r.s.impl.DocumentInfoServiceImpl     : ==== select from db ====
2023-01-05 10:18:58.350  INFO 19692 --- [io-8081-exec-10] c.g.r.s.impl.DocumentInfoServiceImpl     : ==== select from db ====
2023-01-05 10:18:58.519  INFO 19692 --- [nio-8081-exec-3] c.g.r.s.impl.DocumentInfoServiceImpl     : ==== select from db ====
2023-01-05 10:18:58.661  INFO 19692 --- [nio-8081-exec-6] c.g.r.s.impl.DocumentInfoServiceImpl     : ==== select from db ====
2023-01-05 10:18:58.859  INFO 19692 --- [nio-8081-exec-4] c.g.r.s.impl.DocumentInfoServiceImpl     : ==== select from db ====
2023-01-05 10:18:59.012  INFO 19692 --- [nio-8081-exec-9] c.g.r.s.impl.DocumentInfoServiceImpl     : ==== select from db ====
2023-01-05 10:18:59.154  INFO 19692 --- [nio-8081-exec-7] c.g.r.s.impl.DocumentInfoServiceImpl     : ==== select from db ====
复制代码

解决方案一:缓存空对象

针对缓存穿透问题缓存空对象可以有效避免所产生的影响,当查询一条不存在的数据时,在缓存中存储一个空对象并设置一个过期时间(设置过期时间是为了避免出现数据库中存在了数据但是缓存中仍然是空数据现象),这样可以避免所有请求全部查询数据库的情况。

        // 查询对象不存在
        if(StrUtil.equals(obj,"")){
            log.info("==== select from cache , data not available ====");
            return null;
        }
        if (StrUtil.isNotEmpty(obj)) {
            log.info("==== select from cache ====");
            documentInfo = JSONUtil.toBean(obj, DocumentInfo.class);
        } else {
            log.info("==== select from db ====");
            documentInfo = this.lambdaQuery().eq(DocumentInfo::getId, docId).one();
            //如果数据不存在,则缓存一个空对象并设置过期时间
            stringRedisTemplate.opsForValue().set(redisKey, ObjectUtil.isNotNull(documentInfo)?JSONUtil.toJsonStr(documentInfo):"", 5L, TimeUnit.SECONDS);
//            if (ObjectUtil.isNotNull(documentInfo)) {
//                stringRedisTemplate.opsForValue().set(redisKey, JSONUtil.toJsonStr(documentInfo), 5L, TimeUnit.SECONDS);
//            }
        }
复制代码
2023-01-05 13:15:01.057  INFO 16600 --- [nio-8081-exec-3] c.g.r.s.impl.DocumentInfoServiceImpl     : ==== select from db ====

2023-01-05 13:15:01.214  INFO 16600 --- [nio-8081-exec-4] c.g.r.s.impl.DocumentInfoServiceImpl     : ==== select from cache , data not available ====
2023-01-05 13:15:01.384  INFO 16600 --- [nio-8081-exec-5] c.g.r.s.impl.DocumentInfoServiceImpl     : ==== select from cache , data not available ====
2023-01-05 13:15:01.540  INFO 16600 --- [nio-8081-exec-6] c.g.r.s.impl.DocumentInfoServiceImpl     : ==== select from cache , data not available ====
2023-01-05 13:15:01.720  INFO 16600 --- [nio-8081-exec-7] c.g.r.s.impl.DocumentInfoServiceImpl     : ==== select from cache , data not available ====
复制代码

解决方案二:布隆过滤器

缓存空对象的缺点在于无论数据存不存在都需要查询一次数据库,并且redis中存储了大量的空数据,这个时候可以采用布隆过滤器来解决。布隆过滤器可以简单的理解为由一个很长的二进制数组结合n个hash算法计算出n个数组下标,将这些数据下标置为1。在查找数据时,再次通过n个hash算法计算出数组下标,如果这些下标的值为1,表示该值可能存

Redis进阶 - 缓存问题:一致性、穿击、穿透、雪崩、污染等. 01 为什么要理解Redis缓存问题? 在高并发的业务场景下,数据库大多数情况都是用户并发访问最薄弱的环节。所以,就需要使用redis做一个缓冲操作,让请求先访问到redis,而不是直接访问Mysql等数据库。这样可以大大缓解数据库的压力。 当缓存库出现时,必须要考虑如下问题: 缓存穿透 缓存穿击 缓存雪崩 缓存污染(或者满了) 缓存和数据库一致性 02 缓存穿透 问题来源 缓存穿透是指缓存和数据库中都没有的数据,而用户不断发起请求。由于缓存是不命中时被动写的,并且出于容错考虑,如果... 阅读详情

相关推荐

保姆级教程:用MS建完分子模型,如何一键转成LAMMPS能用的data文件?

本文详细介绍了如何将Materials Studio(MS)构建的分子模型转换为LAMMPS可用的data文件的全流程。重点讲解了力场选择、环境配置、转换命令参数解析及常见问题排查,帮助初学者快速掌握MS与LAMMPS之间的数据转换技巧,特别适合计算材料学领域的研究人员。

weixin_42665255的博客 471

Pinocchio 结合 CasADi 进行 IK 逆运动学及 Mujoco 仿真

摘要:本视频演示了如何利用Pinocchio和CasADi实现机械臂逆运动学(IK)求解,并在Mujoco中进行仿真。通过构建优化问题(最小化位姿误差),结合IPOPT求解器对机械臂关节角度进行优化。代码支持导入URDF/MJCF模型,并以so100机械臂为例,实现了末端执行器位置跟踪的仿真测试。关键步骤包括:建立平移/旋转误差函数、设置约束条件、调用CasADi求解,最终在Mujoco中实时更新关节状态完成运动控制。

darcsdn 1004

Python+Seaborn可视化:双因素方差分析结果这样呈现更专业

本文详细介绍了如何利用Python中的Seaborn和statsmodels库,专业地执行并可视化双因素方差分析结果。通过模拟实验数据,演示了从模型构建、统计检验(包括交互作用分析)到诊断验证的全流程。重点展示了如何使用Seaborn绘制交互效应图、组合分布图及热图,将复杂的统计结果转化为直观、富有洞察力的可视化图表,助力科研论文与商业报告的专业呈现。

weixin_29274969的博客 137

离心泵级间导叶的设计计算

离心泵级间导叶的设计计算高速离心泵级间流道的作用是将第一级复合叶轮流出的高速液流收集起来并送入第二级复合叶轮。级间流道的设计应考虑降低液体的流速,实现动能向压力能转化,减小液体的水力损失。级间流道主要有蜗壳形流道和导叶两种结构形...

cqzc55322的博客 2566

保姆级教程:在Ubuntu 20.04上从零搞定ROS Noetic和RealSense驱动,为无人机开发铺路

本文提供了一份详细的Ubuntu 20.04环境下ROS Noetic和RealSense驱动安装指南,专为无人机开发环境搭建设计。从系统配置、ROS安装到RealSense驱动集成,涵盖所有关键步骤和常见问题解决方案,帮助开发者快速搭建自主空中机器人开发平台。

weixin_30329623的博客 65

手把手教你学Simulink——基于Simulink的储能PCS(功率转换系统)离网V/f控制仿真示例

本文带你从零开始,在Simulink中硬核还原了一套完整的100kW 储能PCS离网V/f控制系统。通过引入:✅无PLL的自主角度生成机制(实现真正的“构网”能力)✅电压外环与电流内环的双闭环解耦架构(兼顾稳态精度与动态响应)✅黑启动软充电与有源阻尼等工程化手段(确保系统平滑建压与抗谐振)核心收获透彻理解了储能系统在无大电网支撑下的自主运行机理。掌握了从“跟网(Grid-Following)”向“构网(Grid-Forming)”跨越的控制算法核心差异。

MHD0815的博客 78

【Qt】QtCreator远程部署、调试程序

1)QtCreator 工具–> 选项 --> 设备 --> 添加2)设备设置向导选择–> Generic Linux Device --> 开启向导3)填写“标识配置的名称”(随便写)、设备IP、用户名 --> 下一步4)选择配对秘密文件,第一次配对,可以不填写,点击“下一步”5)点击“完成”6)设备添加成功,需要再配置SSH端口号、用户名、密码等7)保存SSH登陆密码:验证类型选择默认“Default”–> “创建新密钥”–> “Test”

郭老二 5194

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

上一篇: 程序员该如何用“高科技”智能地教狗狗上厕所
下一篇: 如何把 高并发限流 实现的那叫一个优雅!
Java_LingFeng
博客等级 码龄4年 505粉丝 259原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值