Redis性能调优:深入剖析变慢原因及应对策略

news/2024/12/22 19:43:54/

  如果观察到,这个实例的运行延迟是正常 Redis 基准性能的 2 倍以上,即可认为这个 Redis 实例确实变慢了。

1.如何查看实例的运行延迟

  (1)redis-cli -h 127.0.0.1 -p 6379 --intrinsic-latency 60   执行该命令,就可以测试出这个实例 60 秒内的最大响应延迟,如下图:

  从输出结果可以看到,这 60 秒内的最大响应延迟为 72 微秒(0.072毫秒)

(2)redis-cli -h 127.0.0.1 -p 6379 --latency-history -i 1   通过该命令查看一段时间内 Redis 的最小、最大、平均访问延迟,如下图:

  以上输出结果是,每间隔 1 秒,采样 Redis 的平均操作耗时,其结果分布在 0.08 ~ 0.13 毫秒之间。

(3)执行redis-benchmark -h 127.0.0.1 -p 6379 -t set,lpush -n 100000 -c 50,这个命令会模拟50个并发客户端,每个客户端发送10万个请求,请求命令为set,lpush(如果省略每种命令都会执行一遍),对指定的Redis服务器进行性能测试。

2.使用复杂度过高的命令

  通过SLOWLOG get 5命令,查看 Redis 的慢日志(slowlog),找出执行慢的命令操作。

   如果应用程序执行的Redis命令有以下特点,那么有可能会导致操作延迟变大:

  • 经常使用 O(N) 以上复杂度的命令,例如 SORT、SUNION、ZUNIONSTORE 聚合类命令: 导致变慢的原因在于,Redis 在操作内存数据时,时间复杂度过高,要花费更多的 CPU 资源     优化建议:对于数据的聚合操作,放在客户端做

  • 使用 O(N) 复杂度的命令,但 N 的值非常大: 导致变慢的原因在于,Redis 一次需要返回给客户端的数据过多,更多时间花费在数据协议的组装和网络传输过程中    优化建议: 执行 O(N) 命令,保证 N 尽量的小(推荐 N <= 300),每次获取尽量少的数据,让 Redis 可以及时处理返回   N表示命令的个数

3.操作bigkey

  如果查询慢日志发现,并不是复杂度过高的命令导致的,而都是 SET / DEL 这种简单命令出现在慢日志中,那么就要怀疑实例否写入了 bigkey。

  如果一个key写入的value非常大,那么Redis在分配内存时就会比较耗时。同样的,当删除这个key时,释放内存也会比较耗时。

  通过使用bigkey的命令,扫描大值key, 其实,使用这个命令的原理,就是 Redis 在内部执行了 SCAN 命令,遍历整个实例中所有的 key,然后针对 key 的类型,分别执行 STRLEN、LLEN、HLEN、SCARD、ZCARD 命令,来获取 String 类型的长度、容器类型(List、Hash、Set、ZSet)的元素个数。

  优化建议:

  • 业务应用尽量避免写入 bigkey

  • 如果你使用的 Redis 是 4.0 以上版本,用 UNLINK 命令替代 DEL,此命令可以把释放 key 内存的操作,放到后台线程中去执行,从而降低对 Redis 的影响

  •  如果你使用的 Redis 是 6.0 以上版本,可以开启 lazy-free 机制(lazyfree-lazy-user-del = yes),在执行 DEL 命令时,释放内存也会放到后台线程中执行

4.集中过期

  如果发现,平时在操作 Redis 时,并没有延迟很大的情况发生,但在某个时间点突然出现一波延时,其现象表现为:变慢的时间点很有规律,例如某个整点,或者每间隔多久就会发生一波延迟。如果是出现这种情况,那么需要排查一下,业务代码中是否存在设置大量 key 集中过期的情况。

