Redis 性能优化

打印 上一主题 下一主题

主题 536|帖子 536|积分 1610

概述

当我们操作 Redis 发现耗时较长时,原因可能有两个:

  • 服务间存在网络延迟
  • Redis 服务本身存在问题
如果是第一种情况,那么所有服务都会发生网络延迟,只需要联系运维处理即可,这里主要讨论第二种情况

Redis 基准性能测试

基准性能指 Redis 在一台负载正常的机器上的最大响应延迟和平均响应延迟,我们可以找一台同配置的机器,与原机器比较基准性能,看看 Redis 是不是真的变慢了
从 Redis 2.8.7 开始,redis-cli 命令可以追加 –intrinsic-latency 选项,用于监测和统计某个时间段内 Redis 的最大延迟
  1. # 60 指的是测试时长为 60s,可以任意指定
  2. redis-cli -h 127.0.0.1 -p 6379 --intrinsic-latency 60
  3. Max latency so far: 1 microseconds.
  4. Max latency so far: 157 microseconds.
  5. Max latency so far: 173 microseconds.
  6. Max latency so far: 323 microseconds.
  7. Max latency so far: 324 microseconds.
  8. Max latency so far: 325 microseconds.
  9. Max latency so far: 334 microseconds.
  10. Max latency so far: 520 microseconds.
  11. Max latency so far: 527 microseconds.
  12. Max latency so far: 591 microseconds.
  13. Max latency so far: 1178 microseconds.
  14. Max latency so far: 2299 microseconds.
  15. Max latency so far: 2881 microseconds.
  16. Max latency so far: 4113 microseconds.
  17. 597018388 total runs (avg latency: 0.1005 microseconds / 100.50 nanoseconds per run).
  18. Worst run took 40926x longer than the average latency.
