Redis 应用问题

news2024/11/26 4:34:28

1-缓存穿透

1.1-问题描述

Key 对应的数据在数据源并不存在,每次针对此 Key 的请求从缓存获取不到,请求都会压到数据源,从而可能压垮数据源。

比如:用一个不存在的用户ID 获取用户信息,不论缓存还是数据库都没有,若黑客利用此漏洞进行攻击可能压垮数据库。
在这里插入图片描述

1.2-解决方案

一个一定不存在缓存及查询不到的数据,由于缓存是不命中时被动写的,并且出于容错考虑,若从存储层查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到存储层去查询,失去了缓存的意义。

解决方案

  • 对空值缓存
    若一个查询返回的数据为空(不管是数据是否不存在),仍然把这个空结果(null)进行缓存,设置空结果的过期时间会很短,最长不超过五分钟
  • 设置可访问的名单(白名单)
    使用 Bitmaps 类型定义一个可以访问的名单,名单ID 作为 Bitmaps 的偏移量,每次访问和 Bitmap 里面的 ID 进行比较,若访问 ID 不在 Bitmaps 里面,进行拦截,不允许访问。
  • 采用布隆过滤器
    • 布隆过滤器(Bloom Filter)是 1970 年由布隆提出的。它实际上是一个很长的二进制向量(位图)和一系列随机映射函数(哈希函数)。
    • 布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难。
    • 将所有可能存在的数据哈希到一个足够大的 Bitmaps 中,一个一定不存在的数据会被这个 Bitmaps 拦截掉,从而避免了对底层存储系统的查询压力。
  • 进行实时监控:当发现 Redis 的命中率开始急速降低,需要排查访问对象和访问的数据,和运维人员配合,可以设置黑名单限制服务。

2-缓存击穿

2.1-问题描述

Key 对应的数据存在,但在 Redis 中过期,此时若有大量并发请求过来,这些请求发现缓存过期一般都会从数据库中加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把数据库压垮。
在这里插入图片描述

2.2-解决方案

Key 可能会在某些时间点被超高并发地访问,是一种非常热点的数据。这个时候需要考虑一个问题:缓存被击穿的问题。

解决方案

  • 预先设置热门数据:在 Redis 高峰访问之前,把一些热门数据提前存入到 Redis 里面,加大这些热门数据 Key 的时长
  • 实时调整:现场监控哪些数据热门,实时调整 Key 的过期时长。
  • 使用锁
    • 在缓存失效的时候(判断拿出来的值为空),不是立即去访问数据库。
    • 先使用缓存工具的某些带成功操作返回值的操作(比如 Redis 的 SETNX)去 Set 一个 mutex key。
    • 当操作返回成功时,再进行访问数据库的操作,并回设缓存,最后删除 mutex key。
    • 当操作返回失败,证明有线程在访问数据库,当前线程睡眠一段时间再重试整个获取缓存的方法。
      在这里插入图片描述

3-缓存雪崩

3.1-问题描述

Key 对应的数据存在,但在 Redis中过期,此时若有大量并发请求过来,这些请求发现缓存过期一般都会从数据库加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把数据库压垮。

缓存雪崩与缓存击穿的区别在于这里针对很多 Key 缓存,前者则是某一个 Key。

正常访问
在这里插入图片描述
缓存失效瞬间:
在这里插入图片描述

3.2-解决方案

缓存失效时的雪崩效应对底层系统的冲击非常可怕。

解决方案

  • 构建多级缓存架构:Nginx 缓存 + Redis 缓存 + 其他缓存。
  • 使用锁或队列
    用加锁或者队列的方式保证来保证不会有大量的线程对数据库一次性进行读写,从而避免失效时大量的并发请求落到底层存储系统上。不适用高并发情况。
  • 设置过期标志更新缓存
    记录缓存数据是否过期(设置提前量),若过期会触发通知另外的线程在后台去更新实际 Key 的缓存。
  • 将缓存失效时间分散开
    比如:可以在原有的失效时间基础上增加一个随机值,比如 1 - 5 分钟随机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件。