(1)为什么集中过期会导致 Redis 延迟变大?

  答:Redis 的过期数据采用被动过期 + 主动过期两种策略:

  • 被动过期:只有当访问某个 key 时,才判断这个 key 是否已过期,如果已过期,则从实例中删除

  • 主动过期:Redis 内部维护了一个定时任务,默认每隔 100 毫秒(1秒10次)就会从全局的过期哈希表中随机取出 20 个 key,然后删除其中过期的 key,如果过期 key 的比例超过了 25%,则继续重复此过程,直到过期 key 的比例下降到 25% 以下,或者这次任务的执行耗时超过了 25 毫秒,才会退出循环

  注意,这个主动过期 key 的定时任务,是在 Redis 主线程中执行的。也就是说如果在执行主动过期的过程中,出现了需要大量删除过期 key 的情况,那么此时应用程序在访问 Redis 时,必须要等待这个过期任务执行结束,Redis 才可以服务这个客户端请求。此时就会出现,应用访问 Redis 延时变大。如果此时需要过期删除的是一个 bigkey,那么这个耗时会更久。而且,这个操作延迟的命令并不会记录在慢日志中。因为慢日志中只记录一个命令真正操作内存数据的耗时,而 Redis 主动删除过期 key 的逻辑,是在命令真正执行之前执行的。

   优化建议:

  • 集中过期 key 增加一个随机过期时间,把集中过期的时间打散,降低 Redis 清理过期 key 的压力

  • 如果你使用的 Redis 是 4.0 以上版本,可以开启 lazy-free 机制,当删除过期 key 时,把释放内存的操作放到后台线程中执行,避免阻塞主线程。  lazyfree-lazy-expire yes

5.实例内存达到上限

  如果你的Redis实例设置了内存上限maxmemory,那么也有可能导致Redis变慢,当Redis内存达到maxmemory后,每次写入新的数据之前,Redis必须先从实例中踢出一部分数据,让整个实例的内存维持在maxmemory之下,然后才能把新数据写进来。这个踢出旧数据的逻辑也是需要消耗时间的,而具体耗时的长短,要取决于你配置的淘汰策略。

    需要注意的是,Redis 的淘汰数据的逻辑与删除过期 key 的一样,也是在命令真正执行之前执行的,也就是说它也会增加我们操作 Redis 的延迟,而且,写 OPS 越高,延迟也会越明显。

  优化建议:

  • 避免存储 bigkey,降低释放内存的耗时

  • 淘汰策略改为随机淘汰,随机淘汰比 LRU 要快很多(视业务情况调整)

  • 拆分实例,把淘汰 key 的压力分摊到多个实例上

  • 如果使用的是 Redis 4.0 以上版本,开启 layz-free 机制,把淘汰 key 释放内存的操作放到后台线程中执行(配置 lazyfree-lazy-eviction = yes)

6.fork耗时严重

  主进程创建子进程( RDB 和 AOF ),会调用操作系统提供的fork函数。而fork在执行过程中,主进程需要拷贝自己的内存页表给子进程,如果这个实例很大,那么这个拷贝的过程也会比较耗时。而且这个fork过程会消耗大量的CPU资源,在完成fork之前,整个Redis实例会被阻塞住,无法处理任何客户端请求。

  (1)那如何确认确实是因为 fork 耗时导致的 Redis 延迟变大呢?

  答:在Redis上执行INFO命令,查看latest_fork_usec项,单位微秒。latest_fork_usec:59477  这个时间就是主进程在fork子进程期间,整个实例阻塞无法处理客户端请求的时间, 如果发现这个耗时很久,就要警惕起来了,这意味在这期间,整个 Redis 实例都处于不可用的状态。

  优化建议:

  • 控制 Redis 实例的内存:尽量在 10G 以下,执行 fork 的耗时与实例大小有关,实例越大,耗时越久

  • 合理配置数据持久化策略:在 slave 节点执行 RDB 备份,推荐在低峰期执行,而对于丢失数据不敏感的业务(例如把 Redis 当做纯缓存使用),可以关闭 AOF 和 AOF rewrite

  • Redis 实例不要部署在虚拟机上:fork 的耗时也与系统也有关,虚拟机比物理机耗时更久

  • 降低主从库全量同步的概率:适当调大 repl-backlog-size 参数(环形内存的大小),避免主从全量同步

7.开启内存大页

  我们都知道,应用程序向操作系统申请内存时,是按内存页进行申请的,而常规的内存页大小是 4KB。Linux 内核从 2.6.38 开始,支持了内存大页机制,该机制允许应用程序以 2MB 大小为单位,向操作系统申请内存。应用程序每次向操作系统申请的内存单位变大了,但这也意味着申请内存的耗时变长。

