redis之变慢了该如何排查?

news2024/11/15 19:36:44

写在前面

不管什么工具,会使用永远只是第一步,第二步是当其出现某些问题时,拥有排查和修复问题的能力,而我们在使用Redis的过程中,变慢就是其中一个比较棘手的问题,因此本文就一起来看下,当遇到该类问题时应该如何排查,以求能够在工作中帮助到你,当然也更加是帮助我自己,下面我们就开始吧!

1:Redis真的变慢了吗?

这部分我们一起看下如何评判Redis是否变慢,比如说某命令的执行时间是1ms,就一定是变慢了吗?不一定,因为如果是对配置很差的机器,这可能是其最好的表现了,但是对于每秒能够处理10万请求的机器,这肯定就是慢了,但,这都是很主观的判断,并没有一个对比的指标,因此我们就需要这样的一个指标,在这里,这个指标就是基线性能,所谓基线性能就是在服务器没有任何压力,没有任何干扰的情况下的基本性能。其实,从Redis2.8.7版本开始,Redis在redis-cli命令提供了--intrinsic-latency {测试秒数},可以用来测试Redis基线性能,如下:

d:\program_files\Redis-x64-2.8.2402>.\redis-cli --intrinsic-latency 120
Max latency so far: 1 microseconds.
Max latency so far: 4 microseconds.
Max latency so far: 5 microseconds.
Max latency so far: 16 microseconds.
Max latency so far: 33 microseconds.
Max latency so far: 35 microseconds.
Max latency so far: 41 microseconds.
Max latency so far: 77 microseconds.
Max latency so far: 93 microseconds.
Max latency so far: 106 microseconds.
Max latency so far: 118 microseconds.
Max latency so far: 486 microseconds.
Max latency so far: 4048 microseconds.

3268568472 total runs (avg latency: 0.0367 microseconds / 367.13 nanoseconds per run).
Worst run took 110260x longer than the average latency.

可以看到基线性能的数据延迟是0.0367微妙,如果是在实际运行过程中,这个时间变为0.0367微妙*2=0.0634微妙左右则肯定就是变慢了,现在我们确定Redis确实是变慢了,剩下的就是定位变慢的原因了。

2:变慢了该怎么办?

首先可能是我们的使用方式有问题,其次是操作系统的配置问题,最后还有可能是文件系统的问题,我们也从这三个方便展开说明。

2.1:使用方式

主要有两种情况,第一种是使用了时间复杂度较高的命令,即存在慢查询的情况,第二种是设置了大量的key在相同的时间过期。

2.1.1:慢查询

我们可以通过配置慢查询日志的方式来排查慢查询问题,处理方式如下:

首先需要配置慢日志,有两种配置方式,第一种是通过设置redis.conf,如下:

# You can configure the slow log with two parameters: one tells Redis
# what is the execution time, in microseconds, to exceed in order for the
# command to get logged, and the other parameter is the length of the
# slow log. When a new command is logged the oldest one is removed from the
# queue of logged commands.

# The following time is expressed in microseconds, so 1000000 is equivalent
# to one second. Note that a negative number disables the slow log, while
# a value of zero forces the logging of every command.
slowlog-log-slower-than 10000

# There is no limit to this length. Just be aware that it will consume memory.
# You can reclaim memory used by the slow log with SLOWLOG RESET.
slowlog-max-len 128

如上使用了slowlog-log-slower-than指令(directive)和slowlog-max-len指令(directive)。这种是永久方案,正式环境还是建议进行如此适当的配置,第二种方案是使用config set动态修改,如下配置:

127.0.0.1:6379> CONFIG SEt slowlog-log-slower-than 1 # 为了方便测试,将慢查询时长设置为1微妙,正常不会设置这么短,
OK
127.0.0.1:6379> CONFIG SET slowlog-max-len 128 # 内存中的慢日志队列最多记录128个元素,超过的将会删除老的
OK

如下执行一些操作:

127.0.0.1:6379> set name jack
OK
127.0.0.1:6379> get name
"jack"
127.0.0.1:6379> slowlog get 2
1) 1) (integer) 10
   2) (integer) 1668663759
   3) (integer) 3
   4) 1) "get"
      2) "name"
