DCS Redis 连接数突然爆满:按连接、慢查询与内存三层定位的 6 步
周三下午,业务方反馈接口整体变慢,随后 DCS 控制台出现连接数告警:实例的当前连接数逼近上限,新连接开始排队,应用日志里出现大量连接超时。Redis 本身没有宕机,也没有重启,上一周还好好的。最后查下来,问题出在三层叠加:一个新上线的定时任务没有复用连接池、两个慢命令长期占用连接、实例内存使用率同时抬升。本文把当时用的 6 步定位法写下来,照着走基本能把 DCS 连接数爆满缩小到一个具体原因。
一、问题现象:先确认是连接满了,而不是服务挂了
连接数爆满和 Redis 挂了,在业务侧看起来差不多,都是超时和报错,但原因完全不同。服务挂了是连不上,连接满了是连得上、但新连接拿不到。先分清这一步,后面才不会白扩规格。
最直接的办法是在同一台能连通 DCS 的 ECS 上复现一次,同时看连接数和响应:
redis-cli -h <DCS内网地址> -p 6379 -a <密码> --no-auth-warning INFO clients
redis-cli -h <DCS内网地址> -p 6379 -a <密码> --no-auth-warning PING
如果 PING 能返回 PONG,但 INFO clients 里的 connected_clients 接近 maxclients,说明实例还活着,问题在连接层;如果 PING 超时,再去看 DCS 控制台的实例状态和监控曲线,确认是不是实例本身异常。当时我们这一步的结果是 PING 正常、连接数 9800 多、接近上限,基本可以排除实例宕机,只看连接。
二、开始之前:先准备好这三样东西
动手改配置之前,先准备三样东西,避免边查边找权限。首先是 DCS 控制台的实例管理权限,至少能看到连接数、内存使用率、慢日志和参数配置;其次是出问题的时间点,包括告警第一次出现的时间、业务第一次报超时的业务名和接口名;最后是一台与 DCS 内网互通的 ECS,上面装好 redis-cli,用来跑定位命令。
另外注意一个原则:在确认原因之前,不要直接把连接数上限调大来“先恢复业务”。调大上限确实能让新连接先进来,但如果根因是连接泄漏,连接数还会继续涨,扩完很快又会满。正确的顺序是先定位,再针对性收敛连接、改完再验证。
三、分步操作:6 步定位连接数爆满
第 1 步:看连接数曲线,先确认涨的起点
先去 DCS 控制台看连接数监控曲线,找到连接数开始爬升的时间点,再对齐那个时间点前后上线的业务、定时任务和配置变更。如果曲线是缓慢爬升、从不回落,偏向连接泄漏;如果曲线是突然跳涨,偏向某个突发流量或批量任务。
当时我们的曲线是工作日下午两点左右突然跳涨,正好对上一个新定时任务的上线时间,所以先重点查这个任务,而不是先调参数。
第 2 步:看客户端列表,找出连接最多的来源
连接数高了,一定要先分来源,不能只看总数。用 CLIENT LIST 看每个连接的来源 IP、端口、空闲秒数和当前命令:
redis-cli -h <DCS内网地址> -p 6379 -a <密码> --no-auth-warning CLIENT LIST | awk '{print $2, $3}' | sort | uniq -c | sort -nr | head -20
重点看两类连接:一是同一个来源 IP 占了几千个连接,二是 idle 字段很大的连接,也就是建立后长期不用、也不释放的连接。当时有三台应用服务器各占了两千多个连接,而且大部分 idle 超过 1800 秒,明显是连接没有归还到池里。找到来源 IP 后,再去对应服务器上查进程和上线记录,确认是哪一个应用、哪一个服务。
第 3 步:查慢日志,确认有没有慢命令拖住连接
连接多还不够,还要看连接在等什么。慢命令会让连接长时间处于执行状态,池子被占满,新请求只能排队:
redis-cli -h <DCS内网地址> -p 6379 -a <密码> --no-auth-warning SLOWLOG GET 10
redis-cli -h <DCS内网地址> -p 6379 -a <密码> --no-auth-warning SLOWLOG LEN
在输出里看命令名、执行耗时和执行时间。重点关注 KEYS、HGETALL 大哈希、SMEMBERS 大集合、一次取几万条的 LRANGE 这类命令。它们本身不会直接把连接数打满,但会让已有连接迟迟不释放,放大连接数问题。当时慢日志里排前面的就是一个对十万级集合做 SMEMBERS 的命令,单次耗时超过 800 毫秒,高峰期每分钟跑几十次。
第 4 步:看大 Key 与内存,确认内存是否同时吃紧
连接满的时候,顺手看一眼内存。内存使用率如果也同时抬升,说明不只是连接的问题,可能是大 Key、过期策略或写入量同时出了问题。用 --bigkeys 快速扫一遍:
redis-cli -h <DCS内网地址> -p 6379 -a <密码> --no-auth-warning --bigkeys -i 0.1
redis-cli -h <DCS内网地址> -p 6379 -a <密码> --no-auth-warning INFO memory | grep -E 'used_memory_human|maxmemory_human|mem_fragmentation_ratio'
redis-cli -h <DCS内网地址> -p 6379 -a <密码> --no-auth-warning INFO keyspace
–bigkeys 扫的是抽样结果,生产上用 -i 0.1 放慢速度,避免扫库本身再加重负载。当时扫出来的最大 Key 是一个 180MB 的哈希,里面存了一个用户画像快照,每次 HGETALL 都要全量拉取。内存本身没有到上限,但这个大 Key 让慢命令更慢,形成了连锁反应。
第 5 步:核对连接池配置,比对三个数字
定位到来源应用后,再去核对它的连接池配置。三个数字要一起看:应用的连接池最大连接数、DCS 实例的 maxclients、业务高峰期的并发量。如果应用的连接池上限乘以实例数,已经接近或超过 DCS 的连接上限,连接数一到高峰就会满。
常见的连接池配置有 maxTotal、maxIdle 和超时时间。例如 Jedis 池里,把 maxTotal 设得过大、maxIdle 设得过大,都会让空闲连接长期留在实例上。再结合第 2 步的 idle 秒数看:如果空闲连接多、且 maxIdle 很大,基本可以确定池子没有及时回收。
用这条命令先看当前连接相关的参数,再和业务配置逐项比对:
redis-cli -h <DCS内网地址> -p 6379 -a <密码> --no-auth-warning CONFIG GET 'maxclients'
redis-cli -h <DCS内网地址> -p 6379 -a <密码> --no-auth-warning CONFIG GET 'timeout'
第 6 步:看是谁最后改了配置和代码
如果以上五步都没找到原因,就去看变更记录,确认连接数开始上涨的时间点前后,谁改过连接池配置、谁上线了新任务、谁改了缓存使用方式。很多时候连接爆满不是配置从一开始就错,而是某次变更之后,连接就没有再被正确释放了。
把第 1 步的时间点、第 2 步的来源 IP、第 3 步的慢命令和第 5 步的池配置拼在一起,通常就能圈定一个改动。当时我们最后锁定的是:新定时任务每次执行都新建一个客户端、执行完不关闭,而旧任务用的是共享连接池。
四、验证:改完要做这三件事
改完配置和代码后,不要只看连接数降下来就以为修好了。这三件验证都通过了,再算修好:第一,用 INFO clients 连续看 10 分钟,确认 connected_clients 稳定在合理区间,不再缓慢爬升;第二,用原来的慢命令和业务接口各跑一次,确认耗时恢复正常、且没有新的慢日志出现;第三,让业务从真实入口再跑一轮高峰请求,确认没有新的连接超时。
我们当时还补了一件事:把本次改动的连接池配置、大 Key 清单和慢命令清单存了一份记录,标清楚哪些 Key 是可以拆分的、哪些命令必须改成分页或扫描。后续再出问题,可以直接对照这张清单,而不用重新猜。
五、常见坑:五句话
一是“连接数满了就先扩规格”,不对,不先收敛连接,扩完还会再满。二是“用 MONITOR 长期抓包定位”,不对,MONITOR 本身很重,会让实例更慢,先用 SLOWLOG 和 CLIENT LIST。三是“只看连接总数”,不对,一定要分来源 IP 和空闲时长。四是“新连接超时就加连接池”,不对,池子越大、空闲连接越多,反而压得更满。五是“改完只在自己的机器上测一次”,不对,改完要在业务高峰期连续观察连接数曲线,并核对慢日志是否还在涨。
六、小结清单
- 复现:用 PING 和 INFO clients 确认是连接满了还是服务挂了,记录告警第一次出现的时间。
- 定位顺序:连接数曲线、客户端来源、慢日志、大 Key 与内存、连接池配置、变更记录。
- 收敛连接:修复连接泄漏的任务、统一复用连接池,空闲连接及时回收,不写无上限的新建客户端。
- 拆慢命令:大集合、大哈希不要全量拉取,改用分页、扫描或拆 Key,慢日志要清零复查。
- 验证:INFO clients 连续观察、慢命令复测、业务高峰复测三项全部通过;改动内容留档;下次只按这张清单先走一遍。
- 点赞
- 收藏
- 关注作者
评论(0)