一文弄懂Redis慢查询 - Go语言中文社区

一文弄懂Redis慢查询


什么是Redis慢查询?

redis的"慢查询"与redis定义慢查询的时间阈值有关,Redis提供了slowlog-log-slower-thanslowlog-max-len两个配置,slowlog-log-slower-than指当redis命令的执行时间超过该值时,redis会将其记录在redis的慢查询日志中,slowlog-max-len表示记录的条数(超过时会只存储最新的slowlog-max-len条),slowlog-log-slower-than的默认值为10000us,也就是一般来说,redis命令执行时间超过10ms时,我们认为产生了慢查询,redis慢查询日志记录的是执行时间,没有慢查询,并不表示客户端没有超时问题,有可能网络传输有延迟,也有可能排队的命令比较多,导致redis查询也很慢。

产生慢查询有哪些原因?

在业务使用过程中,我们发现redis使用上出现慢查询的原因有主要以下几种:

  • 使用复杂度过高的命令
  • 大key问题
  • 集中过期

如何解决慢查询问题?

针对上述的原因,下面逐个给出导致慢查询的原因细节,并提出对应解决redis慢查询的有效方法。

使用复杂度过高的命令

负责的命令一般指O(N)以上的命令,比如sort、sunion、zunionstore聚合类的命令,或是O(N)类的命令,但是N的值由于业务原因特别大。
对于O(N)以上的命令,redis在操作内存数据时,耗时过高,会耗费更多的cpu资源,导致查询变慢。对于O(N)类的命令,由于N的值特别大Redis 一次需要返回给客户端的数据过多,更多时间花费在数据协议的组装和网络传输过程中。
除此之外,我们都知道,Redis 是单线程处理客户端请求的,如果你经常使用以上命令,那么当 Redis 处理客户端请求时,一旦前面某个命令发生耗时,就会导致后面的请求发生排队,对于客户端来说,响应延迟也会长。

针对此类原因,我们一般有以下两个原则:

  1. 尽量不使用O(N)以上复杂度的命令,某些数据排序或聚合操作,可以放在客户端处理。
  2. 执行O(N)命令时,保证 N 尽量的小(推荐 N <= 300 经验值),每次获取尽量少的数据,让 Redis 可以及时处理返回。

大key问题

所谓大key问题其实并不是值key过大,实则值key对应的value的值过大,此类问题在SET / DEL这类命令中也常出现慢查询。
首先我们要了解下redis写入与删除数据做了什么:

  • 写入数据:为该数据分配内存。
  • 删除数据:释放该数据对应的内存空间。
    很明显,当数据值比较大时,redis分配数据内存或释放空间内存都会比较耗时,针对大key问题,有以下建议:
  1. 尽量避免写入大key(不要写入无关的数据,数据实在过大可以进行拆分,通过多key存储)
  2. 如果你使用的 Redis 是 4.0 以上版本,用 UNLINK 命令替代 DEL,此命令可以把释放 key 内存的操作,放到后台线程中去执行,从而降低对 Redis 的影响
  3. 如果你使用的 Redis 是 6.0 以上版本,可以开启 lazy-free 机制(lazyfree-lazy-user-del = yes),在执行 DEL 命令时,释放内存也会放到后台线程中执行。

顺边提下检测大key的一个命令:

redis-cli -h 127.0.0.1 -p 6379 --bigkeys -i 0.01

输出结果会展示每种数据类型所占用的最大内存 / 拥有最多元素的 key 是哪一个,以及每种数据类型在整个实例中的占比和平均大小 / 元素数量。

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

这里我需要提醒你的是,当执行这个命令时,要注意 2 个问题:

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

集中过期

集中过期产生的慢查询很容易被忽略,可能我们在业务上线时,并没有发生慢查询,而是业务运行时在某个时间点总是突然发生慢查询。为什么集中过期会导致慢查询呢?我们首先了解下redis的两种过期策略:

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

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

如果此时需要过期删除的是一个 bigkey,那么这个耗时会更久。而且,这个操作延迟的命令并不会记录在慢日志中

因为慢日志中只记录一个命令真正操作内存数据的耗时,而 Redis 主动删除过期 key 的逻辑,是在命令真正执行之前执行的。

对于集中过期问题,有以下建议

  1. 避免集中过期,比如将过期时间随机化,添加一个随机的值,分散集中过期的key的过期时间,降低 Redis 清理过期 key 的压力
  2. 对于Redis 是 4.0 以上版本,可以开启 lazy-free 机制,当删除过期 key 时,把释放内存的操作放到后台线程中执行,避免阻塞主线程

总结

本文介绍了redis慢查询的含义,列举了业务使用种常导致redis慢查询的几个原因以及对应的解决办法,希望大家有所收获。

版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/weixin_44271683/article/details/125029966
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2023-01-03 18:01:41
  • 阅读 ( 458 )
  • 分类:Redis

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