MySQL连接池是应用层访问数据库的第一道关卡。连接数配置不合理,要么应用在高峰期拿不到连接,要么数据库被连接数压垮。连接池参数和MySQL服务端参数需要配合调整,本文从连接池的获取逻辑讲起,给出常用连接池(HikariCP、Druid)的配置建议,再演示连接泄漏的排查方法。
连接池的作用与核心参数
连接池复用了TCP连接与MySQL认证握手这两笔开销。每次新建MySQL连接大约消耗几十到几百毫秒,池化后应用侧只需要从池里取空闲连接,性能差距明显。连接池的核心参数:maximumPoolSize最大连接数、minimumIdle最小空闲连接、connectionTimeout获取连接超时、idleTimeout空闲回收时间、maxLifetime连接最大存活时间。这几个参数互相制约,不能单独调大。
# HikariCP 示例配置(Spring Boot)
spring.datasource.hikari.maximum-pool-size=20
spring.datasource.hikari.minimum-idle=5
spring.datasource.hikari.connection-timeout=30000
spring.datasource.hikari.idle-timeout=600000
spring.datasource.hikari.max-lifetime=1800000
maximum-pool-size并不是越大越好。连接数超过数据库处理能力时,多余连接只是排队等待CPU,反而加剧锁竞争。经验公式:连接数 = ((核心数 * 2) + 有效磁盘数)。一个8核单盘的MySQL实例,连接池设20左右是合理起点,再压测微调。
MySQL服务端连接数配置
服务端max_connections决定MySQL能同时接受多少连接。应用连接池的总连接数(各实例池大小之和)不能超过服务端上限,否则报Too many connections。查看当前连接状况:
-- 查看当前连接数与上限
SHOW VARIABLES LIKE 'max_connections';
SHOW STATUS LIKE 'Threads_connected';
SHOW STATUS LIKE 'Threads_running';
-- 查看连接来源分布
SELECT user, host, db, command, time, state
FROM information_schema.processlist
ORDER BY time DESC;
Threads_running远小于Threads_connected是正常状态,说明大部分连接空闲等待。如果Threads_running接近max_connections,说明并发查询堆积,先查慢查询再调池,不要盲目调大max_connections。
连接泄漏的表现与定位
连接泄漏指应用拿连接后没归还,池中连接被慢慢占满。典型表现:监控里active连接数持续上涨、接口响应变慢、报Connection is not available, request timed out。泄漏常见原因:事务里catch了异常没回滚、finally块没释放、异步线程里复用了请求线程的连接、连接借用超时未归还。
定位方法:先看连接池监控里的active、idle、pending统计,确认是获取不到连接;再打开SQL日志记录连接借出与归还:
# HikariCP 开启泄漏检测(超过60s未归还则打印堆栈)
spring.datasource.hikari.leak-detection-threshold=60000
# Druid 开启连接泄漏检测
spring.datasource.druid.remove-abandoned=true
spring.datasource.druid.remove-abandoned-timeout=60
spring.datasource.druid.log-abandoned=true
开启后日志里会打印泄漏连接的完整堆栈,直接定位到哪段代码借了连接没还。生产环境可以先灰度开一段时间抓现场,跑完关掉,避免日志量过大。
常见泄漏代码模式与修复
// 错误写法:事务内异常未回滚,finally未释放连接
@Transactional
public void updateUser(User user) {
UserDetail detail = detailMapper.selectByUserId(user.getId());
if (detail == null) {
throw new BizException("用户不存在"); // 这里抛异常后事务回滚,连接交给Spring管理
}
userMapper.update(user);
}
// 正确写法:手动管理连接时,务必用try-with-resources
public void batchUpdate(List<Long> ids) {
String sql = "UPDATE user SET flag=1 WHERE id=?";
try (Connection conn = dataSource.getConnection();
PreparedStatement ps = conn.prepareStatement(sql)) {
for (Long id : ids) {
ps.setLong(1, id);
ps.addBatch();
}
ps.executeBatch();
} catch (SQLException e) {
log.error("batch update failed", e);
// 异常时自动关闭连接,不会泄漏
}
}
用了Spring的@Transactional时,连接生命周期由事务管理器管理,不要手动调用conn.close(),否则会破坏事务边界。手动拿到Connection的场景(批量、复杂SQL)用try-with-resources保证任何路径都释放。
连接池预热与初始化连接
应用刚启动时连接池是空的,第一波流量会触发创建连接,峰值时瞬时延迟高。HikariCP有initializationFailTimeout和connection-init-sql参数,可以在启动时预创建连接。批量预热方式:应用启动后跑一个预热SQL,SELECT 1,把所有连接真正建立起来。
# 初始化时填充连接,并校验连接可用性
spring.datasource.hikari.initialization-fail-timeout=30000
spring.datasource.hikari.connection-init-sql=SELECT 1
配合最小空闲连接数minimum-idle=5,启动后池里保持5条活跃连接,首波请求不再排队。
连接数监控与告警
连接池监控不能只看平均值。推荐指标:Threads_connected趋势、Threads_running、连接获取等待时间(Hikari的connectionTimeout发生次数)、池中active连接数。接入Prometheus后配置告警规则:Threads_connected超过max_connections的80%预警,active连接数长时间占满池子告警,pending等待数大于0持续5分钟告警。这些指标比单看CPU/内存更能反映数据库层的健康度。
连接池调优没有固定公式,按业务并发量实测微调。监控到位、泄漏检测开着、连接数设置合理,数据库层就能平稳支撑业务增长。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/mysql-lian-jie-chi-diao-you-shi-zhan-lian-jie-shu-pei-zhi/