4-分布式锁

4.1-问题描述

随着业务发展的需要,原单体单机部署的系统被演化成分布式集群系统后,由于分布式系统多线程、多进程并且分布在不同机器上,这将使原单机部署情况下的并发控制锁策略失效,单纯的 Java API 并不能提供分布式锁的能力。

为了解决这个问题就需要一种跨 JVM 的互斥机制来控制共享资源的访问,这就是分布式锁要解决的问题。

分布式锁主流的实现方案

  • 基于数据库实现分布式锁。
  • 基于缓存:Redis等。
  • 基于 Zookeeper。

各自的优缺点

  • 性能:Redis 最高。
  • 可靠性:Zookeeper 最高。

4.2-解决方案

基于 Redis 实现分布式锁。
16.4.2. 解决方案:使用redis实现分布式锁
redis:命令
# set sku:1:info “OK” NX PX 10000
EX second :设置键的过期时间为 second 秒。 SET key value EX second 效果等同于 SETEX key second value 。
PX millisecond :设置键的过期时间为 millisecond 毫秒。 SET key value PX millisecond 效果等同于 PSETEX key millisecond value 。
NX :只在键不存在时,才对键进行设置操作。 SET key value NX 效果等同于 SETNX key value 。
XX :只在键已经存在时,才对键进行设置操作。
在这里插入图片描述

  1. 多个客户端同时获取锁(setnx)
  2. 获取成功,执行业务逻辑{从db获取数据,放入缓存},执行完成释放锁(del)
  3. 其他客户端等待重试

4.3-示例代码

