Redis 缓存击穿

目录

缓存击穿

什么是缓存击穿?

有哪些解决办法?

缓存穿透和缓存击穿有什么区别?

缓存雪崩

什么是缓存雪崩?

有哪些解决办法?

缓存预热如何实现?

缓存雪崩和缓存击穿有什么区别?

如何保证缓存和数据库数据的一致性?

哪些情况可能会导致 Redis 阻塞?

O(n) 命令

SAVE 创建 RDB 快照

AOF

AOF 日志记录阻塞

AOF 刷盘阻塞

AOF 重写阻塞

大 Key

查找大 key

删除大 key

清空数据库

集群扩容

Swap(内存交换)

CPU 竞争

网络问题


缓存击穿

什么是缓存击穿?

缓存击穿中,请求的 key 对应的是 热点数据 ,该数据 存在于数据库中,但不存在于缓存中(通常是因为缓存中的那份数据已经过期) 。这就可能会导致瞬时大量的请求直接打到了数据库上,对数据库造成了巨大的压力,可能直接就被这么多请求弄宕机了。


                                

举个例子:秒杀进行过程中,缓存中的某个秒杀商品的数据突然过期,这就导致瞬时大量对该商品的请求直接落到数据库上,对数据库造成了巨大的压力。

有哪些解决办法?

  1. 永不过期(不推荐):设置热点数据永不过期或者过期时间比较长。
  2. 提前预热(推荐):针对热点数据提前预热,将其存入缓存中并设置合理的过期时间比如秒杀场景下的数据在秒杀结束之前不过期。
  3. 加锁(看情况):在缓存失效后,通过设置互斥锁确保只有一个请求去查询数据库并更新缓存。

缓存穿透和缓存击穿有什么区别?

缓存穿透中,请求的 key 既不存在于缓存中,也不存在于数据库中。

缓存击穿中,请求的 key 对应的是 热点数据 ,该数据 存在于数据库中,但不存在于缓存中(通常是因为缓存中的那份数据已经过期)

缓存雪崩

什么是缓存雪崩?

我发现缓存雪崩这名字起的有点意思,哈哈。

实际上,缓存雪崩描述的就是这样一个简单的场景:缓存在同一时间大面积的失效,导致大量的请求都直接落到了数据库上,对数据库造成了巨大的压力。 这就好比雪崩一样,摧枯拉朽之势,数据库的压力可想而知,可能直接就被这么多请求弄宕机了。

另外,缓存服务宕机也会导致缓存雪崩现象,导致所有的请求都落到了数据库上。

                        ​​​​​​​        

举个例子:数据库中的大量数据在同一时间过期,这个时候突然有大量的请求需要访问这些过期的数据。这就导致大量的请求直接落到数据库上,对数据库造成了巨大的压力。

有哪些解决办法?

针对 Redis 服务不可用的情况:

  1. Redis 集群:采用 Redis 集群,避免单机出现问题整个缓存服务都没办法使用。Redis Cluster 和 Redis Sentinel 是两种最常用的 Redis 集群实现方案,详细介绍可以参考:Redis 集群详解(付费)。
  2. 多级缓存:设置多级缓存,例如本地缓存+Redis 缓存的二级缓存组合,当 Redis 缓存出现问题时,还可以从本地缓存中获取到部分数据。

针对大量缓存同时失效的情况:

  1. 设置随机失效时间(可选):为缓存设置随机的失效时间,例如在固定过期时间的基础上加上一个随机值,这样可以避免大量缓存同时到期,从而减少缓存雪崩的风险。
  2. 提前预热(推荐):针对热点数据提前预热,将其存入缓存中并设置合理的过期时间比如秒杀场景下的数据在秒杀结束之前不过期。
  3. 持久缓存策略(看情况):虽然一般不推荐设置缓存永不过期,但对于某些关键性和变化不频繁的数据,可以考虑这种策略。

 

缓存预热如何实现?

