慢 SQL 不一定是 SQL 慢:一次 MySQL 连接池耗尽的故障定位

接口变慢时,很多人第一反应是去翻慢 SQL。这没错,但有一个前提:用户等待的时间真的花在 SQL 执行上。

这次 service-japi-order-x 就很典型。入口 P99 从 220ms 升到 5.2s,Tomcat busyThreads 从 90 涨到 260,错误日志开始出现:

复制代码
SQLTransientConnectionException:
Connection is not available, request timed out after 30000ms

如果只盯慢日志,很容易得出"数据库慢了"的结论。但真正的问题不是 SQL 普遍变慢,而是应用侧拿不到数据库连接。

第一眼像慢 SQL,但证据不支持

故障窗口里,最先拉的是入口、JVM 和数据库资源指标。结果有点反直觉:

指标 故障前 故障中
api-order-x P99 220ms 5200ms
Tomcat busyThreads 90 260
应用 CPU 平稳 约 45%
GC 平稳 无明显异常
数据库 CPU/IO 平稳 未打满

这组证据说明两件事。

第一,应用不是 CPU 打满,也不是 GC 把请求停住了。第二,数据库也没有表现出整体资源耗尽。如果是典型慢 SQL 扩散,通常会看到慢日志增加、数据库 CPU 或 IO 同步上升,或者执行计划明显退化。

但这里异常日志指向了另一个方向:请求不是"执行 SQL 慢",而是在 getConnection() 阶段等连接。

真正的异常在连接池等待

继续看 Hikari 指标,问题就清楚了:

复制代码
HikariPool-1 maximumPoolSize=80
active=80
idle=0
pending=360
connectionTimeout=30000ms
getConnection timeout count 持续增长

active=80 说明连接池已经打满,idle=0 说明没有空闲连接可借,pending=360 说明大量请求正在排队等连接。这个状态下,业务线程会被阻塞在获取连接上,最终拖高 Tomcat busyThreads。

所以入口耗时的主要组成不是 SQL 执行耗时,而是连接池等待时间。慢 SQL 只是候选原因之一,它可能导致连接长时间不归还,但不能直接等同于"SQL 本身慢"。

这个阶段最关键的判断是:连接为什么不归还?

常见方向有四个:

方向 证据入口 本次结论
连接泄漏 active 是否长期不回落、异常路径是否关闭连接 不像典型泄漏
慢 SQL 慢日志、执行计划、单次 SQL 耗时 普通 select 未明显变慢
锁等待 processlist、data_lock_waits 命中
事务过长 innodb_trx、事务持续时间 命中

连接泄漏的典型表现是故障解除后 active 仍长期不回落,或者连接数随时间单向增长。本次故障恢复后 active 能回落,所以它不是主线。

连接被谁占住了

数据库侧证据把问题收敛到了锁等待和长事务:

复制代码
information_schema.innodb_trx 显示 1 个事务持续 420s
performance_schema.data_lock_waits 中 table_order_x 存在锁等待
https://zhida.zhihu.com/search?content_id=278345624&content_type=Article&match_order=1&q=SHOW+PROCESSLIST&zhida_source=entity 中多条 update table_order_x 处于 Waiting for row lock
慢日志中普通 select 耗时未明显升高

这组数据很有决定性。

innodb_trx 里有一个持续 420s 的事务,说明某个连接长时间持有事务上下文。data_lock_waitsSHOW PROCESSLIST 又显示 table_order_x 上存在行锁等待,多条 update 卡在 Waiting for row lock

这就形成了闭环:

  1. 长事务持有行锁;
  2. 同表更新请求进入锁等待;
  3. 等待中的 SQL 持续占用数据库连接;
  4. 应用连接池 active 被占满;
  5. 新请求在 getConnection() 等待,直到 30s 超时;
  6. Tomcat 业务线程被拖住,接口 P99 和超时率上升。

所以这不是"SQL 语句突然慢了",而是"事务边界和锁等待把连接池耗尽了"。SQL 只是被堵在锁上,连接池才是用户侧延迟放大的关键资源。

止血和修复不能只调大连接池

这类问题最容易做错的动作,是把 maximumPoolSize 从 80 调到更大。短时间看,pending 可能下降;但如果锁等待还在,更多连接只会把更多请求送进数据库等待队列,扩大数据库端压力。

