redis-变慢

2021-02-25  本文已影响0人  麦大大吃不胖

by shihang.mai

Redis 作为优秀的内存数据库,其拥有非常高的性能,单个实例的 OPS 能够达到 10W 左右。但也正因此如此,当我们在使用 Redis 时,如果发现操作延迟变大的情况,就会与我们的预期不符

1. 基准测试

判断redis是否变慢,肯定要先准星基准测试,基准测试可以用以下命令

# 测试出这个实例 60 秒内的最大响应延迟
redis-cli -h 127.0.0.1 -p 6379 --intrinsic-latency 60
# 每间隔 1 秒,采样 Redis 的平均操作耗时
redis-cli -h 127.0.0.1 -p 6379 --latency-history -i 1

2. redis变慢判断

  1. 在相同配置的服务器上,测试一个正常 Redis 实例的基准性能
  2. 找到你认为可能变慢的 Redis 实例,测试这个实例的基准性能
  3. 如果你观察到,这个实例的运行延迟是正常 Redis 基准性能的 2 倍以上,即可认为这个 Redis 实例确实变慢了

3 redis慢原因

3.1 使用复杂度过高的命令

  1. 设置慢日志阈值
# 命令执行耗时超过 5 毫秒,记录慢日志
CONFIG SET slowlog-log-slower-than 5000
# 只保留最近 500 条慢日志
CONFIG SET slowlog-max-len 500
  1. 查看慢日志
# 获取5条慢记录
SLOWLOG get5
  1. 复杂度高的命令导致慢的原因

如果你的应用程序操作 Redis 的 OPS (每秒操作的次数)不是很大,但 Redis 实例的 CPU 使用率却很高,那么很有可能是使用了复杂度过高的命令导致的

  1. 解决方案

3.2 操作bigkey

bigKey定义:如果一个 key 写入的 value 非常大,那么 Redis 在分配内存时就会比较耗时。同样的,当删除这个 key 时,释放内存也会比较耗时,这种类型的 key 我们一般称之为 bigkey

  1. 查看bigKey在实例中分布情况
redis-cli -h 127.0.0.1 -p 6379 --bigkeys -i 0.01

使用这个命令的原理,就是 Redis 在内部执行了 SCAN 命令,遍历整个实例中所有的 key,然后针对 key 的类型,分别执行 STRLEN、LLEN、HLEN、SCARD、ZCARD 命令,来获取 String 类型的长度、容器类型(List、Hash、Set、ZSet)的元素个数

  1. 解决方案

lazy-free的使用分为2类

主动删除
UNLINK,与DEL命令对应

被动删除(TTL删除和maxmemory key驱逐淘汰删除)

3.3 key集中过期

  1. 现象
    变慢的时间点很有规律,例如某个整点,或者每间隔多久就会发生一波延迟
  2. 变慢原因
    Redis 的过期数据采用惰性删除 + 定时主动删除两种策略.这个定时主动删除过期 key 的任务,是在 Redis 主线程中执行的
    如果在执行主动过期的过程中,出现了需要大量删除过期 key 的情况,那么此时应用程序在访问 Redis 时,必须要等待这个过期任务执行结束,Redis 才可以服务这个客户端请求.
  3. 查找
    在代码中查找设置过期时间的关键字
  4. 方案

3.4 实例内存达到上限

  1. 变慢原因
    当 Redis 内存达到 maxmemory 后,每次写入新的数据之前,Redis 必须先从实例中踢出一部分数据,让整个实例的内存维持在 maxmemory 之下,然后才能把新数据写进来
    一般最常使用的是 allkeys-lru、volatile-lru 淘汰策略,它们的处理逻辑是,每次从实例中随机取出一批 key(这个数量可配置),然后淘汰一个最少访问的 key,之后把剩下的 key 暂存到一个池子中,继续随机取一批 key,并与之前池子中的 key 比较,再淘汰一个最少访问的 key。以此往复,直到实例内存降到 maxmemory 之下
    Redis 的淘汰数据的逻辑与删除过期 key 的一样,也是在命令真正执行之前执行的,也就是说它也会增加我们操作 Redis 的延迟
  2. 解决方案

3.5 fork耗时严重

  1. 现象
    操作 Redis 延迟变大,都发生在 Redis 后台 RDB 和 AOF rewrite 期间
  2. 变慢原因
  1. 查找
INFO
# 上一次 fork 耗时,单位微秒
latest_fork_usec:59477
  1. 解决方案

3.6 开启内存大页

Linux 内核从 2.6.38 开始,支持了内存大页机制,该机制允许应用程序以 2MB 大小为单位,向操作系统申请内存页(以前是4K),应用程序每次向操作系统申请的内存单位变大了,但这也意味着申请内存的耗时变长

3.6.1 变慢原因

当 Redis 在执行后台 RDB 和 AOF rewrite 时,采用 fork 子进程的方式来处理。但主进程 fork 子进程后,此时的主进程依旧是可以接收写请求的,而进来的写请求,会采用 Copy On Write(写时复制)的方式操作内存数据

主进程在拷贝内存数据时,这个阶段就涉及到新内存的申请,如果此时操作系统开启了内存大页,那么在此期间,客户端即便只修改 10B 的数据,Redis 在申请内存时也会以 2MB 为单位向操作系统申请,申请内存的耗时变长,进而导致每个写请求的延迟增加,影响到 Redis 性能

3.6.2 查找

cat /sys/kernel/mm/transparent_hugepage/enabled
值:ALWAYS或者NEVER

3.6.3 解决方案

echo never > /sys/kernel/mm/transparent_hugepage/enabled

3.7 开启AOF(没理解)

  1. 变慢原因
    当 Redis 后台线程在执行 AOF 文件刷盘时,如果此时磁盘的 IO 负载很高,那这个后台线程在执行刷盘操作(fsync系统调用)时就会被阻塞住。
    此时的主线程依旧会接收写请求,紧接着,主线程又需要把数据写到文件内存中(write 系统调用),但此时的后台子线程由于磁盘负载过高,导致 fsync 发生阻塞,迟迟不能返回,那主线程在执行 write 系统调用时,也会被阻塞住,直到后台线程 fsync 执行完成后,主线程执行 write 才能成功返回
  2. 解决方案
# AOF rewrite 期间,AOF 后台子线程不进行刷盘操作
# 相当于在这期间,临时把 appendfsync 设置为了 none
no-appendfsync-on-rewrite yes

3.8 使用Swap

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

  1. 现象
    发现 Redis 突然变得非常慢,每次的操作耗时都达到了几百毫秒甚至秒级,那此时就需要检查 Redis 是否使用到了 Swap,在这种情况下 Redis 基本上已经无法提供高性能的服务了
  2. 变慢原因
    当内存中的数据被换到磁盘上后,Redis 再访问这些数据时,就需要从磁盘上读取,访问磁盘的速度要比访问内存慢几百倍
  3. 查找
# 先找到 Redis 的进程 ID
$ ps -aux | grep redis-server

# 查看 Redis Swap 使用情况
$ cat /proc/$pid/smaps | egrep '^(Swap|Size)'
  1. 解决方案

3.9 碎片整理

这个开启后会导致redis的性能下降

参考:https://mp.weixin.qq.com/s/8Af9nOe_05RVxVgXs6JmZA

上一篇 下一篇

猜你喜欢

热点阅读