常见的缓存预热方式有两种:

  1. 使用定时任务,比如 xxl-job,来定时触发缓存预热的逻辑,将数据库中的热点数据查询出来并存入缓存中。
  2. 使用消息队列,比如 Kafka,来异步地进行缓存预热,将数据库中的热点数据的主键或者 ID 发送到消息队列中,然后由缓存服务消费消息队列中的数据,根据主键或者 ID 查询数据库并更新缓存。

 

缓存雪崩和缓存击穿有什么区别?

缓存雪崩和缓存击穿比较像,但缓存雪崩导致的原因是缓存中的大量或者所有数据失效,缓存击穿导致的原因主要是某个热点数据不存在与缓存中(通常是因为缓存中的那份数据已经过期)。

如何保证缓存和数据库数据的一致性?

细说的话可以扯很多,但是我觉得其实没太大必要(小声 BB:很多解决方案我也没太弄明白)。我个人觉得引入缓存之后,如果为了短时间的不一致性问题,选择让系统设计变得更加复杂的话,完全没必要。

下面单独对 Cache Aside Pattern(旁路缓存模式) 来聊聊。


Cache Aside Pattern 中遇到写请求是这样的:更新数据库,然后直接删除缓存 。

如果更新数据库成功,而删除缓存这一步失败的情况的话,简单说有两个解决方案:

  1. 缓存失效时间变短(不推荐,治标不治本):我们让缓存数据的过期时间变短,这样的话缓存就会从数据库中加载数据。另外,这种解决办法对于先操作缓存后操作数据库的场景不适用。
  2. 增加缓存更新重试机制(常用):如果缓存服务当前不可用导致缓存删除失败的话,我们就隔一段时间进行重试,重试次数可以自己定。不过,这里更适合引入消息队列实现异步重试,将删除缓存重试的消息投递到消息队列,然后由专门的消费者来重试,直到成功。虽然说多引入了一个消息队列,但其整体带来的收益还是要更高一些。

相关文章推荐:缓存和数据库一致性问题,看这篇就够了 - 水滴与银弹。

哪些情况可能会导致 Redis 阻塞?

​​​​​​​O(n) 命令

Redis 中的大部分命令都是 O(1)时间复杂度,但也有少部分 O(n) 时间复杂度的命令,例如:

  • KEYS *:会返回所有符合规则的 key。
  • HGETALL:会返回一个 Hash 中所有的键值对。
  • LRANGE:会返回 List 中指定范围内的元素。
  • SMEMBERS:返回 Set 中的所有元素。
  • SINTER/SUNION/SDIFF:计算多个 Set 的交集/并集/差集。
  • ……

由于这些命令时间复杂度是 O(n),有时候也会全表扫描,随着 n 的增大,执行耗时也会越长,从而导致客户端阻塞。不过, 这些命令并不是一定不能使用,但是需要明确 N 的值。另外,有遍历的需求可以使用 HSCANSSCANZSCAN 代替。

除了这些 O(n)时间复杂度的命令可能会导致阻塞之外, 还有一些时间复杂度可能在 O(N) 以上的命令,例如:

  • ZRANGE/ZREVRANGE:返回指定 Sorted Set 中指定排名范围内的所有元素。时间复杂度为 O(log(n)+m),n 为所有元素的数量, m 为返回的元素数量,当 m 和 n 相当大时,O(n) 的时间复杂度更小。
  • ZREMRANGEBYRANK/ZREMRANGEBYSCORE:移除 Sorted Set 中指定排名范围/指定 score 范围内的所有元素。时间复杂度为 O(log(n)+m),n 为所有元素的数量, m 被删除元素的数量,当 m 和 n 相当大时,O(n) 的时间复杂度更小。

SAVE 创建 RDB 快照

Redis 提供了两个命令来生成 RDB 快照文件:

  • save : 同步保存操作,会阻塞 Redis 主线程;
  • bgsave : fork 出一个子进程,子进程执行,不会阻塞 Redis 主线程,默认选项。

