redis学习笔记(七):redis常见问题和解决方案

2023-11-19

目录

一、缓存穿透

1、基本介绍

2、解决方案

(1)布隆过滤器

(2)缓存空对象

(3)参数校验

(4)对比

二、缓存击穿

1、基本介绍

2、解决方案

(1)互斥锁

(2)永不过期

(3)两种方案对比

三、缓存雪崩

1、基本介绍

2、解决方案

(1)过期时间打散

(2)热点数据不过期

(3)加互斥锁

(4)高可用

(5)服务降级

(6)双层缓存策略


一、缓存穿透

1、基本介绍

        缓存穿透是指查询一个根本不存在的数据,缓存层和持久层都不会命中。在日常工作中出于容错的考虑,如果从持久层查不到数据则不写入缓存层,缓存穿透将导致不存在的数据每次请求都要到持久层去查询,失去了缓存保护后端持久的意义。

        正常情况下,当用户请求过来的时候,先查询缓存,如果缓存中存在数据则直接返回;如果缓存中不存在数据,则去查询数据库。当数据库中存在数据时,会将数据放入缓存然后返回;如果数据库中不存在数据,则直接返回空。   

         但是如果用户请求的ID在缓存中不存在或者恶意用户伪造不存在的ID发起请求,那么就会导致每次从缓存中都查不到数据,需要去查询数据库,且数据库也无法查询到数据,也不能放入缓存。因此导致缓存失去了作用,如同被穿透一般,每次都要访问数据库,当流量大的时候会导致数据库崩溃。因此,redis中如果同一个key的缓存命中率很低,有可能就是出现了缓存穿透的问题。

2、解决方案

(1)布隆过滤器

        布隆过滤器的特点是判断不存在的,则一定不存在;判断存在的,大概率存在,但也有小概率不存在。并且这个概率是可控的,我们可以让这个概率变小或者变高,取决于用户本身的需求。

        布隆过滤器由一个 bitSet 和 一组 Hash 函数(算法)组成,是一种空间效率极高的概率型算法和数据结构,主要用来判断一个元素是否在集合中存在。

        在初始化时,bitSet 的每一位被初始化为0,同时会定义 Hash 函数,例如有3组 Hash 函数:hash1、hash2、hash3。

布隆过滤器的写入流程

当我们要写入一个值时,过程如下,以“Python”为例:

1)首先将“Python”跟3组 Hash 函数分别计算,得到 bitSet 的下标为:1、7、10。

2)将 bitSet 的这3个下标标记为1。

假设我们还有另外两个值:java 和 C++,按上面的流程跟 3组 Hash 函数分别计算,结果如下:

java:Hash 函数计算 bitSet 下标为:1、7、11

c++:Hash 函数计算 bitSet 下标为:4、10、11

布隆过滤器的查询流程

当我们要查询一个值时,过程如下,同样以“Python”为例::

1)首先将“Python”跟3组 Hash 函数分别计算,得到 bitSet 的下标为:1、7、10。

2)查看 bitSet 的这3个下标是否都为1,如果这3个下标不都为1,则说明该值必然不存在,如果这3个下标都为1,则只能说明可能存在,并不能说明一定存在。

其实上图的例子已经说明了这个问题了,当我们只有值“Python”和“C++”时,bitSet 下标为1的有:1、4、7、10、11。

当我们又加入值“java”时,bitSet 下标为1的还是这5个,所以当 bitSet 下标为1的为:1、4、7、10、11 时,我们无法判断值“java”存不存在。

其根本原因是,不同的值在跟 Hash 函数计算后,可能会得到相同的下标,所以某个值的标记位,可能会被其他值给标上了。

这也是为啥布隆过滤器只能判断某个值可能存在,无法判断必然存在的原因。但是反过来,如果该值根据 Hash 函数计算的标记位没有全部都为1,那么则说明必然不存在,这个是肯定的。
降低这种误判率的思路也比较简单:

1)一个是加大 bitSet 的长度,这样不同的值出现“冲突”的概率就降低了,从而误判率也降低。

2)提升 Hash 函数的个数,Hash 函数越多,每个值对应的 bit 越多,从而误判率也降低。

