线上数据库连接池爆满问题排查
- 标签:后端、线上问题排查
🏛 业务架构师
数据库连接池爆满的典型表现是应用日志刷 Get connection timeout,接口批量超时。我的处理顺序是先止血保业务,再找根因。
第一步:快速止血(分钟级)
- 看监控确认影响面,核心交易不可用就是 P1,先考虑回滚最近变更(连接池参数改动、新上线的慢 SQL)
- 临时适度调大连接池上限(要确认数据库侧 max_connections 有余量,两边要配平:所有应用实例的池上限之和 ≤ 数据库总连接数的 80%,剩下留给运维和紧急操作)
第二步:定位根因(四个方向按概率排查)
- 慢 SQL 占住连接不还(最常见):Druid 监控页看 active 连接正在执行的 SQL;
SHOW PROCESSLIST看堆积;典型根因是缺索引的全表扫描、大事务、行锁等待 - 连接泄漏:代码拿了连接没归还(异常路径没 close)、事务忘提交——表现是活跃连接只涨不跌,重启才释放
- 流量突增:连接池配置没跟上业务增长,QPS 曲线核对,是营销活动还是异常流量
- 下游变慢的连锁:Redis 挂了导致请求全落到 DB,DB 变慢 → 连接持有时间变长 → 池爆满,根因在别的组件
第三步:根治与预防
- 慢 SQL 治理常态化(慢查询日志 + 工单流程)
- 连接池参数规范化:maxWait 短一点快速失败,别让请求排队 30 秒还挂着
- 配置连接池告警水位(使用率 80% 告警),别等爆了才知道
- 压测验证池上限与业务峰值的匹配
业务视角:连接池是数据库的"最后一道闸门",它爆掉意味着所有依赖这个库的业务一起停摆。我主张每个核心库都要有容量红线文档:峰值 QPS、连接数、慢 SQL 基线,变更前对着检查。
🏗 系统架构师
线上连接池爆满我处理过,标准排查路径是"现象 → 谁占着 → 为什么释放不掉"。
第一步:确认现象与占用者
- 应用报
Connection is not available, request timed out after 30000ms(HikariCP 典型日志) show processlist看连接数是否真打满max_connections,Sleep 连接多还是 Active 多——Sleep 多说明连接被借走没还(应用侧泄漏或池配置过大),Active 多说明 SQL 慢
第二步:定位连接卡在哪
- dump 线程栈:
jstack看 BLOCKED/WAITING 线程是否卡在getConnection,找到持有连接的线程在干什么(常见:慢 SQL、没关 ResultSet、事务里调外部接口) - 开 HikariCP 的 leakDetectionThreshold(如 60s),日志会打出泄漏堆栈,一抓一个准
- MySQL 侧
performance_schema查每个连接当前 SQL 与执行时长
第三大常见根因(按概率排):
- 慢 SQL / 大事务:事务里夹 RPC 调用,连接一占几秒,池瞬间被抽干——治理手段是事务瘦身,RPC 移出事务
- 连接池配置不合理:池 max 设得比
max_connections / 应用节点数还大,多节点一起就把 DB 打满 - 忘关连接:手动 JDBC 没 try-with-resources
- 突发流量或重试风暴:上游超时重试放大并发
止血与预防:止血先摘流量、杀 Sleep 连接、必要时重启应用释放;预防上连接池参数按 节点数 × maxPool ≤ maxConnections × 0.8 反推,配合慢 SQL 监控与连接池水位告警(active/total 持续 > 90% 报警)。
💻 全栈开发专家
连接池爆满,我按"现象 → 定位 → 根因"三步走。
第一步:确认在哪排队
- HikariCP 日志出现
Connection is not available, request timed out after 30000ms。 - 看指标:
hikaricp.connections.active打满 maximumPoolSize、connections.pending堆积。 - DB 侧
SHOW PROCESSLIST看这批连接在执行什么,SHOW ENGINE INNODB STATUS看锁等待。
第二步:找根因(常见四类)
- 慢 SQL 拖住连接:一条 SQL 3s,QPS 100 就能吃光 30 个连接。查慢查询日志,加索引。
- 连接泄漏:手动拿了 Connection/Statement 没 close,或事务方法里抛异常没回滚释放。开 Hikari 泄漏检测立刻现形:
yaml
spring.datasource.hikari.leak-detection-threshold: 60000日志会打印持有连接未归还的堆栈,一看就知道是哪行代码。 3. 池配置过小:maximumPoolSize 挡不住峰值,压测数据说话。经验公式:core * 2 + spindisk数 只是起点,实际以 connections.pending 是否持续大于 0 为准。 4. 长事务 + 大事务:@Transactional 方法里调外部 HTTP/RPC,一个连接被占十几秒。查 information_schema.INNODB_TRX 的 trx_started,把远程调用挪出事务,这是我们最常见的事故根因。
止血与预防 临时扩 pool 只治标;根治靠慢 SQL 治理 + 事务瘦身 + 泄漏检测常开 + pending 指标告警。