更多优质内容
请关注公众号

面试官:redis变慢你会怎么排查和解决-张柏沛IT博客

正文内容

面试官:redis变慢你会怎么排查和解决

栏目:优质转载 系列:深入Redis系列 发布时间:2022-03-16 15:48 浏览量:76

这个问题本质是在问什么情况下redis的主线程会被阻塞住。

 

在不考虑硬件资源差异的情况下,redis变慢有以下可能:

 

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

发生原因:使用O(N)及以上复杂度的指令,而且N非常大,阻塞主线程;

特征:redis的OPS(即客户端每秒对redis的操作次数)不高,但CPU很高;

排查方式:开启和查看slowlog慢日志;

 

解决方法:

避免在redis内聚合数据(如Sort、SUNION等),而是在业务代码聚合;

执行 O(N) 命令时,每次获取N尽量少的数据;

 

 

b. big key操作

发生原因:一个key的value很大时,redis分配内存(写入该key)和释放内存(删除该key)以及读取该key比较耗时,阻塞主线程;

特征:slowlog中发现某个操作是常数复杂度的set/del,但是也记录到slowlog中的情况。

 

排查方式:

--bigkeys,其本质是在内部执行了 SCAN 命令,然后针对 key 的类型,分别执行 STRLEN、LLEN、HLEN、SCARD、ZCARD 命令,来获取 String 类型的长度、容器类型(List、Hash、Set、ZSet)的元素个数;

redis-rdb-tools工具离线排查;

 

排查注意事项:

bigkey 扫描时,Redis 的 OPS 会突增,最好控制一下扫描的频率(指定 -i 参数,它表示扫描过程中每次扫描后休息的时间间隔,单位是秒)

bigkey 扫描容器类型(List、Hash、Set、ZSet)的 key,只能扫描出元素最多的 key。但一个 key 的元素多,不一定表示占用内存也多。

 

解决方法:

删除不常用的bigkey,改用db存储;

对常用的bigkey拆分为多个小key;

 

如何删除big key:

Redis 4.0以前,del 只能在主线程删除,对于容器型key,del的时间复杂度是O(M),其中M是容器类型key包含的元素个数。

可以使用 hscan + hdel、sscan + srem、ltrim、zremrangebyrank每次删除容器的部分元素。

 

Redis 4.0 以上用 UNLINK 命令替代 DEL,此命令可以把释放 key 内存的操作,放到后台线程中去执行;

 

Redis 6.0 以上可开启 lazy-free 机制,在执行 DEL 命令时,释放内存也会放到后台线程中执行;

 

 

c. key集中过期

特征:redis变慢的时间点很有规律,例如某个整点,或者每间隔多久就会发生一波延迟。

原因:为什么key集中过期会让redis变慢?这涉及到redis的主动过期策略。

Redis 每隔 100 毫秒就会从设置了过期时间的全局哈希表中随机取出 20 个 key,删除其中过期的 key。如果过期 key 的比例超过了 25%,则继续重复此过程,直到过期 key 的比例下降到 25% 以下,或者这次任务的执行耗时超过了 25 毫秒,才会退出循环。该过程在 Redis 主线程中执行的,会阻塞主线程,且不会记录到slowlog中。

 

排查方式:

代码层面:检查业务代码出现的expireat / pexpireat 命令。

运维层面:监控 Redis 的各项运行状态数据(执行 INFO 命令就可拿到这个实例的运行数据)。重点关注 expired_keys 这一项,它代表整个实例累计删除过期 key 的数量。 当这个指标在很短时间内突增,需要及时报警出来,然后与业务应用报慢的时间点进行对比分析。

 

解决方法:

key 增加一个随机过期时间;

开启 lazy-free 机制,避免阻塞主线程;

lazy-free有很多参数,例如 lazyfree-lazy-user-del = yes 表示用户的del操作采用lazy free机制;lazyfree-lazy-expire = yes表示redis主动清理过期key时采用lazy free机制;

 

 

d. 实例内存达到 maxmemory 上限

原因:实例内存达到上限时,每次执行写入新key前都需要按照淘汰策略删除旧key,淘汰策略算法是有一定复杂度的。因此在内存达到上限时,OPS越大,延迟越高。

特征:redis实例占用内存达到 maxmemory;

 

解决方案:

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

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