更合理的处理顺序是:

动作 目的
终止异常长事务或暂停相关入口 先释放被持有的行锁
对 api-order-x 做临时限流 防止等待连接的请求继续堆积
检查事务内逻辑 排除事务内 RPC、复杂计算、批量循环更新
收敛更新范围和顺序 降低行锁冲突概率
为连接池等待建立告警 让问题在接口超时前暴露

修复后验证不能只看接口恢复,还要同时看连接池和数据库锁状态:

复制代码
Hikari active 从 80 回落到稳定水位
pending 从 360 回落到 0
getConnection timeout 不再增长
SHOW PROCESSLIST 中 Waiting for row lock 消失
api-order-x P99 回到 220ms 附近

这些指标一起恢复,才说明"连接池耗尽"这个故障链路被切断。

可复用排查命令

排查 MySQL 连接池耗尽时,我一般按"应用连接池 -> 线程阻塞点 -> 数据库连接和锁 -> 事务边界"的顺序看。

复制代码
curl -s http://service-j/actuator/metrics/hikaricp.connections.active

看 active 是否接近 maximumPoolSize,以及是否长期不回落。

复制代码
curl -s http://service-j/actuator/metrics/hikaricp.connections.pending

看是否有大量线程等待连接。pending 上升通常比接口超时更早暴露问题。

复制代码
jstack <pid> > /tmp/thread.dump
grep -n "getConnection\\|HikariPool" /tmp/thread.dump

确认业务线程是否阻塞在获取数据库连接,而不是阻塞在本地锁、RPC 或 Redis。

复制代码
SHOW FULL PROCESSLIST;

看数据库端连接正在执行什么,是否大量处于 Waiting for row lockSending data 或长时间 Sleep。

复制代码
SELECT * FROM information_schema.innodb_trx\G

看是否存在持续时间异常的事务,重点关注事务开始时间、持锁状态和对应线程。

复制代码
SELECT * FROM performance_schema.data_lock_waits\G
SELECT * FROM performance_schema.data_locks\G

定位阻塞事务和被阻塞事务,确认锁等待发生在哪张表、哪个索引或哪类记录上。

复制代码
SHOW ENGINE INNODB STATUS\G

用于补充锁等待、死锁和事务状态细节,适合在锁等待视图信息不足时交叉验证。

复盘要点

  • 直接原因:table_order_x 上存在锁等待,部分更新语句长时间占用连接,导致 Hikari 连接池 active 打满。
  • 根因分析:异常长事务扩大了锁持有时间,连接池等待时间被入口请求感知,最终表现为接口 P99 和超时率上升。
  • 容易误判:接口慢不等于 SQL 慢,慢日志不明显时要检查连接池等待、锁等待和事务持续时间。
  • 预防措施:连接池 active、pending、timeout 必须纳入核心告警,并和接口 P99、数据库锁等待放在同一张排障看板。
  • 长期治理:控制事务边界,避免事务内调用外部依赖或做大批量循环更新;稳定性建设不能依赖无限扩资源,而要让关键资源可观测、可定位、可恢复。
相关推荐
IT 小阿姨(数据库)1 小时前
PostgreSQL 因主库表空间错乱_缺少软链接、从库配置问题,导致主从复制故障【修复过程】
数据库·postgresql
爱敲代码的小鱼1 小时前
springsecurity:
java·开发语言·数据库
秋田君1 小时前
QT_QT信号与槽机制
开发语言·数据库·qt
DFT计算杂谈2 小时前
vasp如何计算金属的铁电极化强度?
数据库
EterNity_TiMe_2 小时前
别让告警变成噪音:用Prometheus和Alertmanager搭一套可远程查看的监控系统
数据库·人工智能·ai·postgresql·prometheus·cpolar
不超限3 小时前
Postgresql 数据库 自增字段,Vastbase 无效的问题
数据库
阿坤带你走近大数据3 小时前
Oracle新老SQL写法对比
数据库·sql·oracle
还是鼠鼠3 小时前
AI掘金头条新闻系统 (Toutiao News)-缓存相关推荐新闻
后端·python·mysql·fastapi·web
努力努力再努力wz4 小时前
【高性能网络库与HTTP Server系列】:基于主从 Reactor 模型实现高性能 C++ 网络库与 HTTP Server
开发语言·网络·数据结构·数据库·c++·网络协议·http