关于缓存异常:缓存雪崩、击穿、穿透的解决方案

前言

关于缓存异常,我们常见的有三个问题:缓存雪崩、缓存击穿、缓存穿透。 这三个问题一旦发生,会导致大量请求直接落到数据库层面。如果请求的并发量很大,会影响数据库的运行,严重的会导致数据库宕机。

为了避免异常带来的损失,我们需要了解每种异常的原因以及解决方案,提高系统的可靠性。

缓存雪崩

缓存雪崩是指大量的应用请求无法在Redis缓存中进行处理,从而使得大量请求发送到数据库层,导致数据库压力过大甚至宕机。

缓存雪崩的原因

第一个原因:同一时间缓存中的数据大面积过期。

具体来说,把热点数据保存在缓存中,并且设置了过期时间,如果在某一个时刻,大量的Key同时过期,此时,应用再访问这些数据的话,就会发生缓存缺失。然后应用就会把请求发送给数据库,如果应用的并发请求量很大,(比如秒杀),那么数据库的压力也会很大,这会进一步影响到其他正常业务的请求处理。

如下图所示:
在这里插入图片描述
第二个原因:Redis 缓存实例发生故障宕机。

解决方案

1、解决热点数据集中失效

针对大量数据集中失效带来的缓存雪崩问题,可以用下面几种方案解决:

  • 均匀过期:给热点数据设置不同的过期时间,给每个key的失效时间加一个随机值;
  • 设置热点数据永不过期:不设置失效时间,有更新的话,需要更新缓存;
  • 服务降级:指服务针对不同的数据采用不同的处理方式:
    • 业务访问的是非核心数据,直接返回预定义信息、空值或者报错;
    • 业务访问核心数据,则允许访问缓存,如果缓存缺失,可以读取数据库。

2、解决Redis实例宕机问题

方案一: 实现服务熔断或者请求限流机制
我们通过监测Redis以及数据库实例所在服务器负载指标,如果发现Redis服务宕机,导致数据库的负载压力增大,我们可以启动服务熔断机制,暂停对缓存服务的访问。

但是这种方法对业务应用的影响比较大,我们也可以通过限流的方式降低这种影响。

举个例子:比如业务系统正常运行时,请求入口每秒最大允许进入的请求数是1万个,其中9000请求个可以被缓存处理,余下1000个会发送给数据库处理。

一旦发生雪崩,数据库每秒处理的请求突然增加到1万个,此时我们就可以启动限流机制。在前端请求入口处,只允许每秒进入1000个请求,其他的直接拒绝掉。这样就可以避免大量并发请求发送给数据库。

方案二:事前预防
通过主从节点的方式构建 Redis 缓存高可靠集群。 如果 Redis 缓存的主节点故障宕机了,从节点还可以切换成为主节点,继续提供缓存服务,避免了由于缓存实例宕机而导致的缓存雪崩问题。

缓存击穿

缓存击穿和缓存雪崩有点像,但是也有一点区别。

缓存雪崩是因为大面积的缓存失效,打崩了数据库。而缓存击穿是指某个访问非常频繁的热点数据,大量并发请求集中在这一个点访问,在这个Key失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,就像在一个屏障上凿了一个洞。

解决方案

  1. 设置热点数据永不过期:不设置失效时间,有更新的话,需要更新缓存;
  2. 加互斥锁:单机可以使用synchronizedlock,分布式可以使用lua脚本。

关于分布式锁有兴趣的同学可以看下这篇:Redis分布式锁的正确打开方式

缓存穿透

缓存穿透指用户要访问的数据既不在缓存中也不在数据库中,导致用户每次请求该数据时都要去数据库查一遍,然后返回空。

如果有恶意攻击者不断请求这种系统不存在的数据,会导致数据库压力过大,严重会击垮数据库。