拆分实例,扩大内存;

开启 layz-free 机制,把淘汰 key 释放内存的操作放到后台线程中执行(配置 lazyfree-lazy-eviction = yes)

 

 

 

e. fork耗时严重

特征:Redis 延迟变大,都发生在 Redis 后台 RDB 和 AOF rewrite 期间;

原因:RDB 和 AOF rewrite 在执行时,主进程会fork出一个子进程进行。主进程需要拷贝自己的内存页表(注意是拷贝内存页表,而非拷贝内存空间)给子进程,如果这个实例很大,那么这个拷贝的过程也会比较耗时。在完成 fork 之前,整个 Redis 实例会被阻塞住。

排查方式:执行 INFO 命令,查看 latest_fork_usec 项(fork的耗时),单位微秒。

 

解决方法:

控制 Redis 实例的内存尽量在 10G 以下;

合理配置持久化策略:在 slave 节点执行 RDB 备份;

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

调大复制积压缓冲区(repl-backlog-size),避免主从同步发生全量同步;

 

 

 

f.开启内存大页

原因:应用程序向操作系统申请内存时,是按4KB的内存页进行申请的 。 Linux 支持内存大页机制,允许应用程序以 2MB 大小为单位申请内存。

这在fork子进程的copy on write时会导致,客户端即便只修改 10B 的数据,Redis 的主进程为这10B的数据申请 2MB,申请内存的耗时变长。

解决方法:

关闭系统的内存大页机制。

 

 

g.绑定CPU

redis节点绑定单个CPU会导致主进程和子进程竞争CPU资源,而影响到客户端请求;

不要绑定单个CPU即可。

 

 

h.系统内存不足

我们知道系统内存不足时,虚拟内存技术会将部分内存换入磁盘。频繁的缺页会导致换入换出的磁盘IO次数增加,使redis操作不再是单纯的内存操作。

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

查看 Redis Swap 使用情况
$ cat /proc/$pid/smaps | egrep '^(Swap|Size)'
Size:               1256 kB
Swap:                  0 kB
Size:                  4 kB
Swap:                  0 kB
Size:                132 kB
Swap:                  0 kB
Size:              63488 kB
Swap:                  0 kB
Size:                132 kB
Swap:                  0 kB
Size:              65404 kB
Swap:                  0 kB
Size:            1921024 kB
Swap:                  0 kB

每一行 Size 表示 Redis 所用的一块内存大小,Size 下面的 Swap 就表示这块 Size 大小的内存,有多少数据已经被换到磁盘上了,如果这两个值相等,说明这块内存的数据都已经完全被换到磁盘上了。

 

解决方法:

增加机器的内存,或者采用redis cluster分布式架构;

整理redis的内存碎片,redis 4.0版本提供了整理碎片的功能,但可能会导致 Redis 性能下降,因为这是在主线程中执行的,需要提前测试评估它对 Redis 的影响。。

# 开启自动内存碎片整理(总开关)
activedefrag yes

# 内存使用 100MB 以下,不进行碎片整理
active-defrag-ignore-bytes 100mb

# 内存碎片率超过 10%,开始碎片整理
active-defrag-threshold-lower 10
# 内存碎片率超过 100%,尽最大努力碎片整理
active-defrag-threshold-upper 100

# 内存碎片整理占用 CPU 资源最小百分比
active-defrag-cycle-min 1
# 内存碎片整理占用 CPU 资源最大百分比
active-defrag-cycle-max 25

# 碎片整理期间,对于 List/Set/Hash/ZSet 类型元素一次 Scan 的数量
active-defrag-max-scan-fields 1000

 

i. 网络带宽过载

原因:redis流量太大,导致带宽占满,响应返回客户端被阻塞或者接收redis操作请求被阻塞。

排查方式:监控Redis 机器的网络流量,在网络流量达到一定阈值时提前报警。

解决方法:

增加带宽;

一主多从,让流量负载均衡;

检查是否有热key,有则对热key冗余存储到其他redis节点并分担流量;




更多内容请关注微信公众号
zbpblog微信公众号

如果您需要转载,可以点击下方按钮可以进行复制粘贴;本站博客文章为原创,请转载时注明以下信息

张柏沛IT技术博客 > 面试官:redis变慢你会怎么排查和解决

热门推荐
推荐新闻