16.4.3. 编写代码
Redis: set num 0
@GetMapping(“testLock”)
public void testLock(){
//1获取锁,setne
Boolean lock = redisTemplate.opsForValue().setIfAbsent(“lock”, “111”);
//2获取锁成功、查询num的值
if(lock){
Object value = redisTemplate.opsForValue().get(“num”);
//2.1判断num为空return
if(StringUtils.isEmpty(value)){
return;
}
//2.2有值就转成成int
int num = Integer.parseInt(value+“”);
//2.3把redis的num加1
redisTemplate.opsForValue().set(“num”, ++num);
//2.4释放锁,del
redisTemplate.delete(“lock”);

}else{
    //3获取锁失败、每隔0.1秒再获取
    try {
        Thread.sleep(100);
        testLock();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

}

重启,服务集群,通过网关压力测试:
ab -n 1000 -c 100 http://192.168.140.1:8080/test/testLock
在这里插入图片描述

查看redis中num的值:
在这里插入图片描述

基本实现。
问题:setnx刚好获取到锁,业务逻辑出现异常,导致锁无法释放
解决:设置过期时间,自动释放锁。
16.4.4. 优化之设置锁的过期时间
设置过期时间有两种方式:

  1. 首先想到通过expire设置过期时间(缺乏原子性:如果在setnx和expire之间出现异常,锁也无法释放)
  2. 在set时指定过期时间(推荐)
    在这里插入图片描述

设置过期时间:
在这里插入图片描述

压力测试肯定也没有问题。自行测试
问题:可能会释放其他服务器的锁。

场景:如果业务逻辑的执行时间是7s。执行流程如下

  1. index1业务逻辑没执行完,3秒后锁被自动释放。
  2. index2获取到锁,执行业务逻辑,3秒后锁被自动释放。
  3. index3获取到锁,执行业务逻辑
  4. index1业务逻辑执行完成,开始调用del释放锁,这时释放的是index3的锁,导致index3的业务只执行1s就被别人释放。
    最终等于没锁的情况。

解决:setnx获取锁时,设置一个指定的唯一值(例如:uuid);释放前获取这个值,判断是否自己的锁
16.4.5. 优化之UUID防误删

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

问题:删除操作缺乏原子性。
场景:

  1. index1执行删除时,查询到的lock值确实和uuid相等
    uuid=v1
    set(lock,uuid);

  2. index1执行删除前,lock刚好过期时间已到,被redis自动释放
    在redis中没有了lock,没有了锁。

  3. index2获取了lock
    index2线程获取到了cpu的资源,开始执行方法
    uuid=v2
    set(lock,uuid);

  4. index1执行删除,此时会把index2的lock删除
    index1 因为已经在方法中了,所以不需要重新上锁。index1有执行的权限。index1已经比较完成了,这个时候,开始执行

删除的index2的锁!

16.4.6. 优化之LUA脚本保证删除的原子性
@GetMapping(“testLockLua”)
public void testLockLua() {
//1 声明一个uuid ,将做为一个value 放入我们的key所对应的值中
String uuid = UUID.randomUUID().toString();
//2 定义一个锁:lua 脚本可以使用同一把锁,来实现删除!
String skuId = “25”; // 访问skuId 为25号的商品 100008348542
String locKey = “lock:” + skuId; // 锁住的是每个商品的数据

// 3 获取锁
Boolean lock = redisTemplate.opsForValue().setIfAbsent(locKey, uuid, 3, TimeUnit.SECONDS);

// 第一种: lock 与过期时间中间不写任何的代码。
// redisTemplate.expire("lock",10, TimeUnit.SECONDS);//设置过期时间
// 如果true
if (lock) {
    // 执行的业务逻辑开始
    // 获取缓存中的num 数据
    Object value = redisTemplate.opsForValue().get("num");
    // 如果是空直接返回
    if (StringUtils.isEmpty(value)) {
        return;
    }
    // 不是空 如果说在这出现了异常! 那么delete 就删除失败! 也就是说锁永远存在!
    int num = Integer.parseInt(value + "");
    // 使num 每次+1 放入缓存
    redisTemplate.opsForValue().set("num", String.valueOf(++num));
    /*使用lua脚本来锁*/
    // 定义lua 脚本
    String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
    // 使用redis执行lua执行
    DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
    redisScript.setScriptText(script);
    // 设置一下返回值类型 为Long
    // 因为删除判断的时候,返回的0,给其封装为数据类型。如果不封装那么默认返回String 类型,
    // 那么返回字符串与0 会有发生错误。
    redisScript.setResultType(Long.class);
    // 第一个要是script 脚本 ,第二个需要判断的key,第三个就是key所对应的值。
    redisTemplate.execute(redisScript, Arrays.asList(locKey), uuid);
} else {
    // 其他线程等待
    try {
        // 睡眠
        Thread.sleep(1000);
        // 睡醒了之后,调用方法。
        testLockLua();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

}

Lua 脚本详解:
在这里插入图片描述

项目中正确使用:

  1. 定义key,key应该是为每个sku定义的,也就是每个sku有一把锁。
    String locKey =“lock:”+skuId; // 锁住的是每个商品的数据
    Boolean lock = redisTemplate.opsForValue().setIfAbsent(locKey, uuid,3,TimeUnit.SECONDS);

在这里插入图片描述

16.4.7. 总结
1、加锁
// 1. 从redis中获取锁,set k1 v1 px 20000 nx
String uuid = UUID.randomUUID().toString();
Boolean lock = this.redisTemplate.opsForValue()
.setIfAbsent(“lock”, uuid, 2, TimeUnit.SECONDS);

2、使用lua释放锁
// 2. 释放锁 del
String script = “if redis.call(‘get’, KEYS[1]) == ARGV[1] then return redis.call(‘del’, KEYS[1]) else return 0 end”;
// 设置lua脚本返回的数据类型
DefaultRedisScript redisScript = new DefaultRedisScript<>();
// 设置lua脚本返回类型为Long
redisScript.setResultType(Long.class);
redisScript.setScriptText(script);
redisTemplate.execute(redisScript, Arrays.asList(“lock”),uuid);

3、重试
Thread.sleep(500);
testLock();

为了确保分布式锁可用,我们至少要确保锁的实现同时满足以下四个条件:

  • 互斥性。在任意时刻,只有一个客户端能持有锁。
  • 不会发生死锁。即使有一个客户端在持有锁的期间崩溃而没有主动解锁,也能保证后续其他客户端能加锁。
  • 解铃还须系铃人。加锁和解锁必须是同一个客户端,客户端自己不能把别人加的锁给解了。
  • 加锁和解锁必须具有原子性。

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

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

相关文章

有哪些适合做小说推文的配音软件?

其实现在市面上的短视频配音软件数不胜数&#xff0c;怎样才能挑选到简单又实用的呢&#xff1f;今天我就来给你盘一盘各大配音软件的优缺点&#xff0c;大家可以根据自己的需求来选择合适的配音软件&#xff01; 一、悦音配音 推荐指数★★★★★ 推荐理由&#xff1a;无需自…

SpringBoot SerializationUtils克隆(反序列化) 类加载器不一致问题(ClassCastException)

问题分析 在SpringBoot中使用 org.apache.commons.lang.SerializationUtils.clone 方法时&#xff0c;发现克隆出来的类强转对应类时发生类型不一致的错误&#xff0c;经过检测发现两个看似相同的类的类加载器不一致 场景 报错信息 java.lang.ClassCastException: com.tianq…

伦敦金投资怎么玩收益高?

伦敦金&#xff0c;作为国际黄金市场的代表&#xff0c;一直备受投资者的关注。它有着高流动性和丰富的投资机会&#xff0c;但要真正玩得收益高&#xff0c;就需要掌握一些关键的方法和技巧。在本文中&#xff0c;万洲金业将为您提供专业建议&#xff0c;告诉您伦敦金投资怎么…

【广州华锐互动】VR虚拟现实技术在森林防火教学中的应用

在探索大自然的神秘面纱时&#xff0c;我们往往会遇到一些意想不到的风险&#xff0c;其中之一就是森林火灾。森林火灾是一种毁灭性的自然灾害&#xff0c;可以在短时间内摧毁大片森林&#xff0c;对人类和动物的生存环境造成严重影响。然而&#xff0c;通过3D仿真技术&#xf…

SteerMouse for mac Mac万能鼠标设置工具 功能介绍

鼠标可谓是用户们在使用电脑时候的必备外接设备呢&#xff01;适合你自己的鼠标设置也绝对能够优化你的Mac使用体验&#xff01;想要更好的Mac体验就试试用Steermouse Mac版吧。它通过软件来自由设置你的鼠标操作&#xff01;在这款万能鼠标设置工具中&#xff0c;用户可以在偏…

uniapp在APP端使用swiper进行页面不卡顿滑动

uniapp在APP端使用swiper进行页面会卡顿&#xff0c;主要是渲染的数据有点多&#xff0c;这里只渲染三个数据就不好那么卡顿了&#xff0c;每次滑动后更新数据 <view><swiper change"changePoint" circular :disable-touch"disableTouch"><…

【Excel密码】四个方法,设置excel表格只读模式

Excel文件想要设置成只读模式&#xff0c;其实很简单&#xff0c;今天给大家分享四个excel设置只读模式的方法。 方法一&#xff1a;文件属性 右键点击文件&#xff0c;查看文件属性&#xff0c;在属性界面&#xff0c;勾选上只读属性就可以了。 方法二&#xff1a;始终以只读…

g.Grafana之Gauge的图形说明

直接上操作截图 1. 创建一个新的Dashboard 2.为Dashboard创建变量 【General】下的Name与Label的名称自定义 【Query options】 下的Group可以填写Zabbix内的所有组/.*/ , 然后通过Regex正则过滤需要的组名 3.设置Dashboard的图形 我使用文字来描述下这个图 1.我们在dash…

【分享】winterm ssh登录报错Unkown error

非软文哈&#xff0c;实测Winterm非常好用&#xff0c;唯一的障碍是 某些特定服务器ssh登录报错Unkown error 后经github issue得知&#xff0c;关闭会话设置-ssh选项卡中的 尝试键盘交互认证的勾即可。 https://github.com/kingToolbox/WindTerm/issues/1922

【C++】基础语法(上)

C基础语法 此语法笔记面向算法竞赛考研&#xff0c;可供参考&#xff0c;本人的一些笔记的记录~ 失踪人口回归&#xff0c;将近半个月没有更新&#xff0c;那么接下来也会逐步开始更新分享知识内容~ 本篇将分享cpp基础语法中的变量、输出输入语句、表达式、顺序语句、条件判断…

el-table添加class类名并添加样式

先上效果图&#xff1a;输入框内容被 内边距 padding: 0 12px; 但是只想在这列开始去除内边距 理想效果 解决方案 AddClass <!-- 给某个td里面添加类名:cell-class-name --> <el-table:data"data"size"mini"borderselection-change"sele…

大恒相机在halcon里面的可读可写参数和只读参数

查询图像采集设备具体参数的算子 get_framegrabber_param (AcqHandle, available_param_names, AvailableParamNames) U3相机 可读可写 只读参数 活动参数 网口相机 只读参数 活动参数

Kafka-入门及简单示例

启动与简单示例 # 命令行1 #开启Zookeeper E:\>cd E:\kafka_2.13-3.6.0E:\kafka_2.13-3.6.0>bin\windows\zookeeper-server-start.bat config\zookeeper.properties # 命令行2 #开启Kafka E:\>cd E:\kafka_2.13-3.6.0 E:\kafka_2.13-3.6.0>bin\windows\kafka-serv…

【Windows-软件-FFmpeg】(01)通过CMD运行FFmpeg进行操作,快速上手

前言 通过"cmd"运行"ffmpeg"进行操作&#xff0c;快速上手&#xff1b; 实操 【实操一】 说明 使用"FFmpeg"来合并音频文件和视频文件 &#xff1b; 环境 Windows 11 专业版&#xff08;22621.2428&#xff09;&#xff1b; 代码 &#xf…

【MATLAB源码-第65期】基于matlab的OFDM/OTFS通信系统性能对比,输处误码率曲线;对比是否采用LDPC编码。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 OTFS&#xff08;Orthogonal Time Frequency Space&#xff09;是一种无线通信调制技术&#xff0c;它利用时间、频率和空间的正交性来传输数据&#xff0c;目的是提高无线通信系统的性能&#xff0c;尤其是在多径和高移动性环…

fastapi-参数

路径参数 你可以使用与 Python 格式化字符串相同的语法来声明路径"参数"或"变量"&#xff1a; from fastapi import FastAPIapp FastAPI()app.get("/items/{item_id}") async def item_details(item_id: int):return {"item_id": i…

关于电容充放电

计算下电容放电时&#xff0c;其电压和时间的关系 百度&#xff1a;电容充放电电压公式 电容充放电时间 百度安全验证https://baijiahao.baidu.com/s?id1618374634281434486&wfrspider&forpc 电容充电放电时间计算公式&#xff1a; 设&#xff0c;V0 为电容上的初始…

双十一有哪些值得买的东西?2023年双十一运动好物强烈推荐

看着日历上的双十一正在一步一步的向我们挥手&#xff0c;同时也能感受到各大电商平台的预热活动&#xff0c;我们很清楚的知道双十一快要到来了&#xff0c;这也意味着一年一度疯狂扫货的日子也快到了&#xff0c;那么在今年双十一有哪些值得买的东西&#xff1f;作为一个玩转…

自动驾驶算法(三):RRT算法讲解与代码实现(基于采样的路径规划)

目录 1 RRT算法原理 2 RRT算法代码解析 3 RRT完整代码 1 RRT算法原理 RRT算法的全称是快速扩展随机树算法(Rapidly Exploring Random Tree)&#xff0c;它的想法就是从根结点长出一棵树当树枝长到终点的时候这样就能找到从终点到根节点的唯一路径。 算法流程&#xff1a; 首先…

Jorani远程命令执行漏洞 CVE-2023-26469

Jorani远程命令执行漏洞 CVE-2023-26469 漏洞描述漏洞影响漏洞危害网络测绘Fofa: title"Jorani"Hunter: web.title"Jorani" 漏洞复现1. 获取cookie2. 构造poc3. 执行命令 漏洞描述 Jorani是一款开源的员工考勤和休假管理系统&#xff0c;适用于中小型企业…