解决方案

  • 接口层增加校验:用户鉴权、参数校验(请求参数是否合法、请求字段是否不存在等等);
  • 缓存空值/缺省值:发生缓存穿透时,我们可以在Redis中缓存一个空值或者缺省值(例如,库存缺省值为0),这样就避免了把大量请求发送给数据库处理,保持了数据库的正常运行。这种方法会存在两个问题:
    • 如果有大量的Key穿透,缓存空对象会占用宝贵的内存空间。针对这种情况可以给空对象设置过期时间。
    • 设置过期时间之后,可能会有缓存与数据库不一致的情况。
  • 布隆过滤器:快速判断数据是否存在,避免从数据库中查询数据是否存在,减轻数据库压力。

前面两种方案比较好实现,接下面我们介绍下布隆过滤器。

布隆过滤器

布隆过滤器(Bloom Filter)是1970年由布隆提出的。它实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中。

  • 优点:空间效率和查询时间都远远超过一般的算法。
  • 缺点:有一定的误识别率,删除困难。

布隆过滤器原理

布隆过滤器本质上是一个初始值都为 0 的 二进制数组和 N 个哈希函数组成

当我们想标记某个数据存在时(例如,数据x已被写入数据库),布隆过滤器会通过三个操作完成标记:

  • 首先,使用 N 个哈希函数,分别计算这个数据的哈希值,得到 N 个哈希值。
  • 然后,我们把这 N 个哈希值对 bit 数组的长度取模,得到每个哈希值在数组中的对应位置。
  • 最后,我们把对应位置的 bit 位设置为 1,这就完成了在布隆过滤器中标记数据的操作。

如下图所示:
在这里插入图片描述

三次哈希,对应的二进制数组下标分别是 1、3、7,将原始数据从 0 变为 1。

同样的,我们标记数据y的逻辑也是一样的。如下图:
在这里插入图片描述

三次哈希,对应的二进制数组下标分别是 1、5、9,将原始数据从 0 变为 1。

下标 1,之前已经被操作设置成 1,则本次认为是哈希冲突,不需要改动。

Hash 规则:如果在 Hash 后,原始位它是 0 的话,将其从 0 变为 1;如果本身这一位就是 1 的话,则保持不变。

正是因为存在哈希冲突,导致布隆过滤器的判断存在误差

因为哈希冲突的存在,导致布隆过滤器不能轻易删除数据,存在误删的风险。

布隆过滤器减少误差的方法:

  • 增加二进制位数组的长度,这样hash后的数据会更加离散化,出现冲突的概率会大大降低;
  • 增加Hash的次数,变相的增加数据特征,特征越多,冲突的概率越小。

布隆过滤器如何使用

比如,当查询一件商品的缓存信息时,我们首先要判断这件商品是否存在。

  • 通过三个哈希函数对商品id计算哈希值;
  • 然后,在布隆数组中查找访问对应的位值,0或1;
  • 判断,三个值中,只要有一个不是1,那么我们认为数据是不存在的。

如下图:
在这里插入图片描述
注意:布隆过滤器只能精确判断数据不存在情况,对于存在我们只能说是可能,因为存在Hash冲突情况,当然这个概率非常低。

在Java中使用布隆过滤器

至于在Java应用中使用布隆过滤器,我们可以通过Redisson实现,它内置了布隆过滤器。

首先引入依赖:

<dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.15.0</version>
</dependency>

代码示例:

