那天早上七点,我正喝着咖啡准备开始一天的工作,突然收到一连串报警邮件。打开一看,全是"HikariPool-1 - Connection is not available, request timed out after 30000ms"的错误。这个错误对于使用过HikariCP连接池的开发者来说应该不陌生,但每次遇到都让人头疼。
先来看看这个报错的完整链条:最外层是MyBatis的PersistenceException,接着是Spring的CannotGetJdbcConnectionException,最终定位到HikariCP的SQLTransientConnectionException。这个调用栈告诉我们,问题出在获取数据库连接这个环节。HikariCP在30秒内(30000ms)无法从连接池中分配一个可用连接给请求线程,于是抛出了超时异常。
这里有个关键点需要理解:HikariCP的连接池管理是异步的。当应用线程请求连接时,HikariPool会先检查是否有空闲连接。如果没有,它会尝试创建新连接(不超过maximum-pool-size限制)。如果创建新连接也需要时间(比如网络延迟或数据库负载高),而此时所有连接都被占用,请求线程就会进入等待队列。这个等待的最长时间就是connection-timeout参数的值,默认正好是30000ms。
在实际项目中,我发现很多开发者一看到这个错误,第一反应就是调大maximum-pool-size。这确实能暂时缓解问题,但并不是最优解。就像我遇到的这个场景:每天早上七点定时任务集中执行时出现连接耗尽,其他时间却完全正常。这说明我们的连接池配置没有考虑到业务高峰期的特点。
遇到"HikariPool-1 - Connection is not available"错误时,我们需要一套系统化的诊断方法。根据我的经验,可以按照以下步骤进行排查:
首先,检查数据库服务器本身的健康状况。通过数据库监控工具查看CPU、内存、IO等指标是否正常,活跃连接数是否达到max_connections限制。有时候问题可能出在数据库端,比如长时间运行的查询阻塞了连接释放。
其次,分析应用日志中的连接获取模式。我在排查时发现,错误集中在早上7:00-7:20这个时段,恰好是多个定时任务并发执行的时间窗口。通过日志可以统计出以下几个关键指标:
这里分享一个实用技巧:可以通过HikariCP的JMX监控获取实时连接池状态。在Spring Boot应用中,只需要在配置中添加:
yaml复制management:
endpoints:
web:
exposure:
include: hikari
然后访问/actuator/hikari端点,就能看到类似如下的关键指标:
json复制{
"pool": "DateHikariCP",
"active": 58,
"idle": 2,
"total": 60,
"waiting": 12,
"max": 60,
"min": 10
}
通过这些数据,我们能准确判断连接池是否真的不够用。在我的案例中,高峰期的active连接经常达到maximum-pool-size上限,同时waiting线程数持续增长,这证实了连接池容量不足的猜测。
理解了问题本质后,我们来讨论如何科学地调优HikariCP参数。很多人直接照搬网上找到的"优化配置",这是非常危险的做法。正确的做法是根据应用的实际负载特征来量化评估每个参数。
maximum-pool-size:这是最关键的参数。设置太小会导致连接不够用,设置太大则可能拖垮数据库。有个经验公式可以参考:
code复制最大连接数 ≈ (平均查询时间(秒) × 峰值QPS) + 缓冲系数(5-10)
比如在我的场景中,平均查询时间是0.5秒,早上高峰QPS约100,那么理论最大连接数应该在55-60左右。这与我最终设置的60相符。
minimum-idle:这个参数控制连接池保持的最小空闲连接数。默认等于maximum-pool-size,但在有明确流量波峰波谷的应用中,可以适当降低。我设置为10,因为夜间流量很低,保持太多空闲连接反而浪费资源。
connection-timeout:连接获取超时时间。30秒的默认值对于大多数OLTP应用来说太长了。我建议设置在3-10秒之间,这样能快速失败,避免请求堆积。但要注意,设置太短可能在数据库临时性能波动时造成不必要的失败。
idle-timeout和max-lifetime:这两个参数控制连接的存活时间。默认值分别是10分钟和30分钟。我调整idle-timeout为3分钟,max-lifetime保持30分钟不变。这样可以更快回收闲置连接,又不至于频繁创建新连接带来开销。
这里有个配置示例,是我在实际项目中验证过的:
yaml复制hikari:
pool-name: MyAppHikariCP
minimum-idle: 10
maximum-pool-size: 60
idle-timeout: 180000
max-lifetime: 1800000
connection-timeout: 5000
connection-test-query: SELECT 1
参数调优虽然重要,但有时候我们需要从系统架构层面寻找更根本的解决方案。在我的案例中,除了调整HikariCP配置,还实施了以下几个优化措施:
定时任务错峰执行:分析发现早上7点有5个关键定时任务同时启动。通过重新调度,将它们分散到6:50-7:10这个时间窗口内执行,避免了瞬时高峰。
引入二级缓存:对于部分高频访问但实时性要求不高的数据,使用Redis缓存。这样减少了约30%的数据库查询量。
实现连接泄漏检测:有时候连接耗尽是因为代码中没有正确关闭连接。我们添加了连接泄漏检测机制:
java复制@Bean
public HikariDataSource dataSource() {
HikariConfig config = new HikariConfig();
config.setLeakDetectionThreshold(30000); // 30秒
// 其他配置...
return new HikariDataSource(config);
}
数据库读写分离:将报表类查询路由到只读副本,减轻主库压力。这个改造需要应用架构支持,但效果非常显著。
监控方面,我们建立了完整的指标看板,监控以下关键指标:
这套组合拳实施后,系统已经稳定运行了6个月,再没出现过"HikariPool-1 - Connection is not available"的错误。即使在业务高峰期,连接池使用率也保持在健康水平。