2023年有一段时间,政策快报平台每天下午都会出现一波“服务慢”的投诉。
接口响应时间从200ms飙升到3-5秒,有时候直接超时。查了日志,发现是数据库连接池在高峰期被耗尽。新的请求拿不到连接,只能等待或超时。
当时我们的连接池配置是“默认参数”——最大连接数10、超时时间30秒。看起来合理的配置,在高峰期根本扛不住。
后来我们花了几周时间,从连接池配置到代码优化,做了一整套改造。接口超时率从5%降到了0.1%以下。今天复盘这个优化过程。
一、问题诊断:连接池为什么被打满?
首先,我们来理解连接池被打满的完整链路。
问题现象:
高峰期接口响应时间从200ms飙升到3-5秒
部分接口直接超时(30秒后返回错误)
数据库CPU使用率并不高(30%左右),说明不是数据库慢查询导致的问题
排查过程:
查看数据库连接数
sql
复制
下载
SHOW PROCESSLIST;
发现连接数接近上限(100个),大量连接处于“Sleep”状态。
查看应用日志
大量“HikariPool-1 - Connection is not available”错误。
这是HikariCP在连接池耗尽时抛出的异常。分析代码
发现有两个问题:
有代码没有正确关闭Connection(try-with-resources使用不当)
部分接口执行时间过长(5-8秒),连接一直被占用
根本原因:连接没有被及时释放 + 执行时间长 + 高峰期请求量大 → 连接池快速耗尽。
二、配置优化:从默认参数到合理参数
优化的起点是调整HikariCP连接池的配置。
| 参数 | 优化前 | 优化后 | 说明 |
|---|---|---|---|
| maximumPoolSize | 10 | 30 | 最大连接数,根据并发量调整 |
| minimumIdle | 10 | 10 | 最小空闲连接,保持不变 |
| connectionTimeout | 30000 | 5000 | 超时时间从30秒缩短到5秒,快速失败而不是长时间等待 |
| idleTimeout | 600000 | 300000 | 空闲超时从10分钟缩短到5分钟,释放不用的连接 |
| maxLifetime | 1800000 | 600000 | 连接最大存活时间从30分钟缩短到10分钟,避免长连接被数据库回收 |
核心逻辑:连接池配置不是越大越好,也不是越小越好。需要根据实际并发量测算。测算方法是:峰值QPS × 平均执行时间 = 需要的连接数。如果峰值QPS为100,平均执行时间200ms,那么20个连接就足够了。如果平均执行时间较长(如5秒),则需要100个左右。
三、代码优化:解决连接泄露
找到未正确关闭Connection的代码,修复或重构,确保所有连接在使用后被关闭。
优化前(有风险):
java
Connection conn = dataSource.getConnection(); // 执行业务逻辑 // 如果这里抛出异常,conn不会被关闭 conn.close();
优化后(安全):
java
try (Connection conn = dataSource.getConnection()) { // 执行业务逻辑 // 自动关闭 }四、慢查询优化:缩短执行时间
检查所有超过1秒的SQL,加索引、改写法。
优化前:5-8秒
优化后:200ms
最终效果
| 指标 | 优化前 | 优化后 |
|---|---|---|
| 连接池耗尽频率 | 每天3-5次 | 几乎为零 |
| 接口超时率 | 5% | 0.1% |
| 高峰期响应时间 | 3-5秒 | 200ms |
| 数据库连接数 | 100(满) | 30-50(稳定) |
经验总结
连接池配置不是“设置一次就不管了”。需要根据业务增长持续调整。
连接泄露是最隐蔽的问题之一。使用try-with-resources是强制规范。
配置调整不是优化的终点。优化慢查询、缩短执行时间,本质上是在“减少对连接的占用时长”,效果比单纯加大连接池更根本。
连接池优化的核心思路是:先找到“连接不够用”的原因——是配置太小、连接泄露,还是执行时间太长。对症下药,比盲目调大连接池更有效。