2) 1) (integer) 9
   2) (integer) 1668663757
   3) (integer) 6
   4) 1) "set"
      2) "name"
      3) "jack"

然后通过slowlog get {个数} ,返回指定个数的慢查询日志,返回的顺序是执行命令的时间倒序,如下:

127.0.0.1:6379> slowlog get 2
1) 1) (integer) 10
   2) (integer) 1668663759
   3) (integer) 3
   4) 1) "get"
      2) "name"
2) 1) (integer) 9
   2) (integer) 1668663757
   3) (integer) 6
   4) 1) "set"
      2) "name"
      3) "jack"

返回结果的每项含义如下:

1):唯一性(unique)的日志标识符。日志的唯一id只有在Redis服务器重启的时候才会重置,这样可以避免对日志的重复处理。
2):被记录命令的执行时间点,以UNIX时间戳格式表示
3):查询执行时间,单位为微秒
4):执行的命令,以数组的形式排列

也可以通过slowlog len 命令查询当前慢日志条目(entry)的个数,如下:

127.0.0.1:6379> slowlog len
(integer) 4

想要清空(cleared)慢日志队列的话,可以通过slowlog reset命令(command)。

当慢日志条目个数超过了slowlog-max-len配置的队列最大长度后,将会删除老的条目,生产环境中不建议将该值设置的过大,因为会占用内存空间,比较建议的做法是,如果是需要保留慢日志的话,可以定期的将慢日志条目同步到三方组件,如MySQL中,进行持久化存储,直接使用其日志ID作为主键ID也是个不错的选择。

2.1.2:过期key删除

想要搞清楚过期key删除为什么会导致查询变慢,需要先搞清楚过期key删除方式,是这样子的,Redis会定期,默认是100毫秒,检测过期需要删除的key,采样ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP个数的key,并将其中过期的key全部删除,如果采样的key,超过四分之一的key都过期,则会重复这个采样删除的过程,直到过期key个数低于四分之一为止,需要注意的是,这个删除过期key的过程是阻塞的(Redis4之后使用了异步线程进行优化),因此如果是同一时刻有大量的key过期的话,可能就会造成主线程的长时间阻塞,对于这个问题就需要排查程序是否有大量的key通过expire设置了相同的过期时间,这种问题的解决方法也比较简单,对于同一批key,在设置expire的时候,通过生成一定范围内的随机数,使key的过期时间分散在一定范围内。

2.2:文件系统

和文件系统相关的其实就是AOF 了,在了解AOF如何影响主线程的操作性能之前,我们需要看下AOF的工作方式,AOF将日志写到磁盘上是通过2个文件系统的调用来完成,一个是write,另一个是fsync,其中write用来将日志写到内核缓冲区,fsync用来将内核缓存区的内容写到磁盘,fsync因为要向磁盘写数据所以速度较慢。AOF可以设置的值有no,everysec,always,和write,fsync这两个系统调用的关系如下:

在这里插入图片描述

当我们将appendfsync设置为always时,因为每一个数据写入的命令,都要同步的等fsync执行完成,所以此时会阻塞主线程,进而影响Redis的性能,导致变慢。那么当我们设置为everysec时是不是就不会导致这个问题了呢?也是会的,因为主线程在第一次启动了一个子线程执行fsync后,还会监测其执行进度,如果是在第二次需要执行fsync时发现上次的子线程fsync还没有执行完毕的话,依然会阻塞主线程,导致Redis变慢,那么什么时候会出现这种情况呢,当磁盘IO压力很大的时候就可能出现了,在不考虑其他应用对IO资源占用的情况下,Redis本身也会导致IO压力变大,其中AOF重写 就是主要元凶,AOF重写用来减小AOF文件的大小,当数据较多时,其对IO的压力会比较大,对于这种情况,我们一般有如下的几种解决方案:

1:业务允许的话,将appendfsync改为no
2:使用Redis集群,减少单个Redis实例的数据量,从而减少AOF重写对IO的压力
3:更换IO性能更好的固态硬盘,但代价也更大,其速度是普通机械硬盘的10倍以上
4:设置no-appendfsync-on-rewrite yes,代表在进行AOF重写时不要调用fsync,但这种方式如果发生了异常宕机,会导致数据丢失

