跳至主要內容

Redis性能问题排查

xw大约 5 分钟数据库Redis

常见问题

复杂度过高的命令排查

  • 开启慢日志

    # 命令执行耗时超过 1 毫秒,记录慢日志
    CONFIG SET slowlog-log-slower-than 1000
    # 只保留最近 500 条慢日志
    CONFIG SET slowlog-max-len 500
    
  • 查看慢日志

    ## 5为慢日志条数
    SLOWLOG GET 5
    

    结果如下:

    image-20220921154918717

bigkeys排查

  • 命令

     ## -i 扫描过程中每次扫描后休息的时间间隔,单位是秒
     redis-cli -h 127.0.0.1 -p 6379 --bigkeys -i 0.01
    

    image-20220921155552675

  1. 对线上实例进行 bigkey 扫描时,Redis 的 OPS 会突增,为了降低扫描过程中对 Redis 的影响,最好控制一下扫描的频率,指定 -i 参数即可,它表示扫描过程中每次扫描后休息的时间间隔,单位是秒
  2. 扫描结果中,对于容器类型(List、Hash、Set、ZSet)的 key,只能扫描出元素最多的 key。但一个 key 的元素多,不一定表示占用内存也多,你还需要根据业务情况,进一步评估内存占用情况

集中过期

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

注意

定时任务删除过期key不会出现在慢日志中,但我们的应用程序却感知到了延迟变大,其实时间都花费在了删除过期 key 上,这种情况需要尤为注意。

解决方案:

  • 增加随机过期时间

  • 4.0以上开启lazy-free机制,当删除过期 key 时,把释放内存的操作放到后台线程中执行,避免阻塞主线程

    lazyfree-lazy-expire yes
    

fork耗时严重

操作 Redis 延迟变大,都发生在 Redis 后台 RDB 和 AOF rewrite 期间,fork 在执行过程中,主进程需要拷贝自己的内存页表给子进程,如果这个实例很大,那么这个拷贝的过程也会比较耗时。

如何判断是否是fork操作导致耗时增加,可以使用info命令进行查看。

image-20220921161522726

使用swap

Swap:操作系统为了缓解内存不足对应用程序的影响,允许把一部分内存中的数据换到磁盘上,以达到应用程序对内存使用的缓冲,这些内存数据被换到磁盘上的区域,就是Swap。

Redis如果从Swap获取数据,需要先访问磁盘,会有可能造成极大的操作延时。

排查:

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

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

image-20220921162340422

内存碎片

内存碎片形成有内部原因和外部原因:

  • 内部原因:内存分配器的分配策略决定操作系统无法做到“按需分配”。

    • Redis使用libc、jemalloc、tcmalloc多种内存分配器来分配内存,默认使用jemalloc。

    • 内存分配器是按照固定大小来分配内存空间,不是完全按照应用程序申请的内存大小来分配。

      以jemalloc为例,是按照一系列固定的大小划分内存空间,例如8字节、16字节、32字节、...、2KB、4KB等。当程序申请的内存最接近某个固定值时,jemalloc就会给它分配相应大小的空间。

  • 外部原因:键值对大小不一样,并且键值对可以被修改和删除。

    • Redis申请内存空间分配时,对于大小不一的内存空间需求,内存分配器按照固定大小分配内存空间,分配的内存空间一般都会比申请的内存空间大一些,这会产生一定的内存碎片。
    • 键值对会被修改和删除,会导致空间的扩容和释放。

使用info命令查看,计算

INFO memory
# Memory
used_memory:350458970752
used_memory_human:326.39G
used_memory_rss:349066919936
used_memory_rss_human:325.09G
…
mem_fragmentation_ratio:1.00
  • used_memory:表示Redis为了保存数据实际申请使用的内存空间。

  • used_memory_rss:表示操作系统实际分配给Redis的物理内存空间,其中包含了内存空间碎片。

  • mem_fragmentation_ratio

    :表示Redis当前的内存碎片率。

    计算公式:mem_fragmentation_ratio=used_memory_rss/used_memory

    • mem_fragmentation_ratio大于等于1但小于等于1.5,这种情况是合理的。
    • mem_fragmentation_ratio大于1.5,表明内存碎片率已经超过了50%。

解决方案如下:

  1. 如果你使用的是 Redis 4.0 以下版本,只能通过重启实例来解决
  2. 如果你使用的是 Redis 4.0 版本,它正好提供了自动碎片整理的功能,可以通过配置开启碎片自动整理

但是,开启内存碎片整理,它也有可能会导致 Redis 性能下降。

原因在于,Redis 的碎片整理工作是也在主线程中执行的,当其进行碎片整理时,必然会消耗 CPU 资源,产生更多的耗时,从而影响到客户端的请求。

配置如下:

# 开启自动内存碎片整理(总开关)
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

排查图

img

参考文章