什么是Redis慢查询?

redis的"慢查询"与redis定义慢查询的时间阈值有关,Redis提供了slowlog-log-slower-than和slowlog-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 处理客户端请求时,一旦前面某个命令发生耗时,就会导致后面的请求发生排队,对于客户端来说,响应延迟也会长。

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

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

大key问题

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

  • 写入数据:为该数据分配内存。
  • 删除数据:释放该数据对应的内存空间。

很明显,当数据值比较大时,redis分配数据内存或释放空间内存都会比较耗时,针对大key问题,有以下建议:

  • 尽量避免写入大key(不要写入无关的数据,数据实在过大可以进行拆分,通过多key存储)
  • 如果你使用的 Redis 是 4.0 以上版本,用 UNLINK 命令替代 DEL,此命令可以把释放 key 内存的操作,放到后台线程中去执行,从而降低对 Redis 的影响
  • 如果你使用的 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 个问题:

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

集中过期

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

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

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

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

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

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

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

总结

到此这篇关于Redis慢查询的具体使用的文章就介绍到这了,更多相关Redis慢查询内容请搜索萤火虫技术以前的文章或继续浏览下面的相关文章希望大家以后多多支持萤火虫技术!

点赞(664) 打赏

评论列表 共有 0 条评论

暂无评论

微信小程序

微信扫一扫体验

立即
投稿

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部