MySQL 连接池最大连接数设多少才不翻车
盲目调高
max_connections不但不能提升吞吐,反而会因线程竞争、内存耗尽导致 MySQL 崩溃。真实压测中,多数业务在
200–500之间就遇到瓶颈,关键不在“能开多少”,而在“有多少连接真正在干活”。 先查当前活跃连接:
SHOW STATUS LIKE 'Threads_connected';和
SHOW STATUS LIKE 'Threads_running';—— 后者才是正在执行 SQL 的线程,它长期 > 50 就说明查询慢或锁等待严重 应用层连接池(如 HikariCP)的
maximumPoolSize建议设为 DBA 允许的
max_connections的 60%~70%,留出空间给后台任务、监控、DBA 操作 MySQL 服务端的
max_connections不建议超过
1000(尤其在 8GB 内存以下机器),每连接默认占用约 2MB 内存,超了直接 OOM
HikariCP 关键参数怎么配才不拖慢请求
HikariCP 不是设了
maximumPoolSize就万事大吉,几个隐藏坑点常让高并发下连接获取超时或空转。
connectionTimeout必须显式设(如
3000),否则默认 30 秒——用户等半分钟才报错,体验极差
idleTimeout建议设为
600000(10 分钟),太短会导致频繁销毁重建连接;但若后端 MySQL 开了
wait_timeout=60,必须让
idleTimeout小于它,否则连接被服务端主动断开后,HikariCP 不知道,下次取出来直接报
Connection closed
leakDetectionThreshold设成
60000(60 秒),能抓到没 close() 的 Connection,避免连接泄露把池子占满 禁用
testOnBorrow(已废弃),改用
connectionTestQuery=SELECT 1+
validationTimeout=3000,避免每次取连接都执行校验拖慢响应
事务边界不清导致连接池卡死的真实案例
高并发下最常见“连接池耗尽”不是因为 QPS 高,而是事务没及时结束,连接被长期占用。典型表现:
Threads_running低,但 HikariCP 的
active连接数持续接近
maximumPoolSize,且
pending队列不断增长。 Spring Boot 中 @Transactional 注解方法若抛出非 RuntimeException(如 checked exception),事务默认不回滚,连接也不会释放——务必确认
@Transactional(rollbackFor = Exception.class)流式查询(
ResultSet.TYPE_FORWARD_ONLY)未关闭
ResultSet或
Statement,连接无法归还,HikariCP 日志会出现
Connection marked as broken because of an exception异步任务(@Async)里开启事务,容易脱离原始请求生命周期,连接在异步线程结束后才释放,造成“连接幽灵占用”
读写分离 + 连接池分组的实际落地要点
单主多从架构下,把所有流量打向主库,连接池再优化也扛不住写请求激增。分组连接池不是简单配两个 HikariDataSource,而是要和路由逻辑对齐。
主库连接池maximumPoolSize可设小些(如 30),从库池可稍大(如 80),因为读请求通常更多、更轻量 避免在 DAO 层硬编码
slaveDataSource,用 AbstractRoutingDataSource + ThreadLocal 路由,但注意 Spring 的事务传播机制:如果一个 @Transactional 方法里先查后写,必须强制走主库,否则从库查到的是旧数据 从库延迟监控必须做——当
Seconds_Behind_Master > 30时,自动将该从库连接池
setIsReadOnly(false)或临时剔除,否则读到脏数据比慢还致命 连接池不是越大越好,也不是配完就稳。真正卡住系统的,往往是那个忘了关 ResultSet 的 for 循环,或是事务传播类型写错的 @Transactional 方法。盯住
Threads_running和 HikariCP 的
active/
pending指标,比背参数重要得多。
