连接池耗尽服务全挂,排查3天才发现HikariCP默认值全是坑

简介: 本文记录一次线上服务雪崩事故的3天排查过程:核心服务RT从80ms飙升至30秒,根源竟是HikariCP三个开发默认值——maximum-pool-size=10、connection-timeout=30s、idle-timeout=10分钟——在高并发下形成乘法级灾难。文章详解问题原理,并给出生产级配置方案。

那天凌晨3点,告警短信把整组人炸醒了。

监控大屏上,一个核心服务的RT从平时的80ms飙到了30秒,所有接口全部超时。看Tomcat线程池——200个线程全部在跑,没有一个空闲。再看数据库——MySQL连接数只有12个,远没到上限151。数据库不慢,服务却崩了。

运维第一反应是重启。重启后好了10分钟,又崩。再重启,又是10分钟。

接下来3天的排查,最后锁定的问题只有一行配置。

排查第一天:看起来只是"慢"

从日志入手,错误信息很明确:

java.sql.SQLTransientConnectionException: HikariPool-1 - Connection is not available, 
request timed out after 30000ms.
    at com.zaxxer.hikari.pool.HikariPool$PoolEntryCreator.call(HikariPool.java:734)

翻译成人话:获取数据库连接等了30秒还没拿到,直接抛异常。

这是 HikariCP 连接池的典型报错——连接池里的连接不够用了,新的请求排队等了 connection-timeout 的时长(默认30秒),还是没等到,报错退出。

第一天的排查方向:是不是有慢SQL把连接占住了?查了MySQL慢查询日志,确实有一条统计SQL跑了3秒——原因是某个表昨天新加了200万行数据,负责统计的索引没建。

加了索引后,SQL从3秒降到50ms。重启服务,以为解决了。

凌晨4点,又崩了。一模一样的报错。

排查第二天:200多个线程全卡在一个地方

第二天做了线程dump。不看不知道,一看吓一跳:

"http-nio-8080-exec-187" #187 daemon prio=5 os_prio=0 tid=... waiting on condition
   java.lang.Thread.State: TIMED_WAITING
        at com.zaxxer.hikari.pool.HikariPool.getConnection(HikariPool.java:358)
        - waiting on <0x00000006c0d4e8f0>
"http-nio-8080-exec-188" #188 daemon prio=5 os_prio=0 tid=... waiting on condition
   java.lang.Thread.State: TIMED_WAITING
        at com.zaxxer.hikari.pool.HikariPool.getConnection(HikariPool.java:358)
        - waiting on <0x00000006c0d4e8f0>

从 #187 到 #200+,所有线程都在 HikariPool.getConnection() 上排队。Tomcat的200个线程池全部被这些等待连接的线程占满了,新来的请求根本没线程处理,直接被拒绝。

问题变成了:为什么连接池里的连接不够用?

看了一下配置——问题初步浮出水面:

# 当前配置(就是Spring Boot的默认值,一直没改过)
spring.datasource.hikari.maximum-pool-size=10
spring.datasource.hikari.connection-timeout=30000

10个连接。这个服务的正常QPS是100,每个数据库查询平均50ms,理论上 10 / 0.05 = 200 QPS 上限,100 QPS 应该够用。

但问题在于:当出现一个3秒的慢查询时,整个池瞬间就崩了。

第三天:终于找到了根因——不是1个参数的锅,是3个默认值联手搞的事

算一笔账,你就能明白为什么"10个连接+30秒超时"是生产环境的定时炸弹:

灾难推演:假设QPS=100,10个连接。当1条SQL从50ms突变为3秒(缺索引/数据量暴涨/锁等待),有效吞吐量从 10/0.05=200 QPS 骤降到 10/3=3.3 QPS。每秒钟有 97 个请求在排队等连接。每个请求最多等 connection-timeout=30秒才报错。30秒 × 97个/秒 = 2910个请求在Tomcat线程池里排队。而Tomcat默认只有200个线程——线程池先于连接池被击穿,整个服务假死。

重新审视 HikariCP 的默认值,有3个参数是专门为开发机设计的,不是为生产环境设计的:

参数

默认值

开发机的逻辑

生产环境的现实

maximum-pool-size

10

本地就一个用户,10个连接绰绰有余

生产QPS 100+,一条慢SQL就让10个连接全部占满

connection-timeout

30000(30秒)

本地请求少,等久一点无所谓

