当前位置: 首页 > news >正文

Redis应用问题解决

16. Redis应用问题解决

16.1 缓存穿透

16.1.1 问题描述

key对应的数据在数据源并不存在,每次针对此key的请求从缓存获取不到,请求都会压到数据源,从而可能压垮数据源。比如用一个不存在的用户id获取用户信息,不论缓存还是数据库都没有,若黑客利用此漏洞进行攻击可能压垮数据库。

 16.1.2 解决方案

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

解决方案:

对空值缓存:如果一个查询返回的数据为空(不管是数据是否不存在),我们仍然把这个空结果(null)进行缓存,设置空结果的过期时间会很短,最长不超过五分钟

设置可访问的名单(白名单):

使用bitmaps类型定义一个可以访问的名单,名单id作为bitmaps的偏移量,每次访问和bitmap里面的id进行比较,如果访问id不在bitmaps里面,进行拦截,不允许访问。

采用布隆过滤器:(布隆过滤器(Bloom Filter)是1970年由布隆提出的。它实际上是一个很长的二进制向量(位图)和一系列随机映射函数(哈希函数)。

布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难。)

将所有可能存在的数据哈希到一个足够大的bitmaps中,一个一定不存在的数据会被 这个bitmaps拦截掉,从而避免了对底层存储系统的查询压力。

进行实时监控:当发现Redis的命中率开始急速降低,需要排查访问对象和访问的数据,和运维人员配合,可以设置黑名单限制服务

16.2 缓存击穿

16.2.1 问题描述

 16.2.2 解决方案

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

解决问题:

(1)预先设置热门数据:在redis高峰访问之前,把一些热门数据提前存入到redis里面,加大这些热门数据key的时长

(2)实时调整:现场监控哪些数据热门,实时调整key的过期时长

(3)使用锁:

  1. 就是在缓存失效的时候(判断拿出来的值为空),不是立即去load db。
  2. 先使用缓存工具的某些带成功操作返回值的操作(比如Redis的SETNX)去set一个mutex key
  3. 当操作返回成功时,再进行load db的操作,并回设缓存,最后删除mutex key;
  4. 当操作返回失败,证明有线程在load db,当前线程睡眠一段时间再重试整个get缓存的方法。

16.3 缓存雪崩 

16.3.1 问题描述

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

缓存雪崩与缓存击穿的区别在于这里针对很多key缓存,前者则是某一个key正常访问

 缓存失效瞬间

16.3.2 解决方案 

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

解决方案:

构建多级缓存架构:

nginx缓存 + redis缓存 +其他缓存(ehcache等)

使用锁或队列

用加锁或者队列的方式保证来保证不会有大量的线程对数据库一次性进行读写,从而避免失效时大量的并发请求落到底层存储系统上。不适用高并发情况

设置过期标志更新缓存:

记录缓存数据是否过期(设置提前量),如果过期会触发通知另外的线程在后台去更新实际key的缓存。

将缓存失效时间分散开:

比如我们可以在原有的失效时间基础上增加一个随机值,比如1-5分钟随机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件。

16.4 分布式锁

16.4.1 问题描述

随着业务发展的需要,原单体单机部署的系统被演化成分布式集群系统后,由于分布式系统多线程、多进程并且分布在不同机器上,这将使原单机部署情况下的并发控制锁策略失效,单纯的Java API并不能提供分布式锁的能力。为了解决这个问题就需要一种跨JVM的互斥机制来控制共享资源的访问,这就是分布式锁要解决的问题!

分布式锁主流的实现方案:

1. 基于数据库实现分布式锁

2. 基于缓存(Redis等)

3. 基于Zookeeper

每一种分布式锁解决方案都有各自的优缺点:

1. 性能:redis最高

2. 可靠性:zookeeper最高

这里,我们就基于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. 其他客户端等待重试

16.4.3 编写代码

Redis: set num 0