默认情况下,Redis 默认配置会使用 bgsave 命令。如果手动使用 save 命令生成 RDB 快照文件的话,就会阻塞主线程。


AOF

AOF 日志记录阻塞

Redis AOF 持久化机制是在执行完命令之后再记录日志,这和关系型数据库(如 MySQL)通常都是执行命令之前记录日志(方便故障恢复)不同。

                         

                                        AOF 记录日志过程

为什么是在执行完命令之后记录日志呢?

  • 避免额外的检查开销,AOF 记录日志不会对命令进行语法检查;
  • 在命令执行完之后再记录,不会阻塞当前的命令执行。

这样也带来了风险(我在前面介绍 AOF 持久化的时候也提到过):

  • 如果刚执行完命令 Redis 就宕机会导致对应的修改丢失;
  • 可能会阻塞后续其他命令的执行(AOF 记录日志是在 Redis 主线程中进行的)

AOF 刷盘阻塞

开启 AOF 持久化后每执行一条会更改 Redis 中的数据的命令,Redis 就会将该命令写入到 AOF 缓冲区 server.aof_buf 中,然后再根据 appendfsync 配置来决定何时将其同步到硬盘中的 AOF 文件。

在 Redis 的配置文件中存在三种不同的 AOF 持久化方式( fsync策略),它们分别是:

  1. appendfsync always:主线程调用 write 执行写操作后,后台线程( aof_fsync 线程)立即会调用 fsync 函数同步 AOF 文件(刷盘),fsync 完成后线程返回,这样会严重降低 Redis 的性能(write + fsync)。
  2. appendfsync everysec:主线程调用 write 执行写操作后立即返回,由后台线程( aof_fsync 线程)每秒钟调用 fsync 函数(系统调用)同步一次 AOF 文件(write+fsyncfsync间隔为 1 秒)
  3. appendfsync no:主线程调用 write 执行写操作后立即返回,让操作系统决定何时进行同步,Linux 下一般为 30 秒一次(write但不fsyncfsync 的时机由操作系统决定)。

当后台线程( aof_fsync 线程)调用 fsync 函数同步 AOF 文件时,需要等待,直到写入完成。当磁盘压力太大的时候,会导致 fsync 操作发生阻塞,主线程调用 write 函数时也会被阻塞。fsync 完成后,主线程执行 write 才能成功返回。

关于 AOF 工作流程的详细介绍可以查看:Redis 持久化机制详解,有助于理解 AOF 刷盘阻塞

 

AOF 重写阻塞

  1. fork 出一条子线程来将文件重写,在执行 BGREWRITEAOF 命令时,Redis 服务器会维护一个 AOF 重写缓冲区,该缓冲区会在子线程创建新 AOF 文件期间,记录服务器执行的所有写命令。
  2. 当子线程完成创建新 AOF 文件的工作之后,服务器会将重写缓冲区中的所有内容追加到新 AOF 文件的末尾,使得新的 AOF 文件保存的数据库状态与现有的数据库状态一致。
  3. 最后,服务器用新的 AOF 文件替换旧的 AOF 文件,以此来完成 AOF 文件重写操作。

阻塞就是出现在第 2 步的过程中,将缓冲区中新数据写到新文件的过程中会产生阻塞


大 Key

如果一个 key 对应的 value 所占用的内存比较大,那这个 key 就可以看作是 bigkey。具体多大才算大呢?有一个不是特别精确的参考标准:

  • string 类型的 value 超过 1MB
  • 复合类型(列表、哈希、集合、有序集合等)的 value 包含的元素超过 5000 个(对于复合类型的 value 来说,不一定包含的元素越多,占用的内存就越多)。

