关于redis:记一次Redis-On命令拖慢接口

52次阅读

共计 2073 个字符,预计需要花费 6 分钟才能阅读完成。

1. 景象

某天忽然收到运维告警音讯,反馈产线有接口在某一段时间慢了,随之性能优化的 JIRA 工作也开过去了,大抵内容如下:

API 告警 - Base Tomcat Accesslog: 
微服务: xxxapi 
接口: /xxxx/xxx POST 
在 [2022/xx/xx 10:42:00 ~ 10:43:00] 工夫窗口,RT > 阈值 (300ms) 产生 141 次。

公司外部对 API 的响应工夫要求是低于 300ms,超过的都属于有性能问题的接口,接下来就排查吧。

2. 排查

2.1. 查监控平台:接口速度

首先就去查看公司的 API 监控平台,确认这个接口确实在这段时间确实在响应速度上,有 100 屡次超过 300ms。

不过发现几个有意思的景象:

  1. 在那段时间前后,该接口都很失常,所以应该只是那段时间产生了什么。
  2. 不只是那一个接口超过 300ms 了,其实那段时间内,也有个别其余接口超过阈值。只不过因为那个接口的并发比拟高,很显著的被告警机制检测进去。

2.2. 查监控平台:全链路日志

尽管下面发现,可能不只是那一个接口的问题,但还是要仔细分析一下那个接口的全链路日志。

通过调用链路图,能够显著的发现,次要的耗时都在 redis 命令的执行阶段。又找了那段时间,其余的几个接口,后果都是一样的。

接下来就应该看看对应接口的代码,是不是执行 redis 命令时,有什么影响性能的问题。

2.3. 剖析代码

找到对应接口的代码,代码中惟一用到 redis 的命令是 HMGET key field1 [field2] ,剖析后排除该代码的问题:

  1. 代码中就那一次的 redis 调用,HMGET 不是耗时的命令(不在后文 O(n) 命令之列),且 field 的数量是可枚举的,最多也就几十个,不会有多耗时。
  2. 该接口因为之前预计到并发高,所以上线之前通过压测,上万 QPS 齐全不成问题,不会在这里栽跟头。

2.4. 查监控平台:redis 连接池

排除了代码层面上该接口的问题,那只能从运维侧看看执行 redis 命令慢的问题了,正好监控平台上有 mysql、redis 连接池的监控。不看不晓得,一看吓一跳。

在出问题时间段,redis 连接数霎时飙升,间接超过了最大闲暇连接数。在工夫节点上,和产线呈现慢响应的工夫齐全吻合。

能够揣测出,这段时间可能执行了某些奇怪的命令,造成了 redis 阻塞,其余 redis 申请只能频繁的创立新的连贯,从而导致 redis 连接池中连接数飙升。

想要揣测那段时间 redis 经验了什么,只能托付于运维的共事了。

2.5. 运维:查 redis 慢申请

通过从运维共事导出来的,那段时间 redis 执行过的慢申请日志,果然和料想的一样。那段时间执行了大量的慢申请命令,尤其是一段 lrange 命令,每次执行工夫都超过了 300ms,一分钟执行了几百次。

通过具体的 redis 命令,倒推找到了对应的代码,该代码对应的其实是一段对应用户登录后,通过 MQ 异步刷新用户权限的逻辑。因为是异步生产,所以就算执行的慢,也并不会因为响应工夫阈值而被告警。

再联合 ELK 中无关该接口的调用日志,能够还原出问题产生的残缺过程。

2.6. 场景还原

lrange 命令独自执行还行,可那段时间在产线有大批量用户的登录并发,导致该慢申请命令阻塞住了 redis,大量 redis 申请都在阻塞排队,redis 连接池中连接数也在一直扩大。

所以说,那段时间所有的 redis 申请命令,都会因为阻塞而提早执行实现。告警的那个接口属于“躺枪”,因为它调用的并发高,所以体现显著,被告警程序当典型抓住了。

如果没有这一步步排查,很难置信一个 API 的告警,是因为另外一个毫无关系的 API 影响的。

3. 解决

3.1. 解决

lrange 是工夫复杂度为 O(n) 的命令,原则上是不应该被频繁调用的。剖析代码上下文逻辑,其实只是为了全量存储和读取 List 类型数据。齐全能够用 redis 中的 String 代替 List。

对于 redis 中 keys *flushdbflushall 等超耗时的命令,能够间接作为禁用命令,保护进 redis.conf 的禁用命令清单中。

但像 lrange 这种能够用,但要严格扫视利用场景的命令,就须要研发人员特地留神。上面整顿了一下 redis 常见的 O(n) 命令。

3.2. O(n) 命令

  • String: MSET、MSETNX、MGET
  • List: LPUSH、RPUSH、LRANGE、LINDEX、LSET、LINSERT
  • Hash: HDEL、HGETALL、HKEYS/HVALS
  • Set: SADD、SREM、SRANDMEMBER、SPOP、SMEMBERS、SUNION/SUNIONSTORE、SINTER/SINTERSTORE、SDIFF/SDIFFSTORE
  • Sorted Set: ZADD、ZREM、ZRANGE/ZREVRANGE、ZRANGEBYSCORE/ZREVRANGEBYSCORE、ZREMRANGEBYRANK/ZREMRANGEBYSCORE

3.3. 其余

因为 Spring 中 Lettuce 连接池的 bug,公司要求都改回 Jedis 连接池。感觉在该问题的防止上,Jedis 连接池应该也有优化空间。待后续实际后再分享。

正文完
 0