@GetMapping("testLock")
public void testLock(){//1获取锁,setneBoolean lock = redisTemplate.opsForValue().setIfAbsent("lock", "111");//2获取锁成功、查询num的值if(lock){Object value = redisTemplate.opsForValue().get("num");//2.1判断num为空returnif(StringUtils.isEmpty(value)){return;}//2.2有值就转成成intint num = Integer.parseInt(value+"");//2.3把redis的num加1redisTemplate.opsForValue().set("num", ++num);//2.4释放锁,delredisTemplate.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秒后锁被自动释放。
  1. index2获取到锁,执行业务逻辑,3秒后锁被自动释放。
  2. index3获取到锁,执行业务逻辑
  3. index1业务逻辑执行完成,开始调用del释放锁,这时释放的是index3的锁,导致index3的业务只执行1s就被别人释放。

最终等于没锁的情况。

解决:setnx获取锁时,设置一个指定的唯一值(例如:uuid);释放前获取这个值,判断是否自己的锁

 16.4.5 优化之UUID防误删

 

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号的商品 100008348542String locKey = "lock:" + skuId; // 锁住的是每个商品的数据
// 3 获取锁Boolean lock = redisTemplate.opsForValue().setIfAbsent(locKey, uuid, 3, TimeUnit.SECONDS);// 第一种: lock 与过期时间中间不写任何的代码。// redisTemplate.expire("lock",10, TimeUnit.SECONDS);//设置过期时间// 如果trueif (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<Long> redisScript = new DefaultRedisScript<>();
// 设置lua脚本返回类型为Long
redisScript.setResultType(Long.class);
redisScript.setScriptText(script);
redisTemplate.execute(redisScript, Arrays.asList("lock"),uuid);

3. 重试

Thread.sleep(500);
testLock();

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

- 互斥性。在任意时刻,只有一个客户端能持有锁。

- 不会发生死锁。即使有一个客户端在持有锁的期间崩溃而没有主动解锁,也能保证后续其他客户端能加锁。

- 解铃还须系铃人。加锁和解锁必须是同一个客户端,客户端自己不能把别人加的锁给解了。

- 加锁和解锁必须具有原子性。

 

相关文章:

Redis应用问题解决

16. Redis应用问题解决 16.1 缓存穿透 16.1.1 问题描述 key对应的数据在数据源并不存在&#xff0c;每次针对此key的请求从缓存获取不到&#xff0c;请求都会压到数据源&#xff0c;从而可能压垮数据源。比如用一个不存在的用户id获取用户信息&#xff0c;不论缓存还是数据库…...

Qemu虚拟机读取物理机的物理网卡的流量信息方法

项目背景&#xff1a; 比如我有三个项目 A&#xff0c;B&#xff0c;C&#xff1b;其中A项目部署在物理机上&#xff0c;B&#xff0c;C项目部署在 虚拟机V1,V2中&#xff0c;三个项目接口需要相互调用。 需要解决的问题点&#xff1a; 1&#xff0c;因为A&#xff0c;B&#x…...

面试题之vue的响应式

文章目录前言一、响应式是什么&#xff1f;二、Object.defineProperty二、简单模拟vue三、深度监听四、监听数组总结前言 为了应对面试而进行的学习记录&#xff0c;可能不够有深度甚至有错误&#xff0c;还请各位谅解&#xff0c;并不吝赐教&#xff0c;共同进步。 一、响应式…...

聚焦弹性问题,杭州铭师堂的 Serverless 之路

作者&#xff1a;王彬、朱磊、史明伟 得益于互联网的发展&#xff0c;知识的传播有了新的载体&#xff0c;使用在线学习平台的学生规模逐年增长&#xff0c;越来越多学生在线上获取和使用学习资源&#xff0c;其中教育科技企业是比较独特的存在&#xff0c;他们担当的不仅仅是…...

NDK RTMP直播客户端二

在之前完成的实战项目【FFmpeg音视频播放器】属于拉流范畴&#xff0c;接下来将完成推流工作&#xff0c;通过RTMP实现推流&#xff0c;即直播客户端。简单的说&#xff0c;就是将手机采集的音频数据和视频数据&#xff0c;推到服务器端。 接下来的RTMP直播客户端系列&#xff…...

Python3--垃圾回收机制

一、概述 Python 内部采用 引用计数法&#xff0c;为每个对象维护引用次数&#xff0c;并据此回收不在需要的垃圾对象。由于引用计数法存在重大缺陷&#xff0c;循环引用时由内存泄露风险&#xff0c;因此Python还采用 标记清除法 来回收在循环引用的垃圾对象。此外&#xff0c…...

C/C++开发,认识opencv各模块

目录 一、opencv模块总述 二、opencv主要模块 2.1 opencv安装路径及内容 2.2 opencv模块头文件说明 2.3 成熟OpenCV主要模块 2.4 社区支持的opencv_contrib扩展主要模块 2.5 关于库文件的引用 一、opencv模块总述 opencv的主要能力在于图像处理&#xff0c;尤其是针对二维图…...

【WLSM、FDM状态估计】电力系统状态估计研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…...

准备2023(2024)蓝桥杯

前缀和 一维前缀和 s[i]s[i-1]a[i]二维前缀和&#xff08;子矩阵的和&#xff09; s[i][j]s[i-1][j]s[i][j-1]-s[i-1][j-1]a[i][j] 差分 一维数组 //b是差分数组b[i]c;b[j1]-c;例题 #include<iostream> using namespace std; int n,m; int b[100002],a[100002]; vo…...

剑指 Offer 60. n个骰子的点数

剑指 Offer 60. n个骰子的点数 难度&#xff1a;middle\color{orange}{middle}middle 题目描述 把n个骰子扔在地上&#xff0c;所有骰子朝上一面的点数之和为s。输入n&#xff0c;打印出s的所有可能的值出现的概率。 你需要用一个浮点数数组返回答案&#xff0c;其中第 i 个…...

阿里巴巴-淘宝搜索排序算法学习

模型效能&#xff1a;模型结构优化 模型效能&#xff1a;减枝 FLOPS&#xff1a;每秒浮点运算的次数 模型效能&#xff1a;量化 基于统计阈值限定&#xff0c;基于学习阈值限定。 平台效能&#xff1a;一站式DL训练平台 平台效能&#xff1a;搜索模型的系统流程 协同关系…...

〖Python网络爬虫实战⑮〗- pyquery的使用

订阅&#xff1a;新手可以订阅我的其他专栏。免费阶段订阅量1000python项目实战 Python编程基础教程系列&#xff08;零基础小白搬砖逆袭) 说明&#xff1a;本专栏持续更新中&#xff0c;目前专栏免费订阅&#xff0c;在转为付费专栏前订阅本专栏的&#xff0c;可以免费订阅付费…...

SQL综合查询下

SQL综合查询下 目录SQL综合查询下18、查询所有人都选修了的课程号与课程名题目代码题解19、SQL查询&#xff1a;查询没有参加选课的学生。题目代码20、SQL查询&#xff1a;统计各门课程选修人数&#xff0c;要求输出课程代号&#xff0c;课程名&#xff0c;有成绩人数&#xff…...

全连接层FC

lenet结构: 输入层(Input Layer):接收手写数字的图像数据,通常是28x28的灰度图像。 卷积层1(Convolutional Layer 1):对输入图像进行卷积操作,提取低级别的特征,使用 6 个大小为 5x5 的卷积核进行卷积,得到 6 个输出特征图,激活函数为 Sigmoid。 平均池化层1(Aver…...

图的遍历及连通性

文章目录 图的遍历及连通性程序设计程序分析图的遍历及连通性 【问题描述】 根据输入的图的邻接矩阵A,判断此图的连通分量的个数。 【输入形式】 第一行为图的结点个数n,之后的n行为邻接矩阵的内容,每行n个数表示。其中A[i][j]=1表示两个结点邻接,而A[i][j]=0表示两个结点无…...

DJ3-4 实时调度

目录 3.4.1 实现实时调度的基本条件 1. 提供必要的信息 2. 系统的处理能力强 3. 采用抢占式调度机制 4. 具有快速切换机制 3.4.2 实时调度算法的分类 1. 非抢占式调度算法 2. 抢占式调度算法 3.4.3 常用的几种实时调度算法 1. 最早截止时间优先 EDF&#xff08;Ea…...

Oracle之PL/SQL游标练习题(三)

游标练习题目1、定义游标&#xff1a;列出每个员工的姓名部门名称并编程显示第10个到第20个记录2、定义游标&#xff1a;从雇员表中显示工资大于3000的记录&#xff0c;只要姓名、部门编号和工资&#xff0c;编程显示其中的奇数记录3、用游标显示所有部门编号与名称&#xff0c…...

docker运行服务端性能监控系统Prometheus和数据分析系统Grafana

文章目录一、Prometheus的安装和运行1、使用docker拉取镜像2、创建prometheus.yml文件3、启动容器4、查看启动是否成功5、记录安装过程中出现的错误二、Grafana的安装和运行1、使用docker拉取镜像2、创建grafana3、运行grafana4、查看grafana运行日志5、登录grafana一、Prometh…...

【Linux】【应用层】多线程编程

一、线程创建 Linux 中的 pthread_create() 函数用来创建线程&#xff0c;它声明在<pthread.h>头文件中&#xff0c;语法格式如下&#xff1a; int pthread_create(pthread_t *thread,const pthread_attr_t *attr,void *(*start_routine) (void *),void *arg);各个参数…...

GameFramework 框架详解之 如何接入热更框架HybridCLR

一.前言 HybridCLR是一个特性完整、零成本、高性能、低内存的近乎完美的c#热更新方案 GameFramework是一个非常出色完整的基于Unity引擎的游戏框架,里面包含了非常多的模块,封装非常完整。 以前市面上的热更大多数都是Lua为主,后来出了一个ILRuntime的C#热更框架,虽然性能…...

全国青少年软件编程(Scratch)等级考试二级考试真题2023年3月——持续更新.....

一、单选题(共25题,共50分) 1. 小猫的程序如图所示,积木块的颜色与球的颜色一致。点击绿旗执行程序后,下列说法正确的是?( ) A.小猫一直在左右移动,嘴里一直说着“抓到了”。 B.小猫会碰到球,然后停止。 C.小猫一直在左右移动,嘴里一直说着“别跑” D.小猫会碰到球,…...

HTML2.1列表标签

列表标签种类 无序列表 有序列表 自定义列表 使用场景&#xff1a;在列表中按照行展示关联性内容。 特点&#xff1a;按照行的形式&#xff0c;整齐显示内容。 一、无序列表 标签名说明ul无序列表整体&#xff0c;用于包裹li标签li表示无序列表的每一项&#xff0c;用于包…...

在 Flutter 多人视频通话中实现虚拟背景、美颜与空间音效

前言 在之前的「基于声网 Flutter SDK 实现多人视频通话」里&#xff0c;我们通过 Flutter 声网 SDK 完美实现了跨平台和多人视频通话的效果&#xff0c;那么本篇我们将在之前例子的基础上进阶介绍一些常用的特效功能&#xff0c;包括虚拟背景、色彩增强、空间音频、基础变声…...

Ambari-web 架构

Ambari-web 使用的前端 Embar.js MVC 框架实现&#xff0c;Embar.js 是一个 TodoMVC 框架&#xff0c;涵盖了单页面应用&#xff08;single page application&#xff09;几乎所有的行为 Nodejs 是一个基于 Chrome JavaScript 运行时建立的一个平台&#xff0c;用来方便的搭建…...

对接百思买Best Buy EDI 的注意事项

在此前的文章&#xff1a;《Best Buy Drop Ship(Commerce hub) EDI业务测试常见报错及解决》中&#xff0c;我们介绍了在业务测试过程中遇到的常见报错及解决方案&#xff0c;以下在此基础上进行补充。 数据未能成功发送给Best Buy可能遇到的情况 Best Buy EDI项目传输业务报…...

2023年郑州重点建设项目名单公布,中创“算力数据中心”项目入选!

4月7日&#xff0c;郑州市人民政府网站公布2023年郑州市重点建设项目名单&#xff0c;名单共列项目680个&#xff0c;总投资1.08万亿元&#xff0c;年度计划投资2691亿元。 在创新驱动能力提升项目名单里&#xff0c;中创算力与人民网人民数据&#xff08;国家大数据灾备中心&a…...

Pytorch 容器 - 1. Module类介绍

目录 1. 基于Module构建自己的网络 2. Module的初始化变量 3. Modules中需要子类 forward() 4. Modules中其他内置函数 1. 基于Module构建自己的网络 torch.nn.Module是所有神经网络模块的基类&#xff0c;如何定义自已的网络&#xff1a; 由于 Module 是神经网络模块的基…...

百度墨卡托坐标转化笔记

一、墨卡托坐标转化 调研了python和java多种实现方式的转换&#xff0c;发现有的不符合需求&#xff0c;原因还没找到。 我是用百度地图返回的poi边界&#xff08;返回的是墨卡托坐标&#xff09; 转换的原理没有深入研究&#xff0c;直接拿来用的&#xff0c;测试可行&…...

每日学术速递4.12

CV - 计算机视觉 | ML - 机器学习 | RL - 强化学习 | NLP 自然语言处理 Subjects: cs.HC 随着新的“生成代理”论文的发布&#xff0c;LLM刚刚达到了一个重要的里程碑——通过使用 LLM&#xff0c;生成代理能够在受《模拟人生》启发的交互式沙箱中模拟类人行为。代理架构扩展…...

HarmonyOS/OpenHarmony公司级技术开发团队硬件基本配置清单

有朋友公司咨询进入HarmonyOS/OpenHarmony领域&#xff0c;组建技术团队&#xff0c;硬件设备的基本配置应该是怎么样的比较合适&#xff1f;这个是进入鸿蒙开发领域相关配置的第一步&#xff0c;我们以一个基本的团队配置为例说明&#xff0c;供想进入的团队参考。 HarmonyOS/…...