大 key 造成的阻塞问题如下:

  • 客户端超时阻塞:由于 Redis 执行命令是单线程处理,然后在操作大 key 时会比较耗时,那么就会阻塞 Redis,从客户端这一视角看,就是很久很久都没有响应。
  • 引发网络阻塞:每次获取大 key 产生的网络流量较大,如果一个 key 的大小是 1 MB,每秒访问量为 1000,那么每秒会产生 1000MB 的流量,这对于普通千兆网卡的服务器来说是灾难性的。
  • 阻塞工作线程:如果使用 del 删除大 key 时,会阻塞工作线程,这样就没办法处理后续的命令

 

查找大 key

当我们在使用 Redis 自带的 --bigkeys 参数查找大 key 时,最好选择在从节点上执行该命令,因为主节点上执行时,会阻塞主节点。

  • 我们还可以使用 SCAN 命令来查找大 key;

  • 通过分析 RDB 文件来找出 big key,这种方案的前提是 Redis 采用的是 RDB 持久化。网上有现成的工具:

    • redis-rdb-tools:Python 语言写的用来分析 Redis 的 RDB 快照文件用的工具
    • rdb_bigkeys:Go 语言写的用来分析 Redis 的 RDB 快照文件用的工具,性能更好。

删除大 key


删除操作的本质是要释放键值对占用的内存空间。

释放内存只是第一步,为了更加高效地管理内存空间,在应用程序释放内存时,操作系统需要把释放掉的内存块插入一个空闲内存块的链表,以便后续进行管理和再分配。这个过程本身需要一定时间,而且会阻塞当前释放内存的应用程序。

所以,如果一下子释放了大量内存,空闲内存块链表操作时间就会增加,相应地就会造成 Redis 主线程的阻塞,如果主线程发生了阻塞,其他所有请求可能都会超时,超时越来越多,会造成 Redis 连接耗尽,产生各种异常。

删除大 key 时建议采用分批次删除和异步删除的方式进行。

清空数据库

清空数据库和上面 bigkey 删除也是同样道理,flushdbflushall 也涉及到删除和释放所有的键值对,也是 Redis 的阻塞点。

集群扩容


Redis 集群可以进行节点的动态扩容缩容,这一过程目前还处于半自动状态,需要人工介入。

在扩缩容的时候,需要进行数据迁移。而 Redis 为了保证迁移的一致性,迁移所有操作都是同步操作。

执行迁移时,两端的 Redis 均会进入时长不等的阻塞状态,对于小 Key,该时间可以忽略不计,但如果一旦 Key 的内存使用过大,严重的时候会触发集群内的故障转移,造成不必要的切换。

Swap(内存交换)

什么是 Swap? Swap 直译过来是交换的意思,Linux 中的 Swap 常被称为内存交换或者交换分区。类似于 Windows 中的虚拟内存,就是当内存不足的时候,把一部分硬盘空间虚拟成内存使用,从而解决内存容量不足的情况。因此,Swap 分区的作用就是牺牲硬盘,增加内存,解决 VPS 内存不够用或者爆满的问题。

Swap 对于 Redis 来说是非常致命的,Redis 保证高性能的一个重要前提是所有的数据在内存中。如果操作系统把 Redis 使用的部分内存换出硬盘,由于内存与硬盘的读写速度差几个数量级,会导致发生交换后的 Redis 性能急剧下降。

 

1、查询 Redis 进程号

redis-cli -p 6383 info server | grep process_id
process_id: 4476

2、根据进程号查询内存交换信息

cat /proc/4476/smaps | grep Swap
Swap: 0kB
Swap: 0kB
Swap: 4kB
Swap: 0kB
Swap: 0kB
.....

如果交换量都是 0KB 或者个别的是 4KB,则正常。

预防内存交换的方法:

 

  • 保证机器充足的可用内存
  • 确保所有 Redis 实例设置最大可用内存(maxmemory),防止极端情况 Redis 内存不可控的增长
  • 降低系统使用 swap 优先级,如echo 10 > /proc/sys/vm/swappiness

CPU 竞争

