[Redis] Redis的三大缓存异常原因分析和解决方案

Redis的三大缓存异常原因分析和解决方案

缓存的三个异常分别是缓存击穿、缓存雪崩、缓存穿透。这三个问题一旦发生,会导致大量的请求积压到数据库层,并发量巨大的情况下很有可能导致数据库宕机或是故障,造成严重的生产事故。

接下来就来看看这三种缓存异常的原因分析和解决方案。

缓存击穿

缓存击穿是指针对某个热点数据的请求,无法在缓存中处理,导致大量的针对该数据的请求一下子全都发送到了后端数据库,使数据库压力激增,影响到数据库处理其他的请求。

产生原因

缓存击穿的情况,经常是发生在热点数据过期失效的情况。

如下图:

[Redis] Redis的三大缓存异常原因分析和解决方案

解决方案

对于缓存击穿,解决方案其实也很直接。对于访问很频繁的热点数据,就不需要设置过期时间了。这样对热点数据的访问可以直接在缓存中进行处理,Redis的数万级别的高吞吐量可以很好的应对大量的并发请求。

缓存雪崩

缓存雪崩指的是大量的应用请求无法在Redis缓存中进行处理,从而被发送到数据库层,导致数据库层的压力激增。

产生原因

产生原因一般有两种情况。

第一种情况:缓存中有大量的数据同时过期,导致请求无法得到处理。

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

如下图:

[Redis] Redis的三大缓存异常原因分析和解决方案

第二中情况:Redis缓存实例发生故障,宕机了,导致大量请求积压到数据库。

一般来说,一个 Redis 实例可以支持数万级别的请求处理吞吐量,而单个数据库可能只能支持数千级别的请求处理吞吐量,它们两个的处理能力可能相差了近十倍。由于缓存雪崩,Redis 缓存失效,所以,数据库就可能要承受近十倍的请求压力,从而因为压力过大而崩溃。

解决方案

针对 缓存中有大量的数据同时过期的情况,可以提供两种解决方案。

  • 微调过期时间:给数据的到期时间增加一个较小的随机数,避免给大量的数据设置相同的过期的时间
  • 服务降级:当发生缓存雪崩时,针对不同的数据采取不同的处理方式。
  • 非核心数据:暂时停止从缓存中查询这些数据,而是直接返回预定义信息、空值或者是错误信息。
  • 核心数据:缓存数据丢失时通过数据库读取。

使用服务降级的方式,只有部分的数据请求会被发送到数据库,则数据库的压力就没有那么大了。

如下图:

[Redis] Redis的三大缓存异常原因分析和解决方案

针对 Redis缓存实例发生故障宕机的情况,同样也有两点建议。

  • 在业务系统中实现服务熔断或者请求限流机制
  • 服务熔断:在发生缓存雪崩时,为了防止引发连锁的数据库雪崩,甚至是整个系统的崩溃,我们暂停业务应用对缓存系统的接口访问。
  • 请求限流:在请求入口前端只允许每秒进入系统的请求数为 1000 个,再多的请求就会在入口前端被直接拒绝服务。
  • 提前预防。通过主从节点的方式构建 Redis 缓存高可靠集群。如果 Redis 缓存的主节点故障宕机了,从节点还可以切换成为主节点,继续提供缓存服务,避免了由于缓存实例宕机而导致的缓存雪崩问题。

如下图:

[Redis] Redis的三大缓存异常原因分析和解决方案

[Redis] Redis的三大缓存异常原因分析和解决方案

【注:服务熔断虽然可以保证数据库的正常运行,但是暂停了整个缓存系统的访问,对业务应用的影响范围大。为了尽可能减少这种影响,可以使用请求限流的方式。】

缓存穿透

缓存穿透指的是要访问的数据既不在Redis中,也不在数据库中,导致请求访问缓存缓缺失,访问数据库而数据库也无数据。这样一来应用无法从数据库中读取写入缓存,缓存成了摆设,同时给数据库和缓存都带来巨大的压力。

如下图:

[Redis] Redis的三大缓存异常原因分析和解决方案