复制代码
从输出结果可以看出 60s 内最大延迟是 4113 微秒
使用以下命令,查看 Redis 的最小、最大、平均访问延迟
``shell
redis-cli -h 127.0.0.1 -p 6379 --latency
min: 0, max: 6, avg: 0.24 (6064 samples)
  1. 按 ctrl + c 结束命令,可见平均延迟为0.24ms,共统计了 6064 个样本数据
  2. 还可以使用以下命令,查看一段时间内 Redis 的最小、最大、平均访问延迟
  3. ```shell
  4. redis-cli -h 127.0.0.1 -p 6379 --latency-history -i 1
  5. min: 0, max: 1, avg: 0.29 (98 samples) -- 1.01 seconds range
  6. min: 0, max: 1, avg: 0.25 (96 samples) -- 1.00 seconds range
  7. min: 0, max: 1, avg: 0.28 (96 samples) -- 1.00 seconds range
  8. min: 0, max: 1, avg: 0.26 (96 samples) -- 1.00 seconds range
  9. min: 0, max: 1, avg: 0.27 (97 samples) -- 1.00 seconds range
  10. ...
复制代码
以上输出结果是,每隔 1 秒采样 Redis 的平均操作耗时,其结果分布在 0.25 ~ 0.29ms 之间
通过以上命令,我们可以在相同配置的服务器上测试比较 Redis 的基准性能,找到可能变慢了的 Redis 实例

原因分析

找到目标,接下来分析可能导致 Redis 变慢的因素
1. 使用慢日志查询耗时命令

Redis 提供了慢日志命令的统计功能,它记录了有哪些命令在执行时耗时比较久
查看 Redis 慢日志之前,你需要设置慢日志的阈值,例如,设置慢日志的阈值为 10ms,并且保留最近 128 条慢日志记录
在 redis.conf 中设置,重启 Redis 实例来生效
  1. slowlog-log-slower-than 10000
  2. slowlog-max-len 128
复制代码
也通过 CONFIG SET 命令动态设置
  1. CONFIG SET slowlog-log-slower-than 10000
  2. CONFIG SET slowlog-max-len 128
复制代码
执行 slowlog get 命令查询最近记录的慢日志,可以指定返回条数,不指定默认十条
  1. 127.0.0.1:6379> SLOWLOG get 5
  2. 1) 1) (integer) 32693       # 慢日志ID
  3.     2) (integer) 1593763337  # 执行时间戳
  4.     3) (integer) 5299        # 执行耗时(微秒)
  5.     4) 1) "LRANGE"           # 具体执行的命令和参数
  6.         2) "user_list:2000"
  7.         3) "0"
  8.         4) "-1"
  9. 2) 1) (integer) 32692
  10.     2) (integer) 1593763337
  11.     3) (integer) 5044
  12.     4) 1) "GET"
  13.         2) "user_info:1000"
  14. .....
复制代码
通过查看慢日志,我们就可以知道在什么时间点,执行了哪些命令比较耗时,一般原因有以下两个:

  • 使用复杂度过高的命令,如 SORT、SUNION、ZUNIONSTORE 等聚合类命令,
  • 查询返回数据量过大
2. 操作 bigkey

如果你查询慢日志发现,并不是复杂度过高的命令导致的,而都是 SET / DEL 这种简单命令出现在慢日志中,那么你就要考虑你的实例否写入了 bigkey
Redis 在写入数据时,需要为新的数据分配内存,相对应的,当从 Redis 中删除数据时,它会释放对应的内存空间。如果一个 key 写入的 value 非常大,那么 Redis 在分配内存时就会比较耗时。同样的,当删除这个 key 时,释放内存也会比较耗时,这种类型的 key 我们一般称之为 bigkey
Redis 提供了扫描 bigkey 的命令,用于扫描一个实例 bigkey 的分布情况
  1. redis-cli -h 127.0.0.1 -p 6379 --bigkeys
  2. ...
  3. -------- summary -------
  4. Sampled 829675 keys in the keyspace!
  5. Total key length in bytes is 10059825 (avg len 12.13)
  6. Biggest string found 'key:291880' has 10 bytes
  7. Biggest   list found 'mylist:004' has 40 items
  8. Biggest    set found 'myset:2386' has 38 members
  9. Biggest   hash found 'myhash:3574' has 37 fields
  10. Biggest   zset found 'myzset:2704' has 42 members
  11. 36313 strings with 363130 bytes (04.38% of keys, avg size 10.00)
  12. 787393 lists with 896540 items (94.90% of keys, avg size 1.14)
  13. 1994 sets with 40052 members (00.24% of keys, avg size 20.09)
  14. 1990 hashs with 39632 fields (00.24% of keys, avg size 19.92)
  15. 1985 zsets with 39750 members (00.24% of keys, avg size 20.03)
复制代码

  • 该命令使用 scan 方式对 key 进行统计,所以无需担心造成阻塞,但可能会对 Redis 实例造成一定的负担
  • 输出大概分为两部分,summary 之上的部分只是显示了扫描的过程,summary 部分给出了每种数据结构中最大的 Key
  • 统计的 bigkey 只有 string 类型是以字节长度来衡量,list、set、zset 等都是以元素个数作为衡量,不能说明其占用内存就一定多
针对 bigkey 导致延迟的问题,有两点可以优化:

  • 将 bigkey 拆分成多个小的 key,需要修改应用程序的代码
  • 使用压缩算法来减小对象的大小
  • 如果你使用的 Redis 是 4.0 以上版本,用 unlink 命令替代 del 命令异步删除
  • 如果你使用的 Redis 是 6.0 以上版本,可以开启 lazy-free 机制(lazyfree-lazy-user-del = yes),在执行 del 命令时,释放内存也会放到后台线程中执行
3. 集中过期

如果有大量的 key 在某个固定时间点集中过期,在这个时间点访问 Redis 时,就有可能导致延时变大,如果此时需要过期删除的是一个 bigkey,那么这个耗时会更久
解决办法是分散过期时间,可以为集中过期 key 增加一个随机过期时间。如果你使用的 Redis 是 4.0 以上版本,可以开启 lazy-free 机制(lazyfree-lazy-expire yes),当删除过期 key 时,把释放内存的操作放到后台线程中执行,避免阻塞主线程
4. 可用内存达到上限

Redis 可以设置最大可用内存(maxmemory),默认是实例内存。当 Redis 达到 maxmemory 时,Redis 必须清除部分数据,从而造成延迟。如果存储了 bigkey,那么耗时会更久
使用 info memory 命令查看 Redis 内存占用情况,比较 used_memory 和 maxmemory
  1. localhost:6379> info memory
  2. used_memory:692264  # redis 服务器分配的内存总量,也就是内部存储数据的内存占用量
  3. used_memory_human:676.04K  # 以可读形式返回 used_memory
  4. used_memory_rss:655336  # 从操作系统的角度返回 redis 进程占用的物理内存总量
  5. .....
  6. maxmemory:0  # redis 能使用的最大内存上限,0 表示没有上限
  7. maxmemory_human:0B  # 以可读形式返回 maxmemory
复制代码
可以调整 Redis 的内存淘汰策略,比如改为随机淘汰,随机淘汰速度要快很多,尽可能减少耗时。如果使用的是 Redis 4.0 以上版本,开启 layz-free 机制,把淘汰 key 释放内存的操作放到后台线程中执行(lazyfree-lazy-eviction = yes)
5. fork 耗时

当 Redis 开启了后台 RDB 和 AOF rewrite 后,需要主进程创建出一个子进程进行数据的持久化。主进程创建子进程,会调用操作系统提供的 fork 函数。而 fork 在执行过程中,主进程需要拷贝自己的内存页表给子进程,如果这个实例很大,那么这个拷贝的过程也会比较耗时
可以执行 info stats 命令获取到 latest_fork_usec 指标,表示 Redis 最近一次 fork 操作耗时,如果耗时很大,比如超过1秒,则需要做出优化调整
  1. localhost:6379> info stats
  2. ...
  3. latest_fork_usec:59477  # 上一次 fork 耗时,单位微秒
  4. ...
复制代码
推荐在低峰期进行备份,而对于丢失数据不敏感的业务(例如把 Redis 当做纯缓存使用)可以关闭持久化

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

南七星之家

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表