Redis 是典型的 CPU 密集型应用,不建议和其他多核 CPU 密集型服务部署在一起。当其他进程过度消耗 CPU 时,将严重影响 Redis 的吞吐量。

可以通过redis-cli --stat获取当前 Redis 使用情况。通过top命令获取进程对 CPU 的利用率等信息 通过info commandstats统计信息分析出命令不合理开销时间,查看是否是因为高算法复杂度或者过度的内存优化问题。

网络问题

连接拒绝、网络延迟,网卡软中断等网络问题也可能会导致 Redis 阻塞

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

【Redis的安装以及主从复制的搭建】配置Redis的哨兵模式

文章目录 一、安装Redis1、上传、解压、重命名2、安装GCC环境3、编译源码4、进行安装5、修改配置文件redis.conf 二、Redis主从复制搭建1、创建文件夹用来实现主从复制 三、配置Redis的哨兵模式1、创建文件夹2、修改sentinel.conf配置文件3、启动 一、安装Redis 1、上传、解压…

WPF中的转换器

单值转换器 1.创建项目后下载两个NuGet程序包 2.先定义一个转换器实现IValueConverter接口 using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; usin…

GEE Ui——批量查询Sentinel-2 图像无云区域的可视化应用(提供缩略图)

目录 简介 功能选项 函数 Map.clear() No arguments. Returns: ui.Map Map.onClick(callback) Arguments: Returns: String reduceRegion(reducer, geometry, scale, crs, crsTransform, bestEffort, maxPixels, tileScale) Arguments: Returns: Dictionary toLis…

汇编练习-1

1、要求 练习要求引自《汇编语言-第4版》实验10.3(P209页) -编程,将data段中的数据,以10进制的形式显示出来 data segment dw 123,12666,1,8,3,38 data ends 2、实现代码(可惜没找到csdn对8086汇编显示方式) assume cs:codedata segmentdw 16 dup(0) ;除…

xml格式转为txt格式?

数据集的标签格式为xml格式,转为yolo的训练格式: 1.创建一个格式转化的.py文件将下面代码复制: import os import glob import xml.etree.ElementTree as ETdef get_classes(classes_path):with open(classes_path, encodingutf-8) as f:cl…

M - Weird Ceiling 【CCPC2024哈尔滨站】

M - Weird Ceiling 思路: 注意到 f ( n , i ) f(n,i) f(n,i) 的值为 n y ( i ) \frac{n} {y(i)} y(i)n​ ,其中 y ( i ) y(i) y(i) 为 n n n 小于等于 i i i 的最大因数。 那么先找到 n n n的所有因数,包括 1 1 1和它本身,在数组a[]中升…

机器学习与数学公式

目录 在机器学习中,将公式应用到算法程序上主要涉及以下几个步骤: 1、数学公式转换成编程逻辑: 2、选择合适的编程语言和工具: 3、使用矩阵运算和优化方法: 4、实现算法逻辑: 5、将公式封装成函数…

基于微信小程序的校园失物招领系统的研究与实现(V4.0)

博主介绍:✌stormjun、8年大厂程序员经历。全网粉丝15w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 🍅文末获取源码联系🍅 👇🏻 精彩专栏推荐订阅👇&…

芯突破 | 国产PCIe桥接芯片掀起的连接革命

PCIe桥接芯片应运而生 随着计算机技术的快速发展,硬件性能不断提升,数据传输速度的需求日益增长,PCIe总线凭借其高带宽和低延迟的优势,已经成为主流。然而,由于不同设备和PCIe总线之间的接口差异,直接连接…

6款高效网页界面原型设计软件:提升设计效率的利器

在网页设计领域,原型设计是将创意转化为实际产品的关键环节。一款优秀的网页界面原型设计软件能够帮助设计师快速、准确地呈现设计思路,优化用户体验,并提高团队协作效率。以下是6款在网页界面原型设计方面表现出色的软件。 1. 即时设计 即…

计算机网络综合题