总结一下:布隆过滤器认为不在的,一定不会在集合中;布隆过滤器认为在的,可能在也可能不在集合中。

(2)缓存空对象

        布隆过滤器可以过滤掉很多不存在的用户id请求。但它会带来两个问题:

  • 布隆过滤器存在误杀的情况,可能会把少部分正常用户的请求也过滤了。
  • 如果用户信息有变化,需要实时同步到布隆过滤器,不然会有问题。

        当缓存未命中,查询持久层也为空,可以将返回的空对象写到缓存中,这样下次请求该key时直接从缓存中查询返回空对象,请求不会落到持久层数据库。为了避免存储过多空对象,通常会给空对象设置一个过期时间。

这种方法会存在两个问题:

  • 如果有大量的key穿透,缓存空对象会占用宝贵的内存空间。
  • 空对象的key设置了过期时间,在这段时间可能会存在缓存和持久层数据不一致的场景。

(3)参数校验

        对用户id做检验,比如合法id是15xxxxxx,以15开头的。如果用户传入了16开头的id,比如:16232323,则参数校验失败,直接把相关请求拦截掉。这样可以过滤掉一部分恶意伪造的用户id。

(4)对比

解决方案 适用场景 维护成本
缓存空对象

数据命中率不高;

数据频繁变化,实时性高

代码维护简单;

需要过多的缓存空间;

数据不一致

布隆过滤器

数据命中率不高;

数据相对固定,实时性低

代码维护复杂;

缓存空间占用少

二、缓存击穿

1、基本介绍

        缓存击穿,是指一个key非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,就像在一个屏障上凿开了一个洞。

        例如我们再商城购买某个热门的商品,一般情况下为了保证访问速度,商城系统会把商品信息放到缓存中,而该商品到了过期时间就会失效。此时如何大量用户去请求同一个商品,但是这个商品却已经在缓存中失效了,这样就会使得大量请求直接冲击数据库,就可能造成瞬间数据库的压力骤增而直接挂掉。

2、解决方案

(1)互斥锁

只允许一个线程重建缓存,其他线程等待重建缓存的线程执行完,重新从缓存获取数据即可。

(2)永不过期

永不过期包含两层意思:

  •  从缓存层面来看,确实没有设置过期时间,所以不会出现热点key过期后产生的问题,也就是“物理”不过期。
  • 从功能层面来看,为每个value设置一个逻辑过期时间,当发现超过逻辑过期时间后,会使用单独的线程去更新缓存。

(3)两种方案对比

  •  分布式互斥锁:这种方案思路比较简单,但是存在一定的隐患,如果在查询数据库 + 和 重建缓存(key失效后进行了大量的计算)时间过长,也可能会存在死锁和线程池阻塞的风险,高并发情景下吞吐量会大大降低!但是这种方法能够较好地降低后端存储负载,并在一致性上做得比较好。
  • 永远不过期:这种方案由于没有设置真正的过期时间,实际上已经不存在热点key产生的一系列危害,但是会存在数据不一致的情况,同时代码复杂度会增大。

三、缓存雪崩

1、基本介绍

        缓存雪崩是指缓存中数据大批量到过期时间,而查询数据量巨大,请求直接落到数据库上,引起数据库压力过大甚至宕机。和缓存击穿不同的是,缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库。

缓存雪崩目前有两种:

  • 有大量的热门缓存,同时失效。会导致大量的请求,访问数据库。而数据库很有可能因为扛不住压力,而直接挂掉。
  • 缓存服务器宕机了,可能是机器硬件问题,或者机房网络问题。总之,造成了整个缓存的不可用。

2、解决方案

(1)过期时间打散

        既然是大量缓存集中失效,那最容易想到就是让他们不集中生效。可以给缓存的过期时间时加上一个随机值时间,使得每个 key 的过期时间分布开来,不会集中在同一时刻失效。这样即使在高并发的情况下,多个请求同时设置过期时间,由于有随机数的存在,也不会出现太多相同的过期key。

(2)热点数据不过期

        该方式和缓存击穿一样,也是要着重考虑刷新的时间间隔和数据异常如何处理的情况。

