MySQL连接池连不上主因是max_connections与应用端配置不匹配,需确保maximumPoolSize不超过其70%~80%,并合理设置leakDetectionThreshold、keepaliveTime、wait_timeout等参数以避免连接泄漏和状态不同步。

MySQL 连接池连不上,八成是 max_connections 和应用端配置对不上
MySQL 服务端的 max_connections 默认通常只有 151,而 Spring Boot 应用默认 HikariCP 的 maximumPoolSize 是 10,看起来很安全——但一上压测或并发稍高,就会出现 Cannot create PoolableConnection: Could not create connection to database server. 或连接超时。根本原因不是数据库扛不住,而是连接池在「等」,等不到空闲连接。
- 务必检查 MySQL 实际生效的
max_connections:执行SHOW VARIABLES LIKE 'max_connections';,注意它可能被配置文件或启动参数覆盖 - 应用端最大连接数(如 HikariCP 的
maximumPoolSize)不能超过max_connections的 70%~80%,留出空间给 DBA 工具、监控探针、临时 SQL 连接 - 别只看连接池“最大值”,更要盯住
minimumIdle(HikariCP)或minIdle(Druid)——设为 0 意味着冷启动时全量建连,容易触发连接风暴
HikariCP 的 connectionTimeout 和 validationTimeout 别乱调
这两个超时值直接决定连接失败时用户感知到的卡顿时间,也影响故障扩散速度。设得太长(比如 30s),一个坏节点会让整个池子卡住;设得太短(比如 100ms),又可能把慢查询误判为失效连接。
-
connectionTimeout:从池中取连接的等待上限,建议 3–5 秒;低于 1 秒容易在瞬时高峰下大量抛Unable to acquire JDBC Connection -
validationTimeout:验证连接是否存活的单次耗时上限,必须小于connectionTimeout,推荐 3 秒;若用testOnBorrow(已过时),验证失败会同步阻塞,拖慢所有请求 - 更稳的做法是关掉 borrow 时验证,改用
connectionTestQuery=SELECT 1+idleTimeout定期清理空闲连接
MySQL 的 wait_timeout 和 interactive_timeout 会悄悄杀掉空闲连接
MySQL 默认 8 小时断开空闲连接,但应用端连接池并不知道连接已被服务端关闭,下次取出就报 Communications link failure 或 Connection reset。这不是网络问题,是连接状态不同步。
- 应用端必须开启保活机制:HikariCP 推荐配
keepaliveTime=300000(5 分钟),并确保connectionInitSql=SELECT 1或启用isValidTimeout - MySQL 端可适当调低
wait_timeout(如 600 秒),让僵死连接更快释放,但不能低于应用端的idleTimeout(HikariCP 默认 10 分钟) - Druid 用户注意:
testWhileIdle=true必须配合timeBetweenEvictionRunsMillis,否则空闲检测不触发
连接泄漏比连接不足更难排查,优先加 leakDetectionThreshold
没显式 close Connection、Statement、ResultSet,或者 try-with-resources 写错位置,都会导致连接被占用却不归还。现象是连接数缓慢上涨,直到打满池子,日志里却看不到明显报错。
- HikariCP 必开:
leakDetectionThreshold=60000(单位毫秒),超 60 秒未归还就打印堆栈,定位泄漏点 - 该参数有性能开销,生产环境建议设为 30–120 秒,别关;设为 0 表示关闭,等于放弃主动防御
- 注意:Spring 的
@Transactional方法内手动获取Connection,必须确保在事务结束前释放,否则泄漏发生在框架层之下,工具难以捕获
连接池不是越大越好,也不是越小越省资源;真正关键的是让应用端的生命周期管理节奏和 MySQL 服务端的状态策略对齐。最容易被忽略的,其实是 wait_timeout 和 leakDetectionThreshold 这两个看似“兜底”的配置——它们不出问题时静默运行,一出就是线上事故。