产生原因

  • 业务层误操作:缓存中的数据和数据库中的数据被误删除了,所以缓存中和数据库中都没有数据。
  • 恶意攻击:专门访问数据库中没有数据。

解决方案

提供三种解决方案

  • 缓存空值或者缺省值。 一旦发生缓存穿透,可针对查询的数据在redis缓存中存一个空值或者缺省值,当应用发送后续请求进行查询的时候就可以从redis中读取到空值或者缺省值返回,避免大量请求数据库。
  • 使用布隆过滤器快速判断数据是否存在,避免从数据库中查询数据,减轻数据库压力 通过查询布隆过滤器快速判断数据是否存在,如果不存在就不需要再去数据库中查询了。
  • 在请求入口的前端进行请求检测。 缓存穿透很大一部分原因是有大量的恶意请求访问不存在的数据,所以对业务系统接收到的请求进行合法性检测,把恶意的请求直接过滤掉,不让它们访问后端缓存和数据库。

跟缓存雪崩、缓存击穿这两类问题相比,缓存穿透的影响更大一些。从预防的角度来说,我们需要避免误删除数据库和缓存中的数据;从应对角度来说,我们可以在业务系统中使用缓存空值或缺省值、使用布隆过滤器,以及进行恶意请求检测等方法。

扩展

布隆过滤器

定义

每布隆过滤器(Bloom Filter)是一个很长的二进制向量数组和一系列随机映射函数,用于检索一个元素是否在一个集合中

优缺点

优点:

  1. 时间复杂度低,增加和查询元素的时间复杂度为O(N)(N为hash函数个数,通常情况下比较小)
  2. 保密性强,因为布隆过滤器不存储元素本身。
  3. 存储空间小。

缺点:

  1. 有一定的误判率,但是可以通过调整参数来降低
  2. 无法获取元素本身
  3. 很难删除元素

工作原理

布隆过滤器的工作原理就是首先多个无偏hash函数把元素的hash值计算出来,这些hash值再对二进制数组的长度取模,得到每个hash值在数组中的对应位置,最后把对应位置的值设为1,完成标记。
如果数据不存在,也就是没有用布隆过滤器标记过,则二进制数组对应位置为零。

  • 二进制数组

[Redis] Redis的三大缓存异常原因分析和解决方案
  • hash函数计算元素的hash值,计算后的元素下标比较均匀的映射到位数组中

[Redis] Redis的三大缓存异常原因分析和解决方案

比如增加一个元素。

过程为:

  • 通过k个无偏hash函数计算得到k个hash值
  • 依次取模数组长度,得到数组索引
  • 将计算得到的数组索引下标位置数据修改为1

如图:

[Redis] Redis的三大缓存异常原因分析和解决方案

【这样关于误判其实就很好理解了,hash函数再怎么好也无法完全避免hash冲突,也就是说有可能存在多个元素计算hash值为相同的,取模数组长度后的数组索引也是相同的,这就是误判的原因】

提供一个布隆过滤器在线计算网址

https://krisives.github.io/bloom-calculator/

使用场景

  • 解决Redis缓存穿透问题
  • 做邮件的黑名单过滤
  • 对爬虫网址做过滤,爬过的不在爬
  • 解决新闻推荐过的不再推荐
  • HBase/RocksDB/LevelDB等数据库内置布隆过滤器,用于判断数据是否存在,可以减少数据库的IO请求

Redis集成布隆过滤器

用Redis可以集成布隆过滤器,版本推荐6.x,最低4.x版本,下载安装插件后在redis.config配置文件中加入redisbloom.so文件的地址并重启。(若是redis集群则每个配置文件中都需要加入redisbloom.so文件的地址)
主要指令有:

  • bf.add 添加一个元素
  • bf.exists 判断一个元素是否存在
  • bf.madd 添加多个元素
  • bf.mexists 判断多个元素是否存在

