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

前言

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

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

缓存雪崩

缓存雪崩是指大量的应用请求无法在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设计的好处 代码复用&…

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 …

WinForm(五)控件和它的成员

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

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

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

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…

火山引擎李玉光:字节跳动大规模K8s集群管理实践

2022年5月31日&#xff0c;在CSDN云原生系列在线峰会第6期“K8s大规模应用和深度实践峰会”&#xff0c;火山引擎资深云原生架构师李玉光分享了《字节跳动大规模K8s集群管理实践》。 字节跳动云原生体系 字节跳动内部云原生技术的使用贯穿组织技术体系各层面&#xff0c;整体如…

(7)关于margin的一些想法2.0

这篇主要讨论的就是margin负值与float的关系。 首先&#xff0c;例子。 <!doctype html> <html> <head> <meta charset"utf-8"> <title>无标题文档</title> <style typetext/css> html,body{padding:0;margin:0;} div{wid…

什么是SRE?一文详解SRE运维体系

在任何有一定规模的企业内部&#xff0c;一旦推行起来整个SRE的运维模式&#xff0c;那么对于可观测性系统的建设将变得尤为重要&#xff0c;而在整个可观测性系统中。 可观测性系统 在任何有一定规模的企业内部&#xff0c;一旦推行起来整个SRE的运维模式&#xff0c;那么对于…

python初探

python近两年似乎已经很热了&#xff0c;不了解一下怎么能行呢&#xff0c;似乎python最大的优点就是简洁、易懂、优雅。目前豆瓣、知乎等后台服务使用的也都是python语言。 python一般可以用于网站服务、小工具、数据分析等工作。它作为高级语言&#xff0c;和js一样&#xff…

solr5.5索引mysql数据(新手总结)

一 solr5.5环境部署到Eclipse(luna版&#xff09; solr部署参见&#xff1a;http://blog.csdn.net/csmnjk/article/details/64121765 二 Ik分词器设置 IK分词器设置参见:http://blog.csdn.net/csmnjk/article/details/51693578 solr4版本的schema.xml文件对应solr5版本的manage…

老板加薪!看我做的WPF Loading!!!

老板加薪&#xff01;看我做的WPF Loading&#xff01;&#xff01;&#xff01;控件名&#xff1a;RingLoading作者&#xff1a;WPFDevelopersOrg原文链接&#xff1a; https://github.com/WPFDevelopersOrg/WPFDevelopers.Minimal框架使用大于等于.NET40&#xff1b;Visua…

如何避免下重复订单

电子交易的一个很基本的问题&#xff0c;就是避免用户下重复订单。用户明明想买一次&#xff0c;结果一看下了两个单。如果没有及时发现&#xff0c;就会带来额外的物流成本和扯皮。对商家的信誉也不好看。 从技术上看&#xff0c;这是一个分布式一致性问题&#xff1b;但实际…

图像分类学习笔记

1.计算机认识图像的方式&#xff1a;都是数字。例如一个 128X128 的3通道的图片 是由 128X128X3个数字 组成的。 2.面临的难点&#xff1a;一幅图可以说明。 3.分类器 A&#xff1a;Nearest Neighbor Classifier&#xff1a;与CNN无关&#xff0c;但是可以帮助我们理解一下分类…