以上方案要根据具体的业务场景进行分析,选择一种最适合自己的,个人感觉其中1,4可能是会被用到比较多。因为2,3不管是带来的工作量,还是成本都还是比较高的。

2.3:操作系统

和操作系统相关的因素是内存,其中可能让Redis变慢的主要是2方面的原因,第一个是swap,第二个是内存大页,我们分别来看下。

2.3.1:swap

swap是操作系统在内存紧张时执行的一种机制,即将部分不常用的内存空间对应的数据写到磁盘,然后将该内存分配给程序使用,当需要用到该部分数据时再将数据从磁盘上读取出来,这里涉及到了磁盘的IO,所以速度会比较慢,这也是swap可能导致Redis变慢的原因。

如何判断是否发生了swap呢,首先需要先确定Redis实例的进程号,如下:

[root@localhost 1300]# ./redis-cli info | grep process_id
1366

然后通过如下命令查看是否发生了swap:

[root@localhost 1366]# cd /proc/1366
[root@localhost 1366]# cat smaps | egrep '^(Swap|Size)'
Size: 584 kB
Swap: 0 kB
Size: 4 kB
Swap: 4 kB
Size: 4 kB
Swap: 0 kB
Size: 462044 kB
Swap: 462008 kB
Size: 21392 kB
Swap: 0 kB

其中的size代表的就是申请的内存块大小(可以看到是申请了多个内存块的,而不是一大块),紧跟着的swap就是该块内存发生了swap的大小,当大于0时就是代表发生了内存交换,当和size相等时就说明全部发生了内存交换swap。当出现了超过MB级别的内存交换swap时就有可能会导致Redis变慢了,说明Redis内存紧张了,针对这种情况有如下的方案可供参考:

1:如果是集群环境,则增加新节点,降低单节点的内存占用量
2:增加机器内存

2.3.2:大内存页

内存页是操作系统分配内存时的最小单位,一般是4kb,Linux 内核从 2.6.38版本开始支持2M的大内存页的分配。

理论上大内存页应该是更好了才对,因为会减少内存分配的时长,但是我们需要考虑RBD 的场景,在执行RDB时,为了保证RDB生成快照的数据数据一致性(为同一时刻的数据),对于更新操作,Redis会使用cow机制,对对应的内存页拷贝一份,因此对于大内存页,即使是修改的只是几个字节的数据,也需要拷贝2M的数据,而这个过程是会阻塞主线程的,所以也会导致Redis变慢的问题,对于这个问题处理也比较简单,只需要关闭大内存页机制就可以了,如下可以查看是否打开了大内存页:

[root@localhost 1300]# cat /sys/kernel/mm/transparent_hugepage/enabled 
[always] madvise never

可以看到其中包含always关键字,就说明打开了大内存页机制,可以通过如下命令关闭大内存页:

[root@localhost 1300]# echo never > /sys/kernel/mm/transparent_hugepage/enabled
[root@localhost 1300]# cat /sys/kernel/mm/transparent_hugepage/enabled
never

3:总结

如下是遇到Redis变慢问题时的checklist:

1:获取Redis实例在当前环境下的基线性能。
2:是否用了慢查询命令?如果是的话,就使用其他命令替代慢查询命令,或者把聚合计算命令放在客户端做。
3:是否对过期key设置了相同的过期时间?对于批量删除的key,可以在每个key的过期时间上加一个随机数,避免同时删除。
4:是否存在bigkey?对于bigkey的删除操作,如果你的Redis是4.0及以上的版本,可以直接利用异步线程机制减少主线程阻塞;如果是Redis4.0以前的版本,可以使用SCAN命令迭代删除;对于bigkey的集合查询和聚合操作,可以使用SCAN命令在客户端完成。
5:RedisAOF配置级别是什么?业务层面是否的确需要这一可靠性级别?如果我们需要高性能,同时也允许数据丢失,可以将配置项no-appendfsync-on-rewrite设置为yes,避免AOF重写和fsync竞争磁盘IO资源,导致Redis延迟增加。当然,如果既需要高性能又需要高可靠性,最好使用高速固态盘作为AOF日志的写入盘。
6:Redis实例的内存使用是否过大?发生swap了吗?如果是的话,就增加机器内存,或者是使用Redis集群,分摊单机Redis的键值对数量和内存压力。同时,要避免出现Redis和其他内存需求大的应用共享机器的情况。
7:在Redis实例的运行环境中,是否启用了透明大页机制?如果是的话,直接关闭内存大页机制就行了。
8:是否运行了Redis主从集群?如果是的话,把主库实例的数据量大小控制在2~4GB,以免主从复制时,从库因加载大的RDB文件而阻塞。
9:是否使用了多核CPU或NUMA架构的机器运行Redis实例?使用多核CPU时,可以给Redis实例绑定物理核;使用NUMA架构时,注意把Redis实例和网络中断处理程序运行在同一个CPUSocket上。

