数据库连接池被占满,三分钟从监控定位到具体 SQL
线上突然大量报错,日志里清一色是获取连接超时。这时候先别急着重启,重启只是把所有等待中的请求一起掐掉,十分钟后大概率复发。按下面四步走,通常三分钟内能锁定是哪类 SQL 把池子耗干了。
第一步,确认是「池子小」还是「有人不还」。
看连接池监控的三个数:活跃连接数、空闲连接数、等待队列长度。活跃数长时间顶在 maxPoolSize 上、等待队列持续增长,说明池子被占满;如果活跃数不高但等待队列很长,那是获取连接的路径本身被卡住,比如网络抖动或者数据库端连接建立慢,两者的处理方式完全不同。
第二步,按状态给连接分组。
在数据库侧查 information_schema.processlist 或者 pg_stat_activity,按 state 和执行时长排序。重点看两类:执行时间很长的(慢查询占着连接不放),以及 state 是 Sleep 但时间很长的(连接借出去没还,典型是应用侧忘了关闭或者事务没提交)。
第三步,把长事务和慢 SQL 拎出来。
MySQL 用 sys.innodb_trx 看事务持续时间和持有的锁,长事务不提交,它占的连接不会释放,后面的请求只能排队。慢 SQL 则直接看执行计划,常见的坑是索引失效后全表扫描,一条语句从毫秒级变成几十秒,几个并发就把池子占满。
第四步,看连接池配置有没有被误改。
有几种典型配置会放大问题:最小空闲连接设得太小,流量突增时要临时建连;连接最大存活时间比数据库侧 wait_timeout 还长,会拿到已经被服务端断掉的死连接;借出连接后没有设置获取超时,请求会无限期等待,把线程也一起拖死。
治标和治本要分开做。
治标是临时把池子调大、把慢 SQL 下掉或者在入口做限流,先把业务救回来。治本得落到三件事上:给所有慢 SQL 加监控和阈值告警;事务范围尽量收窄,别在事务里调用外部接口;连接池的获取超时和最大存活时间按数据库实际参数配,别用默认值。
最后提醒一句,连接池打满往往不是连接池的问题,是上游某个接口变慢导致的连锁反应。只盯着池子调参数,下次换个入口还会再来一次。
- 点赞
- 收藏
- 关注作者
评论(0)