线上数据库连接池打满:从应急止损到架构根治的完整方案

深夜告警:连接池活跃连接 100%,等待队列排到几千,业务请求全线超时。
这篇文章不讲「调大 maxActive」那种把问题往后拖的做法,而是完整聊一遍连接池耗尽的治理体系。

一、先分清故障类型,再动手

很多团队遇到连接池打满,第一反应是重启服务、调大最大连接数、加告警。这三板斧能应急,但治不了根 —— 半小时后连接再次被打满,就只能一直救火。

连接池耗尽不是一个问题,而是四类问题的共同表象,动手前必须先区分:

表格

故障类型 典型特征 判断依据
连接泄露 活跃连接只增不减,缓慢爬升 拿了连接没在 finally 里释放,连接永久不归还
事务占坑 连接持有时间异常长 事务内嵌套 RPC 调用、事务迟迟不提交,连接被长期占住
慢 SQL 少量 SQL 拖垮整个池 单条 SQL 执行几分钟,连接死死占住不释放
流量突增 连接数随流量同步暴涨 突发流量冲垮,属于「合理占满」而非故障

区分方法很直接:看连接持有时间的分布。
泄露是「不还」,持有时间趋向无穷大;慢 SQL 是「还得慢」,集中在少数长尾请求上;流量突增是整体水位抬升但单次持有时间正常。

看错类型,动作全是无用功 —— 泄露问题你去扩容,只会让数据库端连接数跟着暴涨,反过来打满数据库最大连接数、拖垮整个库,形成恶性循环。

二、应急止损:不重启服务也能止血

核心链路不能停,重启又不能随时做,应急阶段有四招,全部不用改代码:

  1. 数据库端批量杀连接
    让 DBA 批量 kill 掉空闲超时和执行超时的慢连接,注意筛选条件,别把正常活跃连接一起杀。应用池侧仍会把这些连接当活的,杀完要确保连接池开启借出校验(testOnBorrow 或 keepalive),否则业务拿到死连接会报 Communications link failure。
  2. 动态调小连接池最大等待时长
    从等 60 秒改成 500ms 快速失败。宁可让部分请求立刻报错,也不要让线程堆积拖垮服务线程池,演变成雪崩。
  3. 非核心接口熔断降级
    报表、查询类接口直接降级返回兜底数据,把有限连接留给支付、下单等核心链路。
  4. 服务入口兜底熔断
    连接池完全打满且无法快速恢复时,入口直接拒绝新请求,防止堆积拖垮线程池。

操作顺序:先杀库端连接释放存量 → 再收紧等待时间控制增量 → 然后降级保护核心链路 → 最后才考虑入口熔断。

三、根因定位:让代码自己「招供」

止损之后必须回答:到底是哪段代码导致的?人工翻日志效率太低,要靠机制:

  • 开启连接池泄露检测
    HikariCP 的 leakDetectionThreshold(最小 2000ms,支持经 JMX 动态开启)和 Druid 的 logAbandoned,都能在连接借出超阈值未归还时,打印获取连接时的完整调用堆栈 —— 哪行代码拿了没还,一目了然。两者默认关闭,要提前配好。
  • 看连接池监控看板
    活跃连接数、等待队列长度、平均连接持有时间三指标对照:活跃数只涨不跌是泄露;等待队列长而持有时间正常是流量问题。
  • 配合慢 SQL 日志和事务监控
    拉出执行时间 TOP 的 SQL 和长事务列表,交叉定位慢 SQL 和「事务不提交」的源头。

重要提醒:
泄露不能只靠人工排查,还要配自动化兜底,但要分清能力边界:只有 Druid 的 removeAbandoned 会强制回收超时未归还的连接,HikariCP 只告警不回收。
而 removeAbandoned 生产环境要慎用 ——Druid 源码本身警示 not use in production,强制回收可能误杀执行中的长事务,持有线程报 Connection has been closed,造成业务数据错乱。
要开的话,阈值须大于业务最长合理耗时(建议 10 分钟以上),且只作应急期临时安全网,根治仍要回到代码规范。

四、架构根治:从编码规范上杜绝复发

应急和定位都是「术」,根治要落在架构和规范上:

  1. 强制约束事务边界
    严禁在数据库事务内调用 RPC 或第三方接口 —— 这是事务占坑的头号来源。事务要短小,网络调用一律移出事务。
  2. 统一封装数据访问层
    连接的获取和释放由框架统一管理,业务代码不允许裸操作连接,从规范层面杜绝泄露。
  3. 建立常态化 SQL 审核机制
    上线前过索引校验和执行计划检查,从源头减少慢 SQL;同时给 SQL 设置最大执行超时(应用侧 statement timeout 最通用,MySQL 的 max_execution_time 只对 SELECT 生效),超时自动中断,避免一条慢 SQL 拖垮整个池。核心接口再配 SQL 异常熔断 —— 超时比例过高自动降级。
  4. 流量侧分层限流
    入口按业务优先级限流,非核心业务降级返回兜底数据;数据库端设置应用连接数上限,避免无限制扩容把库打满。分库分表场景下多个连接池要分池监控,排查优先级:核心链路的库 > 高流量业务库 > 边缘库。

写在最后

连接池问题看似基础,恰恰是「会调参数」和「能设计高可用体系」的分水岭。
真正的考验不是那一个 maxActive,而是三点:
能不能区分泄露、慢 SQL、流量突增等故障场景;
知不知道不重启服务的应急止损方案;
有没有连接池治理、SQL 管控、熔断降级的落地经验。

普通做法是出事故重启救火;成熟的体系是应急止损、根因定位、架构优化形成闭环。
这套思路不止适用于连接池,线程池、缓存、消息队列的耗尽问题,治理框架都是相通的。

posted @ 2026-11-09 09:05  codigger  阅读(0)  评论(0)    收藏  举报