Redis变慢原因排查

1、查看redis慢日志

----------------------1、使用复杂度过高的命令-----------------------------------
# 查看基准性能
# 60 秒内的最大响应延迟
docker exec -it redis redis-cli  --intrinsic-latency 60
Max latency so far: 1 microseconds.
Max latency so far: 7 microseconds.
Max latency so far: 8 microseconds.
Max latency so far: 5806 microseconds.
Max latency so far: 200755 microseconds.
Max latency so far: 209539 microseconds.
Max latency so far: 220647 microseconds.
Max latency so far: 340514 microseconds.
Max latency so far: 385149 microseconds.
这 60 秒内的最大响应延迟为 385149 微秒(385毫秒)。
# 查看一段时间内 Redis 的最小、最大、平均访问延迟:
docker exec -it redis redis-cli --latency-history -i 1
min: 0, max: 1, avg: 0.18 (98 samples) -- 1.01 seconds range
min: 0, max: 1, avg: 0.16 (97 samples) -- 1.00 seconds range
min: 0, max: 1, avg: 0.16 (97 samples) -- 1.00 seconds range
min: 0, max: 1, avg: 0.15 (97 samples) -- 1.00 seconds range
min: 0, max: 1, avg: 0.12 (98 samples) -- 1.01 seconds range
min: 0, max: 1, avg: 0.18 (98 samples) -- 1.01 seconds range
min: 0, max: 1, avg: 0.19 (97 samples) -- 1.00 seconds range
min: 0, max: 1, avg: 0.16 (97 samples) -- 1.00 seconds range
min: 0, max: 1, avg: 0.21 (98 samples) -- 1.01 seconds range
min: 0, max: 1, avg: 0.18 (98 samples) -- 1.01 seconds range
min: 0, max: 1, avg: 0.15 (98 samples) -- 1.01 seconds range
min: 0, max: 1, avg: 0.20 (97 samples) -- 1.00 seconds range
min: 0, max: 1, avg: 0.17 (98 samples) -- 1.01 seconds range
min: 0, max: 1, avg: 0.15 (98 samples) -- 1.01 seconds range
min: 0, max: 1, avg: 0.15 (98 samples) -- 1.01 seconds range
min: 0, max: 1, avg: 0.13 (98 samples) -- 1.01 seconds range
min: 0, max: 1, avg: 0.18 (98 samples) -- 1.01 seconds range
每间隔 1 秒,采样 Redis 的平均操作耗时,其结果分布在 0.15 ~ 0.25 毫秒之间。

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

# 命令执行耗时超过 5 毫秒,记录慢日志
CONFIG SET slowlog-log-slower-than 5000

# 只保留最近 500 条慢日志
CONFIG SET slowlog-max-len 500

slowlog get 【分页数】 # 获取全部的慢指令
slowlog get 5

127.0.0.1:6379> SLOWLOG get 5
1) 1) (integer) 32693       # 慢日志ID
   2) (integer) 1593763337  # 执行时间戳
   3) (integer) 5299        # 执行耗时(微秒)
   4) 1) "LRANGE"           # 具体执行的命令和参数
      2) "user_list:2000"
      3) "0"
      4) "-1"
     
#######################################
# 变慢原因
## 命令方面
# 1、使用时间复杂度过高的指令
# 2、返回数据过多

--------------------------1、操作bigkey-------------------------
# 查看bigkey指令
docker exec -it redis redis-cli --bigkeys -i 0.01
Sampled 829675 keys in the keyspace!
Total key length in bytes is 10059825 (avg len 12.13)

Biggest string found 'key:291880' has 10 bytes
Biggest   list found 'mylist:004' has 40 items
Biggest    set found 'myset:2386' has 38 members
Biggest   hash found 'myhash:3574' has 37 fields
Biggest   zset found 'myzset:2704' has 42 members

36313 strings with 363130 bytes (04.38% of keys, avg size 10.00)
787393 lists with 896540 items (94.90% of keys, avg size 1.14)
1994 sets with 40052 members (00.24% of keys, avg size 20.09)
1990 hashs with 39632 fields (00.24% of keys, avg size 19.92)
1985 zsets with 39750 members (00.24% of keys, avg size 20.03)

------------------------3、集中过期----------------------
Redis 的过期数据采用被动过期 + 主动过期两种策略:

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

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

   **这个主动过期 key 的定时任务,是在 Redis 主线程中执行的**

   如果在执行主动过期的过程中,出现了需要大量删除过期 key 的情况,那么此时应用程序在访问 Redis 时,必须要等待这个过期任务执行结束,Redis 才可以服务这个客户端请求。

------------------------4、实例内存达到上限---------------------
当 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+版本支持)

  • 使用复杂度过高的命令
    • 尽量不使用 O(N) 以上复杂度过高的命令,对于数据的聚合操作,放在客户端做
    • 执行 O(N) 命令,保证 N 尽量的小(推荐 N <= 300),每次获取尽量少的数据,让 Redis 可以及时处理返回
  • 操作bigkey
    • 业务应用尽量避免写入 bigkey
    • Redis 是 4.0 以上版本,用 UNLINK 命令替代 DEL,此命令可以把释放 key 内存的操作,放到后台线程中去执行
    • Redis 是 6.0 以上版本,可以开启 lazy-free 机制(lazyfree-lazy-user-del = yes),在执行 DEL 命令时,释放内存也会放到后台线程中执行
  • 集中过期
    • 集中过期 key 增加一个随机过期时间,把集中过期的时间打散,降低 Redis 清理过期 key 的压力
    • 可以开启 lazy-free 机制,当删除过期 key 时,把释放内存的操作放到后台线程中执行,避免阻塞主线程
  • 实例内存达到上限
    • 避免存储 bigkey,降低释放内存的耗时
    • 淘汰策略改为随机淘汰,随机淘汰比 LRU 要快很多(视业务情况调整)
    • 拆分实例,把淘汰 key 的压力分摊到多个实例上
    • 如果使用的是 Redis 4.0 以上版本,开启 layz-free 机制,把淘汰 key 释放内存的操作放到后台线程中执行(配置 lazyfree-lazy-eviction = yes)
  • fork耗时严重
  • 开启内存大页
  • 开启AOF
  • 绑定CPU
  • 使用Swap
  • 碎片整理
  • 网络带宽过载
  • 其他原因
上一篇:having 和where的区别


下一篇:Django ORM聚合查询,分组查询