写在后面

参考文章列表:

Redis慢日志 。

redis之AOF和RDB持久化 。

揭秘:SSD固态价格大跳水后,为何还是无法取代机械硬盘? 。

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

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

相关文章

STM32实现0.96寸OLED显示模拟IIC和IIC四种实现(标准库和HAL库)

目录 本文通过四种方法实现OLED显示 设备选择 OLED介绍 接线表设计 OLED应用 1.标准库模拟IIC实现OLED显示 2.标准库IIC实现OLED显示 3.HAL库模拟IIC实现OLED显示 4.HAL库IIC实现OLED显示 实现效果 代码下载 本文通过四种方法实现OLED显示 设备选择 1.单片机&#…

数字电路中的基础电路结构

基本单元: 1.1 与非门 1.2 或非门 2输入与非门需要4个晶体管(n输入与非门需要2xn个晶体管),非门需要两个晶体管,2输入或非门需要6个晶体管(n输入或非门需要 2xn 2个晶体管)。 静态存储器 1bi…

用 Java 的 IO 流进行读写文件操作

前言 在计算机领域里 IO,有时也写作 I/O,是Input / Output的缩写,也就是输入和输出。这里的输入和输出是指不同系统之间的数据输入和输出,比如读写文件数据,读写网络数据等等。 本文内容大纲如下: Java 有哪些IO框…

数据结构【AVL树模拟实现】

目录 AVL树概念 AVL树结构 insert AVL树的旋转 新节点插入较高右子树的右侧---右右:左单旋 新节点插入较高左子树的左侧---左左:右单旋 新节点插入较高左子树的右侧---左右:先左单旋再右单旋 新节点插入较高右子树的左侧---右左&…

Bug解决:出现C++:internal compiler error: killed(program cc1plus)

最近在学习hyperscan过程中,安装的过程总是会出现 C:internal compiler error: killed(program cc1plus) 反复查找之后既不是版本问题也不是依赖问题,查阅了很多解决方案后,确认是交换空间不足,所以我们解决的方法是临…

Kotlin 开发Android app(七)上:Kotlin函数fun

对于函数来说,现在的语言越来越想把它往变量上靠。 确实对于函数来说,他应该有很多变量的特性。 在Kotlin 中,定义函数是很简单的一件事情,我觉得编程有的时候是被一些语言给高复杂了,命名很简单的一些事情&#xff0…

matlab绘图

clear a-2:0.1:2; b-3:0.1:3; [x,y]meshgrid(a,b); z(1-(x.2)/4-(y.2)/9).^(1/2); mesh(x,y,z) hold on mesh(x,y,-z) clear a-1:0.1:1; b-2:0.1:2; [x,y]meshgrid(a,b); z(4/9)*(x.2)(y.2); mesh(x,y,z)

运维监控系统 PIGOSS BSM 拓扑自动发现原理

PIGOSS BSM提供了自动发现拓扑功能,能够发现全网拓扑、指定网段拓扑、路由拓扑,能够自动关联系统已经监控的设备的状态在拓扑图上实时显示。在做全网发现的时候,可以指定网段或起始路由器,并设定全网的snmp配置参数后,…

vue 使用webpack打包,出现路径404 ,导致白屏webpack-bundle-analyzer使用