(3)加互斥锁

        该方式和缓存击穿一样,按 key 维度加锁,对于同一个 key,只允许一个线程去计算,其他线程原地阻塞等待第一个线程的计算结果,然后直接走缓存即可。

(4)高可用

        针对缓存服务器宕机的情况,在前期做系统设计时,可以做一些高可用架构。比如:如果使用了redis,可以使用哨兵模式,或者集群模式,避免出现单节点故障导致整个redis服务不可用的情况。

        使用哨兵模式之后,当某个master服务下线时,自动将该master下的某个slave服务升级为master服务,替代已下线的master服务继续处理请求。

(5)服务降级

        缓存降级是指缓存失效或缓存服务器挂掉的情况下,不去访问数据库,直接返回默认数据或访问服务的内存数据。在项目实战中通常会将部分热点数据缓存到服务的内存中,这样一旦缓存出现异常,可以直接使用服务的内存数据,从而避免数据库遭受巨大压力。降级一般是有损的操作,所以尽量减少降级对于业务的影响程度。

(6)双层缓存策略

        主缓存:有效期按照经验值设置,设置为主读取的缓存,主缓存失效后从数据库加载最新值。

        备份缓存:有效期长,获取锁失败时读取的缓存,主缓存更新时需要同步更新备份缓存。

        缓存预热什么是缓存预热?

        缓存预热就是系统上线后,将相关的缓存数据直接加载到缓存系统,这样就可以避免在用户请求的时候,先查询数据库,然后再将数据回写到缓存。

        如果不进行预热, 那么 Redis 初始状态数据为空,系统上线初期,对于高并发的流量,都会访问到数据库中, 对数据库造成流量的压力。

缓存预热的操作方法

  • 数据量不大的时候,工程启动的时候进行加载缓存动作;
  • 数据量大的时候,设置一个定时任务脚本,进行缓存的刷新;
  • 数据量太大的时候,优先保证热点数据进行提前加载到缓存。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

redis学习笔记(七):redis常见问题和解决方案 的相关文章