(1)这对 Redis 会有什么影响呢?

   答:主要跟redis Copy On Write机制有关。 主进程在拷贝内存数据时,这个阶段就涉及到新内存的申请,如果此时操作系统开启了内存大页,那么在此期间,客户端即便只修改 10B 的数据,Redis 在申请内存时也会以 2MB 为单位向操作系统申请,申请内存的耗时变长,进而导致每个写请求的延迟增加,影响到 Redis 性能。 同样地,如果这个写请求操作的是一个 bigkey,那主进程在拷贝这个 bigkey 内存块时,一次申请的内存会更大,时间也会更久。

  优化建议:

  对于 Redis 这种对性能和延迟极其敏感的数据库来说,我们希望Redis在每次申请内存时,耗时尽量短,所以不建议你在Redis机器上开启这个机制。

  查看 Redis 机器是否开启了内存大页 :cat /sys/kernel/mm/transparent_hugepage/enabled

  关闭redis内存大页机制 :echo never > /sys/kernel/mm/transparent_hugepage/enabled

8.AOF刷盘机制

  根据业务需求选择AOF三种不同的刷盘时机,不同的处理策略会提升redis的性能。

  在 Redis.conf 配置文件中的 appendfsync 配置项可以有以下 3 种参数可填:

  • Always,这个单词的意思是「总是」,所以它的意思是每次写操作命令执行完后,同步将 AOF 日志数据写回硬盘;主线程

  • Everysec,这个单词的意思是「每秒」,所以它的意思是每次写操作命令执行完后,先将命令写入到 AOF 文件的内核缓冲区,然后每隔一秒将缓冲区里的内容写回到硬盘; AOF 刷盘线程(异步)

  • No,意味着不由 Redis 控制写回硬盘的时机,转交给操作系统控制写回的时机,也就是每次写操作命令执行完后,先将命令写入到 AOF 文件的内核缓冲区,再由操作系统决定何时将缓冲区内容写回硬盘。

9.使用Swap

  如果发现 Redis 突然变得非常慢,每次的操作耗时都达到了几百毫秒甚至秒级,那此时就需要检查Redis是否使用到了 Swap,在这种情况下Redis基本上已经无法提供高性能的服务了。

  (1)什么是swap?

   答:如果对操作系统有些了解,就会知道操作系统为了缓解内存不足对应用程序的影响,允许把一部分内存中的数据换到磁上,以达到应用程序对内存使用的缓冲,这些内存数据被换到磁盘上的区域,就是 Swap。问题就在于,当内存中的数据被换到磁盘上后,Redis 再访问这些数据时,就需要从磁盘上读取,访问磁盘的速度要比访问内存慢几百倍!

(2) 查看Redis 进程是否使用到了 Swap?

  答:# 先找到 Redis 的进程 ID               $ ps -aux | grep redis-server

   # 查看 Redis Swap 使用情况           $ cat /proc/$pid/smaps | egrep '^(Swap|Size)'

  输出结果如下:

   每一行Size表示Redis所用的一块内存大小,Size下面的Swap就表示这块Size大小的内存,有多少数据已经被换到磁盘上了,如果这两个值相等,说明这块内存的数据都已经完全被换到磁盘上了。如果只是少量数据被换到磁盘上,例如每一块Swap占对应Size的比例很小,那影响并不是很大。如果是几百兆甚至上GB的内存被换到了磁盘上,那么你就需要警惕了,这种情况Redis的性能肯定会急剧下降。

  优化建议:

  • 增加机器的内存,让 Redis 有足够的内存可以使用

  • 整理内存空间,释放出足够的内存供 Redis 使用,然后释放 Redis 的 Swap,让 Redis 重新使用内存

10.碎片整理

  Redis 的数据都存储在内存中,当我们的应用程序频繁修改 Redis 中的数据时,就有可能会导致 Redis 产生内存碎片,内存碎片会降低 Redis 的内存使用率