30秒超时意味着线程池被排队线程占满,其他正常请求全被拒绝

idle-timeout

600000(10分钟)

连接池小,回收快慢无所谓

空闲连接10分钟才回收,连接长时间不用可能已被数据库端断开,下次使用直接报错

单个看,每个默认值都有它的道理。但三个组合在一起,高并发场景下的效果是乘法级的灾难:

  • maximum-pool-size=10 → 吞吐天花板极低
  • connection-timeout=30000 → 出问题时线程堵30秒才退出,堵住的线程又占着Tomcat线程池
  • idle-timeout=600000 → MySQL端 wait_timeout 如果设了较短值(比如某些云数据库默认60秒),连接在池里闲置10分钟早就被数据库关了,取出来用就报 CommunicationsException

三个参数的Production-Ready配置

1. maximum-pool-size:不是越大越好,但要算清楚

先看数据库能承受多少连接:

-- 查数据库最大连接数
SHOW VARIABLES LIKE 'max_connections';
-- 查当前已用连接数
SHOW STATUS LIKE 'Threads_connected';

假设MySQL最大连接数151,系统账号和监控占20个,剩下131个给业务。如果线上有4个应用实例,每个实例的连接池大小上限 = 131 / 4 ≈ 32。

然后再用业务公式验证:

# 公式:连接池大小 = 峰值QPS × 平均数据库查询RT(s) × 1.5
# 示例:峰值QPS=200,平均SQL RT=50ms=0.05s
# → 200 × 0.05 × 1.5 = 15 个连接
# 但必须保留慢查询buffer:再预留30%
# → 15 × 1.3 ≈ 20 个连接
spring.datasource.hikari.maximum-pool-size=20

一个经验值:单实例一般配 20~50,不要超过数据库最大连接数 ÷ 实例数的 80%。10 是绝对不够的。

2. connection-timeout:30秒太长了,3秒就够了

你在生产环境等过一个30秒的请求吗?用户早就关页面了。

connection-timeout 的逻辑是"等多久还没拿到连接就抛异常"。30秒意味着:

  • 用户等了30秒才看到报错(他10秒前就已经关了页面)
  • 这30秒里Tomcat线程一直被占用,堵着后面的正常请求
  • 如果30秒内有3000个请求进来,3000个线程全堵着——Tomcat线程池瞬间爆炸
# 设置3秒超时——如果3秒拿不到连接,说明连接池大概率已经崩了
# 快速失败比长时间等待要好得多
spring.datasource.hikari.connection-timeout=3000

快速失败原则:3秒拿不到连接就报错,用熔断/降级兜底,而不是让所有请求堵30秒然后把整个服务拖死。

3. idle-timeout 和 max-lifetime:别让你的连接被数据库偷偷关了

最后一个坑:HikariCP里的连接闲置太久,数据库那边早就断开了,拿出来用就报错。

# MySQL默认 wait_timeout = 28800(8小时),一般不改
# 但很多云数据库RDS的默认值不同,有些只有60秒
# HikariCP 的 max-lifetime 必须比数据库的 wait_timeout 短
# 一般设 30分钟(1800000ms)
spring.datasource.hikari.max-lifetime=1800000  # 30分钟,默认值OK
# idle-timeout 要小于 max-lifetime
# 设置5分钟——连接5分钟没用就回收,不让数据库端先断开
spring.datasource.hikari.idle-timeout=300000    # 5分钟
# 额外加 keepalive ——定期ping保持连接活跃(HikariCP 4.0+ 支持,最低间隔30秒)
spring.datasource.hikari.keepalive-time=60000   # 每60秒ping一次

一张表记住所有配置

参数

默认值(坑)

生产建议值

为什么

maximum-pool-size

10

20~50

按 QPS×RT×1.5 计算

connection-timeout

30000

3000

快速失败,堵30秒会拖死线程池

idle-timeout

600000

300000

5分钟回收,防止数据库端先断

max-lifetime

1800000

1800000

默认OK,但必须小于MySQL wait_timeout

keepalive-time

0(关闭)

60000

每60秒ping保活

写在最后

HikariCP 本身是一个非常好的连接池——启动快、开销低、性能顶级。问题不在于它,在于默认值是为"本地开发"这个场景设置的。Spring Boot 把它作为默认连接池时,并没有把默认值改成生产级别的。