随机推荐

  • 如何在Linux系统上监测系统温度?(亲测可用)

    转载自 http os 51cto com art 201311 417208 htm 按理说 在大多数情况下 你用不着为电脑的运行温度而操心 除了制造瑕疵外 电脑硬件在设计时确保温度不会超过最高工作温度 但即使没有任何的硬件故障 也会由于
  • QT6源码编译全过程

    QT6源码编译全过程 windows环境下使用VS2019 QT6源码编译全过程 windows环境下使用VS2019 qt6 编译 刘亿辰的博客 CSDN博客 一 随记 QT作为一个跨平台的界面开发平台 经过了历史长河的洗礼以及一代代Qt
  • 【Windows系统】资源管理器右键卡顿案例

    问题 最近在使用办公电脑过程中 发现在Windows系统资源管理器中使用右键会出现卡顿现象 这是一台经常使用 工作日上班都会使用 以前没有这个问题 出现问题的环境 windows版本 win10 x64 22H2 解决方法 1 关闭映射到本
  • VUE之Echarts地图引入及配置项详解

    步骤 建立dom用于渲染地图组件 div style width 100 height 100 class map charts div 引入所需js文件 import echarts from echarts require echart
  • Unity中在场景中添加水资源效果的方法

    首先 导入系统自带的水资源包 鼠标右击Project视图中的Assets文件夹 在弹出来的列表中选择 Import Package gt Water Basic 然后 弹出一个 Importing package 窗口 自由选择其中的包 全
  • Python操作数据库

    pymysql模块简单实用 1 安装pymysql模块 pip3 install PyMySQL 2 建立连接 与MySQL服务端建立连接 conn pymysql connect host 127 0 0 1 ip port 3306 端
  • 因果模型五:用因果的思想优化风控模型——因果正则化评分卡模型

    因果模型五 用因果的思想优化风控模型 因果正则化评分卡模型 一 模型中的因果和相关 二 不可知样本选择偏差 三 因果推断 四 因果与评分卡的融合 五 模型效果评估 5 1 人工合成数据效果测试 5 2 YFCC100M图像数据测试 5 3
  • 高德地图打点获取点的坐标和名称

    一 首先引入地图 代码在最后 二 然后是打点获取坐标和名称的方法 代码在最后 重点 如果不加上密钥的话可能会得不到数据名称 你想要的代码来了 密钥 window AMapSecurityConfig securityJsCode 你申请ke
  • GraphPad Prism 9.2 Mac 2021最新安装使用教程

    GraphPad Prism集生物统计 化学统计 以及科技绘图于一身 其中医学所能用到的绘图需要它几乎都能满足 Prism 现在被各种生物学家以及社会和物理科学家广泛使用 超过110个国家的超过20万名科学家依靠 Prism 来分析 绘制和
  • SpringBoot学习遇到的问题(1) - 配置文件有日志的debug模式等配置项,为什么不起作用...

    这个问题困扰我近乎两天 通过查找N多资料后终于解决 写下来共享给大家 logging level root DEBUG 一系列的日志配置项 都不起作用的原因是springboot启动加载不到src main resources下的配置文件a
  • 那些踩过的declared implicitly的坑

    缺少头文件 我的本意是想做串口打印进行调试 于是我在usart c中重写了这两个函数 这里顺便记录下如何串口打印 usart c中 int fputc int ch FILE f HAL UART Transmit huart1 uint8
  • 2023计算机四非保研(复试:东北大学,成电,西电,浙软,中海洋,天大)

    文章目录 个人情况 夏令营情况 预推免情况 进入复试 中国海洋大学 学硕 浙大软院 专硕 天津大学智算 专硕 中科院网络中心 专硕 西电网安院 学硕 东北大学计算机 学硕 成电计算机 专硕 最终offer 感想 个人情况 本科学校 西北某四
  • firmware-mod-kit工具安装和使用说明

    一 firmware mod kit工具的安装 firmware mod kit工具的功能和binwalk工具的类似 其实firmware mod kit工具在功能上有调用binwalk工具提供的功能以及其他的固件解包工具的整合 下载fir
  • pp-human在rk3588上部署

    https github com leeguandong Yolov5 rknnlite2https github com leeguandong Yolov5 rknnlite2 这是我在paddledetection和rknn官方基础上
  • Jmeter使用JDBC对数据库压测

    背景说明 压测除了全链路压测外 有时候也需要对指定服务进行性能测试 这里以jmeter工具对数据库进行压测说明 压测不同数据库需要安装不同的数据库驱动 这里以mysql为例进行压测 步骤一 数据库驱动安装 1 进入mysql官网 根据不同m
  • 课程设计心得_关于switch输入字母进入死循环问题

    做C语言课程设计时 采用了大量的switch 在后期找bug时 当输入字符类型时 如a 之类的 程序进入了死循环 但又不想换成其他的 主要是懒 不想大量改动 void menu windows int n system cls fflush
  • Python3 字典

    字典是另一种可变容器模型 且可存储任意类型对象 字典的每个键值 key gt value 对用冒号 分割 每个对之间用逗号 分割 整个字典包括在花括号 中 格式如下所示 注意 dict 作为 Python 的关键字和内置函数 变量名不建议命
  • 安全运维之Resin应用服务器中间件安装使用与安全配置

    本章目录 0x00 快速入门 0x01 Resin安装 0x02 Resin配置文件 0x03 Resin应用 0x04 Security 0x05 Help 附录补充 1 Resin 日志记录之format配置详解 原文地址 https
  • 登入服务器bmc_如何使用 BMCTool 远程管理 PowerEdge C 系列服务器

    文章内容 症状 本文介绍了如何使用 BMCTool 管理 PowerEdge C 系列服务器 BMC 工具旨在压缩和改进 IPMITool 的功能 可从poweredgec dell com下载 需要安装 OpenIPMI 和 IPMITo
  • redis学习笔记(七):redis常见问题和解决方案

    目录 一 缓存穿透 1 基本介绍 2 解决方案 1 布隆过滤器 2 缓存空对象 3 参数校验 4 对比 二 缓存击穿 1 基本介绍 2 解决方案 1 互斥锁 2 永不过期 3 两种方案对比 三 缓存雪崩 1 基本介绍 2 解决方案 1 过期