(1)如何计算碎片率?

  答:可以通过执行 INFO 命令,得到这个实例的内存碎片率, mem_fragmentation_ratio = used_memory_rss / used_memory。其中 used_memory 表示 Redis 存储数据的内存大小,而 used_memory_rss 表示操作系统实际分配给 Redis 进程的大小。如果 mem_fragmentation_ratio > 1.5,说明内存碎片率已经超过了 50%,这时我们就需要采取一些措施来降低内存碎片了。

  优化建议:

  • 如果你使用的是 Redis 4.0 以下版本,只能通过重启实例来解决

  • 如果你使用的是 Redis 4.0 版本,它正好提供了自动碎片整理的功能,可以通过配置开启碎片自动整理:开启自动内存碎片整理(总开关):activedefrag yes;当碎片达到 100mb 时,开启内存碎片整理:active-defrag-ignore-bytes 100mb; 当碎片超过 10% 时,开启内存碎片整理:active-defrag-threshold-lower 10;内存碎片超过 100%,则尽最大努力整理:active-defrag-threshold-upper 100;内存自动整理占用资源最小百分比:active-defrag-cycle-min 25; 内存自动整理占用资源最大百分比:active-defrag-cycle-max 75。

  但是,开启内存碎片整理,它也有可能会导致 Redis 性能下降,原因在于,Redis 的碎片整理工作是也在主线程中执行的,当其进行碎片整理时,必然会消耗 CPU 资源,产生更多的耗时,从而影响到客户端的请求。所以,当你需要开启这个功能时,最好提前测试评估它对 Redis 的影响。


http://www.ppmy.cn/news/1557272.html

相关文章

深度学习网络训练及部署环节相关工具

数据可视化&#xff1a; matplotlib:可视化库seaborn:统计数据可视化模块 网络构建 网络编译&#xff1a; 网络训练&#xff1a; tqdm:进度条模块 模型可解释性 CAM:类别激活映射图 Saliency Maps:显著图 SmoothGrad:效果更好的显著图 GLIME:LIME方法的改进 网络评估…

CPU性能优化--函数分组

热点函数可以被分组到一起以进一步提升CPU前端缓存的利用率&#xff0c;当热点函数被分组在一起时候&#xff0c;他们可能会共用相同的缓存行&#xff0c;这会减少CPU需要读取的缓存行数量。 图44给出了被分组函数foo,bar和zoo的图形化展示。默认布局需要读取四个缓存行&#x…

solon 集成 activemq-client (sdk)

原始状态的 activemq-client sdk 集成非常方便&#xff0c;也更适合定制。就是有些同学&#xff0c;可能对原始接口会比较陌生&#xff0c;会希望有个具体的示例。 <dependency><groupId>org.apache.activemq</groupId><artifactId>activemq-client&l…

群落生态学研究进展】Hmsc包开展单物种和多物种分析的技术细节及Hmsc包的实际应用

联合物种分布模型&#xff08;Joint Species Distribution Modelling&#xff0c;JSDM&#xff09;在生态学领域&#xff0c;特别是群落生态学中发展最为迅速&#xff0c;它在分析和解读群落生态数据的革命性和独特视角使其受到广大国内外学者的关注。在学界不同研究团队研发出…

ip_forward函数

ip_forward 函数是 Linux 内核中用于处理 IP 数据包转发的重要函数。它负责将数据包从一个网络接口转发到另一个网络接口。以下是这个函数的一些关键点和工作流程的概述: 1. **数据包接收**:当一个数据包到达网络设备(如以太网卡)时,内核会首先接收到这个数据包。 2. **路…

删除链表的倒数第N个结点(最优解)

题目来源 19. 删除链表的倒数第 N 个结点 - 力扣&#xff08;LeetCode&#xff09; 题目描述 给你一个链表&#xff0c;删除链表的倒数第 n 个结点&#xff0c;并且返回链表的头结点。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5], n 2 输出&#xff1a;[1,2,3,5]…

一文速通 IIC I2C子系统驱动 通信协议原理 硬件 时序 深度剖析

本文作为一个引入&#xff0c;作用是让读者理解熟知IIC协议关键内容&#xff0c;结合实际手册内容&#xff0c;深度解析协议本质&#xff0c;作为后续嵌入式linux驱动IIC子系统的一个铺垫。 目录 1. 硬件连接 2. IIC传输时序 2.1.写操作 2.2.读操作 2.3.I2C信号 3.IIC协议…

中间件介绍

中间件是一种位于操作系统和应用软件之间的系统软件&#xff0c;它提供了数据交换、应用集成、流程管理和安全保障等服务。以下是中间件的一些基本概念和应用场景&#xff1a; 中间件的定义 中间件是一种独立的系统软件或服务程序&#xff0c;它位于操作系统和应用软件之间&…