这件事的教训不只是"记得改配置"——上线前检查清单里,连接池配置必须是必查项。10个连接在本地跑得飞起,到了生产就是一颗不知道什么时候爆炸的定时炸弹。

你的项目里 HikariCP 的这几个参数改过了吗?还在用默认值的话,下次高并发可能就不是排查3天的问题了。

相关文章
|
3月前
|
人工智能 Java 应用服务中间件
HikariCP + Tomcat + Spring Boot:九个容易被忽视的默认值配置
某电商凌晨订单服务崩溃,根源竟是HikariCP默认连接池仅10个连接。本文直击Spring Boot三大组件(HikariCP、Tomcat、自动配置)中9个高危默认值,涵盖连接池超时、线程瓶颈、删库风险等典型生产问题。
|
3月前
|
Java 应用服务中间件 数据库连接
Spring Boot这5个配置项默认值就是错的,高并发第1天就502
Spring Boot上线前5项关键配置必查:Tomcat线程数(建议≥500)、HikariCP连接池(单实例≤30)、Actuator端点最小暴露(禁用env/heapdump)、生产日志分级(业务INFO+第三方WARN)、强制激活prod profile。缺一即可能导致502、超时或安全风险。
|
SQL
记一次不常见到主从延迟问题
Slave_SQL_Running_State: Waiting for dependent transaction to commit 导致的主从延迟
8683 1
|
3月前
|
Web App开发 人工智能 jenkins
还在手写测试报告?推荐一款 AI 测试Skill:只需一句指令,自动生成专业定制化测试报告!
`api-report-generator`是一款AI驱动的接口测试报告生成工具,一句指令即可将执行数据自动转化为含11大专业分区、风险分级、优化建议及Allure联动的决策型HTML报告,告别“数字堆砌”,让报告真正有人看、能决策。
379 0
还在手写测试报告?推荐一款 AI 测试Skill:只需一句指令,自动生成专业定制化测试报告!
|
3月前
|
人工智能 JSON 算法
API选品 vs 人工选品:效率与精准度的终极对决
本文剖析电商等领域“选品之争”:API自动化选品以高效、可扩展见长,擅处理海量数据;人工选品则胜在文化洞察、战略判断与伦理把关。二者非替代而是互补——API广筛初排,人工精筛校准,闭环反馈持续优化。未来属于“AI增强型专家”。
|
前端开发 API 开发工具
为什么 iPhone 自带图书 App 不支持音量键翻页?我写了一个开源解决方案
SwiftBook是一款开源iOS EPUB阅读器(MIT协议),专为“躺着看书党”优化:首创音量键翻页(KVO+静音音频黑科技)、CSS多栏分页、内置思源宋体,支持主题/字号/断点续读等。仅需Mac+Xcode即可安装体验。
为什么 iPhone 自带图书 App 不支持音量键翻页?我写了一个开源解决方案
|
3月前
|
存储 移动开发 小程序
圈子论坛软件系统商业版开发搭建全攻略:社交圈子系统多端适配与行业场景落地指南
本圈子系统采用UniApp+ThinkPHP6技术栈,一套代码覆盖微信小程序/H5/APP多端;支持付费入圈、会员体系、实时聊天与内容风控,集成支付、地图、云存储及AI安全审核,兼顾高效开发与商业合规。
277 0
圈子论坛软件系统商业版开发搭建全攻略:社交圈子系统多端适配与行业场景落地指南
|
4月前
|
人工智能 运维 Shell
还在手动敲命令?OpenCode CLI 这 10+ 实用命令让你的开发效率起飞
熟练使用 CLI 命令是高效驾驭 OpenCode 的基础。本文系统讲解 OpenCode CLI 基础命令、核心功能、TUI 自定义命令、快捷键配置及环境变量,帮助开发者全面掌握 OpenCode 使用方法。
1778 1
|
3月前
|
人工智能 自然语言处理 搜索推荐
AI Shopping,开始进入信任时代
Phia事件引发对AI购物时代的深层反思:当购物助手从“帮人找商品”转向“代人做决策”,联盟佣金、Cookie Stuffing与数据隐私暴露出核心矛盾——消费者是否愿将决策权(Decision Authority)托付给AI?信任,而非算力,正成为AI商业化的真正瓶颈。(239字)
200 0
|
Java API 数据安全/隐私保护
Spring Boot中的文件系统操作
Spring Boot中的文件系统操作