redis-变慢原因及排查方法
一 redis的简介
redis的是单线程的,并且它的运行速度很快,若果redis执行比较耗时,就会阻塞其他的请求。
客户端发出一条请求命令分为四部分:发出命令=》命令排队=》命令执行=》返回结果
如果查询速度慢,则主要在命令执行环节
二 排查步骤
1 排查redis的基准性能
基准性能就是指redis在一台负载正常的机器上,其最大的响应延迟和平均响应延迟分别怎么样,若果延迟时间比较大,就要升级硬件
测试60秒内最大响应延迟:$redis-cli -h 127.0.0.1 -p 6379 --intrinsic-latency 60
2 查看slowlog
redis提供了慢日志命令统计功能,查看慢日志时需要设置慢日志的阈值和保留条数
config set slow-log-slower-than 5000 #5000 指的是大于5秒的阈值
config set slowlog-max-len 500 #500是保留的条数
设置完成后,通过指令showlog get 5 查询最近五条慢记录
三 redis变慢原因
1 使用复杂度过高的命令
造成问题的原因:
1 经常使用O(N)以上的复杂命令,例如sort,SUNION,ZUNIONSTORE等聚合类命令,原因:redis在操作内存数据时,时间复杂度过高,要花费更多的CPU资源
2 使用O(N)复杂度命令,但是N的值非常大,原因:redis一次需要返给客户端的数据过多,更多时间花费在数据协议的组装和网络传输过程中
解决方案:
1 尽量不使用O(N)以上复杂度过高的命令,对于数据的聚合操作,放在客户端做
2 执行O(N)命令,保证N的值尽量小(推荐N<=300),每次获取尽量少的数据,让redis可以及时处理返回
2 操作bigkey(value值很大)
造成问题的原因:
value值过大,当redis写入或删除比较大的value值时,分配或释放内存的时间都比较长。
通过redis-cli -h 127.0.0.1 -p 6379 --bigkeys -i 0.01 查找bigkey
解决方案:
1 业务尽量避免写入bigkey #推荐
2 redis6.0以上的版本,开启lazy-free机制(配置文件加上:lazy-free-user-del=yes),在执行del命令时,释放内存会放在后台进程里执行 #不推荐
3 集中过期
造成问题的原因:
平时都是正常的,突然某个时间点出现一波延时,大概率证明redis出现了集中过期。如果有大量的key在某个固定的时间段集中过期,在这个时间段访问redis时,就有可能导致延期过大
redis的过期数据采用被动过期+主动过期两种策略
被动过期:只有当访问某个key时,才判断这个key是否过期,如果过期则从实例中删除
主动过期:redis内部维护一个定时任务,默认每隔100毫秒就会从全局的过期哈希表中随机取出20个key,然后删除其中过期的key,如果过期key的比例超过了25%,则重复此过程。如果过期的key不足25%或者执行超过了25毫秒,就会退出循环
延期原理:
自动过期的key是在redis的主线程执行的,如果执行主动过期的过程中出现大量的删除过期key的情况,那么这个时间段客户端访问redis时,就要排队等候,如果删除的过期的key存在bigkey,那么就会出现延迟变大
解决方案
1 redis4.0版本以上,可以开启lazy-free机制,即在配置文件中加入 **lazyfree-lazy-expire yes**。这样当删除过期key的时候,把释放内存放到后台执行,避免阻塞主线程。
2 集中过期的key增加一个随机过期时间,把集中过期的时间打散,降低redis清理过期key的压力,例如:redis.expireat(key,expire_time+random(500))
4 开启AOF
AOF工作原理:
当redis开启AOF后,其工作原理如下
1 redis执行写命令时,把这个写命令写入到AOF文件内存中(write系统调用)
2 redis根据配置的AOF刷盘策略,把AOF内存数据刷到磁盘上(fsync系统调用)
AOF的3种刷盘机制:
1 appendfsync always: 主线程每次执行写操作立即执行刷盘。会占用比较大的磁盘IO资源,数据安全性最高
2 appendfsync no: 主线程每次写操作只写内存就返回,内存数据什么时候刷到磁盘,交由系统决定。性能最高,但是安全性最低,会丢失数据
3 appendfsync everysec(默认):主线程每次写操作只写内存就返回,然后后台线程每隔1秒执行一次刷盘操作。对性能影响较小,存在丢失1秒内的数据的可能
ps: 操作磁盘比操作内存慢几百倍
appendfsync everysec的实现过程 :
造成问题的原因:
当redis后台系统执行AOF刷盘时,磁盘的IO负载很高,那么这个后台系统的刷盘操作可能会阻塞住,这种情况会影响写内存的过程,所以也会出现延时等待
解决方案:
在配置文件加入:no-appendfsync-on-rewrite yes;目的是AOFrewrite期间,AOP的后台子线程不进行刷盘操作,相当于这期间,临时把appendfsync 设置未none。但是存在宕机对数据的情况
5 fork耗时严重
排查思路:
为了保证Redis数据的安全性,我们可能会开启后台定时RDB和AOF rewrite功能。如果你发现操作Redis延迟变大,都发生在Redis后台RDB和AOF rewrite期间,那你就需要排查,在这期间有可能导致变慢的情况。
导致变慢的原因:
1 当Redis开启了后台RDB和AOF rewrite后,在执行时,它们都需要主进程创建出一个子进程进行数据的持久化。主迸程创子进 程,会调用操作系统提供的fork函数。
2 而fork在执行过程中,主进程需要拷贝自己的内存页表给子进程,如果这个实例很大,那么这个拷贝的过程也会比较耗时。
3 而且这个fork过程会消耗大量的CPU资源,在完成fork之前,整个Redis实例会被阻塞住,无法处理任何客户端请求。
4 如果此时你的CPU资源本来就很紧张,那么fork的耗时会更长,甚至达到秒级,这会严重影响Redis的性能。
ps: 在redis上执行info命令,可以通过查看latest_fork_usec获取上一次fork的耗时,单位是微秒
除了数据持久化外,当主从节点第一次建立数据同步时,主节点也创建子进程生成RDB文件,然后发给从节点进行一次全量同步,这个过程也会对redis产生性能影响。
解决方案:
1 控制Redis实例的内存:尽量在10G以下,执行FORK的耗时与实例大小有关.实例越大,耗时越久
2 合理配置数据持久化策略:在slave节点执行RDB备份,推荐在低峰期执行,而对于丢失数据不敏感的业务(例如把 redis当做纯缓存使用)可以关闭AOF和AOF rewrite
3 redis 实例不要部署在虚拟机上:fork的耗时也与系统也有关,虚拟机比物理机耗时更久
4 降低主从库全量同步的概率:适当调大repl-backlog-size参数,避免主从全量同步
6 碎片整理
简介
Redis的数据都存储在内存中,当我们的应用程序频繁修改Redis中的数据时,就有可能会导致Redis产生内存碎片。内存碎片会降低Redis的内存使用率,我们可以通过执行INFO命令,得到这个实例的内存碎片率:
# Memory
used memory:5709194824
used memory human:5.32G used_memory_rss:8264855552 used memory rss human:7.70G
mem Jragmentation_ratio:1.45
这个内存碎片率是怎么计算的?
很简单,mem_fragmentation_ratio = used_memory_rss / used_memoryo ,其中used_memory表示Redis存储数据的内存大小,而used_memory_rss表示操作系统实际分配给Redis进程的大小。如果mem_f「agmentation_「ati。> 1.5,说明内存碎片率
已经超过了 50%,这时我们就需要采取一些措施来降低内存碎片了。
解决的方案一般如下:
-
如果你使用的是Redis 4.0以下版本,只能通过重启实例来解决
-
如果你使用的是Redis 4.0版本,它正好提供了自动碎片整理的功能,可以通过配置开启碎片自动整理
导致变慢的原因:
Redis的碎片整理工作是也在主线程中执行的,当其进行碎片整理时,必然会消耗CPU资源,产生更多的耗时,从而影响到客户端的请求。
Redis碎片整理的参数配置如下:
#开启自动内存碎片整理(总开关)
activedefrag yes
# 内存使用100MB以下,不进行碎片整理
active-defrag-ignore-bytes 10Omb
# 内存碎片率超过10%,开始碎片整理
active-defrag-threshold-lower 10
# 内存碎片率超过100%,尽最大努力碎片整理
active-defrag-threshold-upper 100
# 内存碎片整理占用CPU资源最小百分比
active-defrag-cycle-min 1
# 内存碎片整理占用CPU资源最大百分比
active-defrag-cycle-max 25
#碎片整理期间,对于LisVSet/Hash/ZSet类型元素一次Scan的数量
active-defrag-max-scan-fields 1000
四 redis变慢(系统相关)
1实例内存达到上限
排查思路:
如果你的Redis实例设置了内存上限maxmemory,那么也有可能导致Redis变慢。当我们把Redis当做纯缓存使用时,通常会给这个实例设置一个内存上限maxmemory,然后设置一个数据淘汰策略。
而当实例的内存达到了 maxmemory后,你可能会发现,在此之后每次写入新数据.操作延迟变大了。
导致变慢的原因:
当Redis内存达到maxmemory后,每次写入新的数据之前,Redis必须先从实例中踢出一部分数据,让整个实例的内存维持在maxmemory之下,然后才能把新数据写进来。
这个踢出旧数据的逻辑也是需要消耗时间的,而具体耗时的长短,要取决于你配置的淘汰策略:
• allkeys-lru:不管key是否设置了过期,淘汰最近最少访问的key
• volatile-lru:只淘汰最近最少访问、并设置了过期时间的key
• allkeys-random:不管key是否设置了过期,随机淘汰key
• volatile-random:只随机淘汰设置了过期时间的key
• allkeys-ttl:不管key是否设置了过期,淘汰即将过期的key *下
• noeviction:不淘汰任何key,实例内存达到maxmeory后,再写入新数据直接返回错误
• allkeys-lfu:不管key是否设置了过期,淘汰访问频率最低的key ( 4.0+版本支持)
• volatile-lfu:只淘汰访问频率最低、并设置了过期时间key ( 4.0+版本支持)
需要注意的是,Redis的淘汰数据的逻辑与删除过期key的一样,也是在命令真正执行之前执行的,也就是说它也会增加我们操作Redis的延迟,而且,写OPS越高,延迟也会越明显。
解决方案
1 .避免存储bigkey,降低释放内存的耗时
2 .淘汰策略改为随机淘汰,随机淘汰比LRU要快很多(视业务情况调整)
3 .拆分实例,把淘汰key的压力分摊到多个实例上
4 .如果使用的是Redis 4.0以上版本,开启layz-f「ee机制,把淘汰key释放内存的操作放到后台线程中执行(配置lazyfree-lazyeviction = yes)
2 开启内存大页
排查思路
• 我们都知道,应用程序向操作系统申请内存时.是按内存页进行申请的.而常规的内存页大小是4KB。
• Linux内核从2.6.38开始,支持了内存大页机制,该机制允许应用程序以2MB大小为单位,向操作系统申请内存。
• 应用程序每次向操作系统申请的内存单位变大了,但这也意味着申请内存的耗时变长。
导致变慢的原因:
1.当Redis在执行后台RDB和AOF rewrite时,采用fork子进程的方式来处理。但主进程fork子迸程后,此时的主进程依旧是可以接收写请求的.而进来的写请求,会采用Copy On Write (写时复制)的方式操作内存数据。
2.也就是说,主进程一旦有数据需要修改,Redis并不会直接修改现有内存中的数据,而是先将这块内存数据拷贝出来,再修改这块新内存的数据,这就是所谓的 [写时复制]
3.写时豆制你也可以埋解成,谁需要发生写操作,谁就需要先拷贝,再修改。
4.这样做的好处是,父进程有任何写操作,并不会影响子进程的数据持久化(子进程只持久化fork这一瞬间整个实例中的所有数据即可,不关心新的数据变更.因为子迸程只需要一份内存快照,然后持久化到磁盘上)。
5.但是请注意,主进程在拷贝内存数据时,这个阶段就涉及到新内存的申请,如果此时操作系统开启了内存大页,那么在此期间,客户端即便只修改10B的数据,Redis在申请内存时也会以2MB为单位向操作系统申请,申请内存的耗时变长,进而导致每个写请求的延迟增加,影响到Redis性能。
6.同样地,如果这个写请求操作的是一个bigkey,那主进程在拷贝这个bigkey内存块时.一次申请的内存会更大,时间也会更久。可见,bigkey在这里又一次影响到了性能。
解决方案
关闭内存大页机制。
首先,你需要查看Redis机器是否开启了内存大页:
$ cat/sys/kernel/mm/transparent_hugepage/enabled
[always] madvise never
如果输出选项是always,就表示目前开启了内存大页机制,我们需要关掉它:
$ echo never > /sys/kernel/mm/transparent_hugepage/enabled
其实,操作系统提供的内存大页机制,其优势是,可以在一定程序上降低应用程序申请内存的次数。
但是对于Redis这种对性能和延迟极其敏感的数据库来说,我们希望Redis在每次申请内存时,耗时尽早短,所以我不建议你在Redis机器上开启这个机制。
3 使用Swap
排查思路
如果你发现Redis突然变得非常慢.每次的操作耗时都达到了几百毫秒甚至秒级,那此时你就需要检查Redis是否使用到了 Swap在这种情况下Redis基本上已经无法提供高性能的服务了。
导致变慢的原因:
什么是Swap?为什么使用Swap会导致Redis的性能下降?
如果你对操作系统有些了解,就会知道操作系统为了缓解内存不足对应用程序的影响.允许把一部分内存中的数据换到磁盘上,以达到应用程序对内存使用的缓冲,这些内存数据被换到磁盘上的区域,就是Swap。
问题就在于,当内存中的数据被换到磁盘上后,Redis再访问这些数据时,就需要从滋盘上读取,访问磁盘的速度要比访问内存慢几百倍!尤其是针对Redis这种对性能要求极高、性能极其敏感的数据库来说,这个操作延时是无法接受的。
此时,你需要检查Redis机器的内存使用情况,确认是否存在使用了 Swap。你可以通过以下方式采查看Redis进程是否使用到了Swap:
#先找到Redis的进程ID
$ ps -aux | grep redis-server
#查看Redis Swap使用情况
S cat/proc Spid/smaps i egrep,A(Swap|Size)'
输出结果如下:
Size: 1256 kB
Swap: OkB
Size: 4kB
Swap: OkB
Size: 132 kB
Swap: OkB
Size: 63488 kB
Swap: OkB
Size: 132 kB
Swap: OkB
Size: 65404 kB
Swap: OkB
Size: 1921024 kB
Swap: OkB
这个结果会列出Redis进程的内存使用情况。
每一行Size表示Redis所用的一块内存大小,Size下面的Swap就表示这块Size大小的内存,有多少数据已经被换到磁盘上了,如果这两个值相等,说明这块内存的数据都已经完全被换到磁盘上了。如果只是少量数据被换到磁盘上.例如每一块Swap占对应Size的比例很小,那影响并不是很大。如果是几百兆甚至上GB的内存被换到了磁盘上,那么你就需要警惕了,这种情况Redis的性能肯定会急剧下降。
解决方案
1 .增加机器的内存,让Redis有足够的内存可以使用
2 .整理内存空间,释放出足够的内存供Redis使用,然后释放Redis的Swap,让Redis重新使用内存
释放Redis的Swap过程通常要重启实例,为了避免重启实例对业务的影响,一般会先进行主从切换,然后释放旧主节点的Swap.重启旧主节点实例,待从库数据同步完成后,再进行主从切换即可。
可见,当Redis使用到Swap后,此时的Redis性能基本已达不到高性能的要求(你可以埋解为武功被废),所以你也需要提前预防这种情况。预防的办法就是,你需要对Redis机器的内存和Swap使用情况进行监控,在内存不足或使用到Swap时报警出来,及时处理。
4 网络带宽过载
排查思路:
如果以上产生性能问题的场景,你都规避掉了,而且Redis也稳定运行了很长时间,但在某个时间点之后开始.操作Redis突然开始变慢了,而且一直持续下去,这种情况又是什么原因导致?
此时你需要排查一下Redis机器的网络带宽是否过钱,是否存在某个实例把整个机器的网路带宽占满的情况。
导致变慢的原因:
网络带宽过载的情况下,服务器在TCP层和网络层就会出现数据包发送延迟、丢包等情况。
Redis的高性能,除了操作内存之外,就在于网络IO 了,如果网络IO存在瓶颈,那么也会严重影响Redis的性能。
解决方案
1 .及时确认占满网络带宽Redis实例,如果属于正常的业务访问,那就需要及时扩容或迁移实例了,避免因为这个实例流量过大,影响这个机器的其他实例。
2 .运维层面,你需要对Redis机器的各项指标增加监控,包括网络流量,在网络流量达到一定闹值时提前报警,及时确认和扩容。
5其他原因
1)频繁短连接
你的业务应用,应该使用长连接操作Redis,避免频繁的短连接。
频繁的短连接会导致Redis大是时间耗费在连接的建立和释放上,TCP的三次握手和四次挥手同样也会增加访问延迟。
2) 运维监控
1 监控其实就是对采集Redis的各项运行时指标,通常的做法是监控程序定时采集Redis的INFO信息,然后根据INFO信息中的状态数据做数据展示和报警。
2 这里我需要提醒你的是.在写一些监控脚本.或使用开源的监控组件时.也不能掉以轻心。在写监控脚本访问Redis时,尽全采用长连接的方式采集状态信息,避免频繁短连接。同时,你还要注意控制访问Redis的频率.避免影响到业务请求。
3 在使用一些开源的监控组件时,最好了解一下这些组件的实现原理,以及正确配置这些组件,防止出现监控组件发生Bug,导致短时大量操作Redis,影响Redis性能的情况发生。
3) 其它程序争抢资源
Redis机器最好专项专用,只用来部署Redis实例,不要部署其他应用程序,尽早给Redis提供一个相对「安静」的环境,避免其它程序占用CPU、内存、磁盘资源,导致分配给Redis的资源不足而受到影响。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构