使用webpack打包时,遇到两个问题,导致页面出现白屏 一个是cdn对于静态文件限制大小,不能大于2MB,超过这个大小后,就不能上传cdn成功,导致页面加载时,长时间白屏,且找不到资源&#x…

INTERSPEECH 2022|FS-CANet: 基于全带子带交叉注意力机制的语音增强

INTERSPEECH 2022 FS-CANet: 基于全带子带交叉注意力机制的语音增强 本文由清华大学与腾讯天籁实验室、香港中文大学合作,提出了一个全带-子带交叉注意力(FSCA)模块来交互融合全局信息和局部信息,并将其应用于FullSubNet&#…

Dubbo源码(十) 与Spring一起学习Dubbo里的Aware

目录 一、Spring 1.BeanNameAware 2. BeanClassLoaderAware 3. ApplicationContextAware 4. EnvironmentAware 5. ApplicationEventPublisherAware 6. aware注入时机 二、Dubbo 1. ExtensionAccessorAware 三、小结 现在很多同行做java开发几年了,被迫停留…

[附源码]java毕业设计竞价拍卖系统

项目运行 环境配置: Jdk1.8 Tomcat7.0 Mysql HBuilderX(Webstorm也行) Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。 项目技术: SSM mybatis Maven Vue 等等组成,B/S模式 M…

灵雀云ACP 斩获“2022金边奖-最佳云原生边缘云平台”

近日,由边缘计算社区主办的全球边缘计算大会上海站成功召开,灵雀云凭借出色的全栈云原生技术实力、专业的高品质服务以及在边缘云场景的丰富落地实践,斩获“2022金边奖-最佳云原生边缘云平台”奖项。 “十四五”规划中明确指出要“协同发展云…

使用kubeadm部署kubernetes集群

文章目录环境环境初始化配置hosts配置时钟同步禁用firewalld、selinux、postfix禁用swap分区开启IP转发、修改内核信息配置IPVS安装Docker配置yum源安装docker-ce配置镜像加速器安装kubernetes组件配置yum源安装kubeadm、kubelet、kubectl工具配置containerd部署master安装pod网…

UTF-8、Unicode编码与汉字的相关内容

介绍 UTF-8是Unicode的一种实现方式,比如一个汉字用Unicode编码表示是两个字节,而用UTF8编码表示则为3个字节。 之所以写这篇文章,是因为我的webserver程序中,浏览器发送资源请求且该资源名为中文时出现了编码问题。 UTF8编码 U…

【SQL 中级语法 2】自连接的用法

SQL的连接运算根据其特征的不同,有着不同的名称,如内连接、外连接、交叉连接等。一般来说,这些连接大都是以不同的表或视图为对象进行的,但针对相同的表或相同的视图的连接也并没有被禁止。针对相同的表进行的连接被称为“自连接”…

python之排序

目录1. 对一维array中的数值进行从大到小排序2. 将DataFrame的列逆序排列3. 根据字符串中的数字进行排序3.2 啊4. 列表参考资料1. 对一维array中的数值进行从大到小排序 import numpy as np a np.array([5,6,8,2,1,7,5,3,90,78,62,5,4,2,9,4]) # b a.sort(axis0,kindquickso…

抑制剂拮抗剂等小分子化合物

小分子化合物主要通过调节其蛋白靶点的活性发挥作用。目前小分子化合物的蛋白靶点主要包括酶、离子通道和受体三大类。根据靶点种类的不同,小分子化合物发挥着不同的作用。 1. 酶的抑制剂(enzyme inhibitor) 在所有的小分子化合物中&#xff…

【虹科案例】虹科脉冲发生器在半导体行业中的应用

非易失性存储单元特点 存储器研究的趋势是开发一种称为非易失性 RAM 的新型存储器,它将 RAM 的速度与大容量存储器的数据存储相结合。几年来有许多新单元类型的提议,例如 FeRAM(铁电存储器)、ReRAM(电阻式存储器&…

C++string类的模拟实现以及经验分享

文章目录1. 为什么学习string类?1.1 C语言中的字符串1.2 两个面试题2. string类的实现构造函数:拷贝构造函数赋值运算符重载:析构函数流提取运算符重载1. 为什么学习string类? 1.1 C语言中的字符串 C语言中,字符串是…