import org.redisson.Redisson;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;public class RedissonBloomFilter {public static void main(String[] args) {Config config = new Config();config.useSingleServer().setAddress("redis://127.0.0.1:6379");RedissonClient client = Redisson.create(config);RBloomFilter<String> bloomFilter = client.getBloomFilter("test-bloom-filter");// 初始化布隆过滤器,数组长度100W,误判率 1%bloomFilter.tryInit(1000000L, 0.01);// 添加数据bloomFilter.add("Shawn");// 判断是否存在System.out.println(bloomFilter.contains("xujunson"));System.out.println(bloomFilter.contains("Shawn"));}
}

运行结果:

false   // 肯定不存在
true    // 可能存在,有1%的误判率

好了,以上就是关于缓存异常的整理。跟缓存雪崩、缓存击穿这两类问题相比,缓存穿透的影响更大一些,需要重点关注一下。


---------------------
作者:徐俊生
来源:CSDN
原文:https://blog.csdn.net/D812359/article/details/120648158
版权声明:本文为作者原创文章,转载请附上博文链接!
内容解析By:CSDN,CNBLOG博客文章一键转载插件

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

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

相关文章

RobotFramework 自动化测试实战进阶篇

工具 Robotframework, 采用PO设计模式 PO模型 PO模型即Page Objects&#xff0c;直译意思就是“页面对象”&#xff0c;通俗的讲就是把一个页面&#xff0c;或者说把一个页面的某个区域当做一个对象&#xff0c;通过封装这个对象可以实现调用。 PO设计的好处 代码复用&…

Android6 0权限机制(一):介绍

本篇文章已授权微信公众号 hongyangAndroid &#xff08;鸿洋&#xff09;独家发布 Android6.0权限机制&#xff08;一&#xff09;&#xff1a;介绍 Android6.0权限机制&#xff08;二&#xff09;&#xff1a;封装 Android6.0权限机制&#xff08;三&#xff09;&#xff1a;…

MAX10 ADC的一些基知识

MAX10 ADC 的一些知识 1、 MAX 10 内部集成的12bit SAR ADC的特点为&#xff1a; a、 采样速率高达1Mhz. b、 模拟通道多达18个&#xff0c;单个ADC多达17个&#xff0c;双ADC器件中有16个双功能ADC通道&#xff0c;2个专用的ADC。 c、 提供单端测…

Blazor University (42)JavaScript 互操作 —— 生命周期和内存泄漏

原文链接&#xff1a;https://blazor-university.com/javascript-interop/calling-dotnet-from-javascript/lifetimes-and-memory-leaks/生命周期和内存泄漏源代码[1]如果我们运行我们在从 Javascript 调用 .NET 中创建的应用程序并检查浏览器控制台窗口&#xff0c;我们会看到…

深入浅出聊布隆过滤器(Bloom Filter)

之前在网上看到过这么一段话&#x1f447; Data structures are nothing different. They are like the bookshelves of your application where you can organize your data. Different data structures will give you different facility and benefits. To properly use the …

第五周作业

本周作业内容&#xff1a;显示当前系统上root、fedora或user1用户的默认shell&#xff1b;#egrep "^(root|user1|fedora)" /etc/passwd|cut -d: -f72、找出/etc/rc.d/init.d/functions文件中某单词后面跟一组小括号的行&#xff0c;形如&#xff1a;hello()&#xff…

我为什么卸载了今日头条

曾经的自媒体人自述。 两三年前自媒体热曾席卷中国互联网&#xff0c;当时短视频还不是很火&#xff0c;一般的自媒体人都是以撰写文章为主&#xff0c;各种微信公众号层出不穷&#xff0c;10W的俗称 爆文&#xff08;豹纹&#xff09;。后来以今日头条为领头的短视频自媒体出现…

appium执行iOS测试脚本并发问题

appium1.4.XiOS9.Xxcode7.X: appium1.4.xiOS9.xxcode7.x&#xff0c;这一整套的配置做移动端自动化测试是测试人员常用的测试框架。关于&#xff0c;这一套测试框架的并发问题&#xff1a;基于mac端&#xff0c;启动多台appium服务器会导致appium的运行出错。这是因为多个appiu…

WinForm(五)控件和它的成员

窗体无疑是WinForm的主角&#xff0c;每个窗体都是用一个class来承载&#xff0c;那么窗体的控件&#xff0c;就是类中的私有字段了。每个窗体有三个文件&#xff0c;两个.cs文件&#xff0c;是一个分部类&#xff0c;Designer.cs是自动生成的C#代码&#xff0c;一般是拖拽控件…

Atitit.异常处理 嵌套  冗长的解决方案

Atitit.异常处理 嵌套 冗长的解决方案 1. 异常处理的需要改进的地方1 2. 异常设计的初衷是, 在程序中出现错误时, 由程序自己处理错误, 尽量不要以exit(0)这种粗暴的方式中止程序. 1 3. 正常流程和异常流程的分离。2 4. “是药三分毒”&#xff0c; 任何事物有缺点&#xff0c…

一文详解|增长那些事儿

目录 增长的背景 1.1 增长的定义 1.2 如何判断事物是否在增长 1.3 如何判断事物能否持续增长 如何进行增长 2.1 寻找增长机会点&#xff08;人的能力&#xff09; 2.1.1 发散与收剑找机会点 2.1.2 实验分析验证 2.1.3 增长洞察提取策略 2.1.4 如何找到大机会 2.2 设…

在MVC项目中使用Ninject

项目结构图&#xff1a; App_start文件夹中的文件是VS自己创建的&#xff0c;其中NinjectWebCommon类在创建之初并不存在。后面会再次提到&#xff01; 添加一个Home控制器。代码如下&#xff1a; using EssentialTools.Models; using Ninject; using System; using System.Col…

linux IP、端口连通性测试

ssh -v -p 50001 root10.210.200.82转载于:https://www.cnblogs.com/kuiyeit/p/6723508.html

紧急通知:360 网站卫士前端公共库已停止服务

所有使用了360前端公共库的开发者和站长们&#xff0c;请及时更换你的前端库的链接&#xff08;主要是前端库和谷歌 fonts&#xff09;&#xff0c;否则网站打开速度会极慢&#xff0c;甚至会在 Chrome 浏览器中崩溃。 360前端公共库曾经提供的服务有&#xff1a; 前端公共库&a…

一文学会Autofac的基础操作:几种实现注册方式、3种注入方式、生命周期、AOP以及过滤器实现依赖注入...

前言&#xff1a;直接开干。使用Autofac进行服务注册实践&#xff1a;新建三个项目&#xff0c;分别是webapi项目 Wesky.Core.Autofac以及两个类库项目 Wesky.Core.Interface和Wesky.Core.Service。在Webapi项目下&#xff0c;引用Autofac的三个包&#xff1a;Autofac、Autofac…

解析互联网广告术语 CPM、CPC、CPA、CPS、CPL、CPR 是什么意思

1. CPM&#xff08;Cost per mille&#xff09;&#xff0c;每千次展现收费 这是一种最为常见的广告模式&#xff0c;也是很多网站流量变现的一种途径&#xff0c;这种广告不管计算点击&#xff0c;或者什么注册下载之类的转化&#xff0c;只要这个广告在网站上被正常的展现给…

JavaScript数组迭代方法(图解)

转载于:https://www.cnblogs.com/seanna/p/6724032.html

Rider调试ASP.NET Core时报thread not gc-safe的解决方法

新建了一个ASP.NET Core 5.0的Web API项目&#xff0c;当使用断点调试Host.CreateDefaultBuilder(args)时&#xff0c;进入该函数后查看中间变量的值&#xff0c;报错Evaluation is not allowed: The thread is not at a GC-safe point。在群里问了也没人回应&#xff0c;可能没…

The SDK platform-tools version ((23)) is too old to check APIs compiled with API 26;

好像是更新过啥SDK之后&#xff0c;项目一直在包名的那一行显示红线&#xff0c;不过是不报编译错误的&#xff0c;就是看着老扎心老扎心的&#xff0c;开始以为是指定的SDK版本的问题&#xff0c;修改后发现无效&#xff0c;最后找到方法解决&#xff1a; 打开SDK Manager ---…

oracle 各种日期函数格式和操作

2019独角兽企业重金招聘Python工程师标准>>> ORACLE日期时间函数大全 TO_DATE格式(以时间:2007-11-02 13:45:25为例) Year: yy two digits 两位年 显示值:07 yyy three digits 三位年 显示值:00…