Original: https://www.cnblogs.com/knqiufan/p/16212519.html
Author: knqiufan
Title: [Redis] Redis的三大缓存异常原因分析和解决方案

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/570457/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • Node更丝滑的打开方式

    Node更丝滑的打开方式 1. 使用背景 最近前端的一个项目,使用gulp作为工程化。在运行过程中出现如下错误 gulp[3192]: src\node_contextify.cc…

    Java 2023年6月13日
    069
  • 【JAVA UI】HarmonyOS功能很强大的弹窗XPopup

    ​参考资料 我们也可以参考Android的XPopup的框架去比较学习 api讲解 项目集成XPopup 1.我们需要在项目级的bulid.gradle 添加如下代码 reposi…

    Java 2023年5月29日
    087
  • java中StringBuffer和StringBuilder

    StringBuffer StringBuffer是一个容器 StringBuffer实现了Serializable,说明可以串行化 在父类中 AbstractStringBuil…

    Java 2023年6月6日
    071
  • JAVA中“LIST泛型MAP根据某一个KEY去重

    JAVA中”LIST泛型MAP根据某一个KEY去重 Java中”List泛型Map根据某一个key去重,保留一个数据。利用jdk8stream()流实现去重…

    Java 2023年5月29日
    056
  • spring 拦截器流程 HandlerInterceptor AsyncHandlerInterceptor HandlerInterceptorAdapter

    HandlerInterceptor源码 3种方法: preHandle:拦截于请求刚进入时,进行判断,需要boolean返回值,如果返回true将继续执行,如果返回false,将…

    Java 2023年6月5日
    0119
  • SpringBoot快速入门

    编写业务代码, Controller package com.andan.web.controller; import org.springframework.web.bind.a…

    Java 2023年6月16日
    071
  • 动态规划三种基本背包问题模板

    1.01背包 有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。 第 i 件物品的体积是 vi,价值是 wi。 求解将哪些物品装入背包,可使这些物品的总体积不超过背包容…

    Java 2023年6月16日
    068
  • 【校招VIP】[Java][211][7分]重点展开与java开发有关的项目

    关注【校招VIP】公众号 ,回复【简历 】,添加校招顾问微信,即可获取简历指导! 本份简历是一位21届211硕java同学的简历,简历评分7分。 一、学员简历 二、指导意见 简历版…

    Java 2023年6月5日
    074
  • ToneGenerator Init failed Crash 崩溃

    需求需要在扫码时产生一个短促的提示音, 搜了下像这样实现。测试时发现多次扫码后,会触发程序崩溃问题。 异常如下 代码如下: 一番搜索, 以下为最佳答案, 加上以后,循环测试, 不再…

    Java 2023年6月15日
    053
  • kafka_2.12-2.2.1 集群搭建

    一、zookeeper集群搭建 kafka集群依赖于zookeeper的集群,搭建zookeeper集群的步骤参考我之前写过的,Solr集群搭建详细教程(一)中的第二步 二、下载解…

    Java 2023年6月9日
    070
  • [学习笔记] Java多态

    多态是同一种行为具有多个不同表现形式的能力,同一事件发生在不同的对象上会产生不同结果; 多态的同一个接口,使用不同的实例而执行不同的操作; 多态性是对象的多种表现形式的体现; 多态…

    Java 2023年6月5日
    081
  • 【Java面试手册-基础篇】Java类中的main() 方法可以被继承吗?

    答案是肯定的,也就是说, main() 方法可以被继承。 下面来看看一个具体的例子,首先定义两个类 MainParent 和 MainChild,如下: MainParent.ja…

    Java 2023年6月8日
    071
  • Java并发编程:线程池

    一、为什么使用线程池 使用线程的时候直接就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频…

    Java 2023年6月5日
    080
  • 详解NGINX如何统计网站的PV、UV、独立IP

    http {include    mime.types;default_type application /octet-str…

    Java 2023年5月30日
    069
  • springboot读取jar中resource下的文件

    一、一般的读取resource下的static中的图片方法 方法一:读取非jar中文件 方法二、读取非jar中文件,或者jar中指定文件名路径的文件 比如 resourcePath…

    Java 2023年5月30日
    080
  • 数据结构笔记—第一篇 数据结构概述

    第一篇数据结构概述 1.数据结构概述 什么是数据结构? 简单来说,就是计算机存储,组织数据的方式;它包含三方面的内容,逻辑关系、存储关系及操作。 记住关键字:存储—&#…

    Java 2023年6月16日
    075
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球