Redis--基础知识点--28--慢查询相关

发布于:2025-05-30 ⋅ 阅读:(19) ⋅ 点赞:(0)

1 慢查询的原因

1.1 非命令数据相关原因

1.1.1 网络延迟

原因:客户端与 Redis 服务器之间的网络延迟可能导致客户端感知到的响应时间变长。

解决方案:优化网络环境

排查:

1.1.2 CPU 竞争

原因:Redis 是单线程的,如果服务器的 CPU 资源被其他进程占用,可能导致 Redis 命令执行变慢。

解决方案:停掉其它无用的进程,或增加硬件设备

排查:

  • info cpu: 查看redis 使用cpu情况

1.1.3 连接数不足

原因:连接数设置过小。如果QPS较大,然后Redis链接池的最大连接数设置的却比较小,那么获取链接的时间就会增加。

解决方案:调整连接数

排查:

  • info clients:查看连接数等信息
  • client list: 查看所有连接
  • client kill ip:port : 杀死指定连接

1.1.4 内存不足

原因:当 Redis 内存达到 maxmemory 后,每次写入新的数据之前,Redis 必须先从实例中踢出一部分数据,让整个实例的内存维持在 maxmemory 之下,然后才能把新数据写进来。这个踢出旧数据的逻辑也是需要消耗时间的,而具体耗时的长短,要取决于配置的淘汰策略。

解决方案:

  • 1 增加最大内存限制
  • 2 调整过期时间与淘汰策略,让非热点数据及时清除

排查:

  • info memory

1.1.5 内存碎片整理耗时开销大

原因:Redis 的数据都存储在内存中,当我们的应用程序频繁修改 Redis 中的数据时,就有可能会导致 Redis 产生内存碎片。内存碎片会降低 Redis 的内存使用率。
具体原因看Redis内存碎片详解中 2

解决方案:

排查:

1.1.6 Fork耗时严重

原因:当 Redis 开启了后台 RDB 和 AOF rewrite 后,在执行时,它们都需要主进程创建出一个子进程进行数据的持久化。主进程创建子进程,会调用操作系统提供的 fork 函数。而 fork 在执行过程中,主进程需要拷贝自己的内存页表给子进程,如果这个实例很大,那么这个拷贝的过程也会比较耗时。而且这个 fork 过程会消耗大量的 CPU 资源,在完成 fork 之前,整个 Redis 实例会被阻塞住,无法处理任何客户端请求。如果此时你的 CPU 资源本来就很紧张,那么 fork 的耗时会更长,甚至达到秒级,这会严重影响 Redis 的性能。

1.2 命令数据相关的原因

1.2.1 复杂命令

原因:

  • KEYS:遍历所有键,时间复杂度为 O(N),可能导致性能瓶颈。
  • SORT:对数据进行排序,时间复杂度为 O(N log N),当数据量较大时会显著影响性能。
  • SUNION、ZUNIONSTORE:聚合类命令,当操作的数据量较大时会消耗较多 CPU 资源。

解决方案:

  • 尽量不使用 O(N) 以上复杂度过高的命令,对于数据的聚合操作,放在客户端做。 执行 O(N) 命令,保证 N 尽量的小(推荐 N <= 300),每次获取尽量少的数据,让 Redis 可以及时处理返回。比如: 尽量避免使用 KEYS、SORT 等命令,改用 SCAN 或在客户端完成数据聚合。
  • 批量操作:减少网络往返,但避免单次批量过大。比如:一次性删除百万级成员的集合;对于多个操作,使用MGET、MSET 等批量命令、管道、分页命令lrange 减少网络往返。

排查:

  • CONFIG SET slowlog-log-slower-than 1000
  • SLOWLOG GET
  • 3 查看相应的key

1.2.2 大KEY

原因:BigKey 是指存储了大量数据的 Key(如大型列表、集合或哈希)。对 BigKey 的操作(如 DEL、SET)可能会导致 Redis 阻塞,因为这些操作需要处理大量的数据。
示例:一个存储了 100 万条数据的列表(List),执行 DEL 命令时可能会阻塞 Redis 服务。
具体原因:Redis 大 Key:别让你的 Redis 变成“胖子”! 中二

解决方案:
Redis 大 Key:别让你的 Redis 变成“胖子”! 中五

排查:

1.2.3 缓存集中过期

原因:为什么集中过期会导致 Redis 延迟变大?这就需要我们了解 Redis 的过期策略是怎样的。Redis 的过期数据采用被动过期 + 主动过期两种策略:

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

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

解决方案:

  • 1 集中过期 key 增加一个随机过期时间,把集中过期的时间打散,降低 Redis 清理过期 key 的压力;
  • 2 如果你使用的 Redis 是 4.0 以上版本,可以开启 lazy-free 机制,当删除过期 key 时,把释放内存的操作放到后台线程中执行,避免阻塞主线程。

排查:
复杂命令 和 大key 是一般出现比较多的导致慢查询的原因,但是如果慢查询总是在特定的时间点出现,或者每隔一段时间出现一次,那么就有可能是集中过期,可以通过监控确认下。

1.2.4 lua脚本执行时间过长

原因:可能lua脚本逻辑不够优化

解决方案:优化逻辑

  • 1 CONFIG SET slowlog-log-slower-than 1000
  • 2 SLOWLOG GET
  • 3 查看相应的脚本

3 监控工具

除了上边的各种排查方案中的命令,还可以使用第三方监控工具如 Prometheus 和 Grafana。

4 慢查询配置相关命令

slowlog len

  • 获取慢查询日志的长度

slowlog reset

  • 命令清理慢查询日志

slowlog get [N]

  • 获取最近的 N 条慢查询日志(不填 N 默认返回全部)

config set slowlog-log-slower-than 1000

  • 指定命令执行时长的阈值,执行命令的时长超过这个阈值时就会被记录下来

config set slowlog-max-len 1200

  • 指定慢查询日志最多存储的条数

config rewrite

  • 配置写入配置文件