IP数据报的划分 CRC差错检测 冗余码的计算 因此,余数是1110,传输的数为11010110111110。在传输过程中最后两位变成o,接收端能够发现,因为11010110111110除以10011余数不为0。 子网划分 暴力求解法 (定长子网划分大量…

对比JavaScript、C、Python在声明变量后未初始化处理上的差异与深度解析

文章目录 💯前言💯三者声明变量后未初始化的不同默认行为JavaScriptC语言Python 💯JavaScript中的变量管理作用域与变量声明Hoisting(变量提升)var的思考与缺陷 💯C语言中的变量管理内存模型概述变量的作用…

Day105:代码审计-PHP原生开发篇SQL注入数据库监控正则搜索文件定位静态分析

Day105:代码审计-PHP原生开发篇&SQL注入&数据库监控&正则搜索&文件定位&静态分析_php代码审计实战-CSDN博客 知识点: 1、PHP审计-原生态开发-SQL注入&语句监控 2、PHP审计-原生态开发-SQL注入&正则搜索 3、PHP审计-原生态…

零基础‘自外网到内网’渗透过程详细记录(cc123靶场)——上

一、网络环境示意图 二、环境搭建 首先将三个虚拟机使用VMware打开。 接下来对虚拟机进行配置。 首先配置虚拟机“护卫神主机大师(项目四)”。 点击编辑虚拟机设置。 发现存在两个网卡。 打开虚拟网络编辑器。 点击更改设置。 点击添加网络。 选择VM19后点击确定。 根据网络…

架构师必修之项目篇:基于ASR+GPT4.0+TTS实现全双工智能语音助手

1. 系统架构设计 1.1 ASR模块设计 ASR(Automatic Speech Recognition)模块是全双工智能语音助手的前端入口,负责将用户的语音输入转换为文本数据。该模块的设计关键在于高准确率的语音识别和快速响应。 语音信号预处理:首先对采集…

为什么说PPQ对于FPGA的神经网络的量化来说是神?

这几天踩了无数坑把PPQ走通了,把自己搭的一个网络实现了完美量化,测试结果也可以正常分类。看到结果终于明白为什么说PPQ对于FPGA的神经网络的量化来说是神了 虔诚地放出链接: OpenPPL/ppq PS:这辈子要是能去商汤就好了…… 一、 …

VMWareTools安装及文件无法拖拽解决方案

文章目录 1 安装VMWare Tools2 安装vmware tools之后还是无法拖拽文件解决方案2.1 确认vmware tools安装2.2 客户机隔离2.3 修改自定义配置文件2.4 安装open-vm-tools-desktop软件 1 安装VMWare Tools 打开虚拟机VMware Workstation,启动Ubuntu系统,菜单…

【TabBar嵌套Navigation案例-常见问题按钮-WebView-加载本地html文件 Objective-C语言】

一、接下来,我们来说,webView如何加载本地的html文件 1.把这里的http://www.baidu.com/ 如何替换成本地的html文件,实际上,我们只需要把URL替换一下就可以了, 然后呢,先给大家看一眼素材,在我们的help.json里边,比如说,第一个按钮, 如何领奖,这块儿有一个叫做htm…

pwn:[NISACTF 2022]ReorPwn?

题目 解题 附件下载 在kali里打开 执行checksec命令,查看控制程序的命令 64位 用ida64位版本打开 打开页面显示如下 发现关键函数system() 双击system(a) 显示如下 需要知道command是怎么传入的 通过原始的数据可以知道 gets函数获取参数a,即用户输入…

诗林工作室(编号:mb0005)分享:HTML模版Paxton,一款自适应响应式图集、博客设计开发模板

这是来自国外一款HTML网页模板,适合Web开发人员做前端站点设计参考使用。全站模版倾向于图集、博客等多行业的平台模版开发。此模版适合各大CMS的主题模版开发参考,如常见的Wordpress主题开发、Z-Blog模板开发、Typecho模板开发、DiscuzX模板开发、Jooml…