作者 lucy · 2026-04-11
年前给客户上线了一套 Web 管理系统,数据库搭在另一台云服务器上。最近业务量上来后,开始频繁出现数据库连接超时的问题,排查了一圈,最终定位到连接池配置上。记录一下全过程,供有类似问题的朋友参考。
⚠️ 声明:本文记录的是个人实战经验,解决方案仅供参考。不同业务场景、数据量级、服务器配置下,最优方案可能差异很大,请结合实际情况判断。
场景说明
系统上线初期运行平稳,日活几百人的时候完全没问题。到了今年 Q1,用户量涨到小几千,高峰期开始出现这样的错误日志:
could not obtain connection;
nested exception is org.hibernate.exception.JDBCConnectionException:
could not obtain connection
---
Caused by: com.mysql.cj.jdbc.exceptions.TimeoutException:
wait timeout exhausted
大量 Connection timeout,用户端表现为页面加载缓慢、接口返回 500。
排查过程
第一步:数据库最大连接数够用吗?
第一反应是怀疑数据库端连接数上限:
mysql> show variables like "max_connections";
+-----------------+-------+
| Variable_name | Value |
+-----------------+-------+
| max_connections | 151 |
+-----------------+-------+
默认值 151。再看当前实际连接数:
mysql> show status like "Threads_connected";
+-------------------+-------+
| Variable_name | Value |
+-------------------+-------+
| Threads_connected | 38 |
+-------------------+-------+
才 38 个,远没达到上限,排除这个。
第二步:检查网络延迟
从应用服务器 ping 数据库服务器,延迟正常(2-3ms),没有网络抖动。继续排查。
第三步:看连接池配置 — 这里是重点
检查连接池配置文件,发现几个核心参数设置过于保守:
# 问题参数(原始配置)
initial-size: 10 # 初始连接数
min-idle: 5 # 最小空闲
max-active: 20 # 最大活跃 ← 太低,高并发直接打满
max-wait: 60000 # 等连接最大等 60 秒 ← 太长了!
time-between-eviction-runs-millis: 60000
min-evictable-idle-time-millis: 300000
max-active 只有 20,高峰期 200 个并发请求打进来,连接根本不够用。max-wait 设置了 60 秒意味着线程会卡住等待 60 秒才超时,大量请求堆积,线程资源耗尽,系统崩溃。
解决方案(仅供参考)
⚠️ 以下参数是结合本次场景(4核8G服务器,预估百级并发)调整的,实际请根据自己业务量评估。
连接池参数调优
# 优化后的参数
initial-size: 10
min-idle: 10
max-active: 100 # 从20提升到100
# 超时时间缩短
max-wait: 5000 # 从60000改为5000ms,快速失败
# 空闲连接回收优化
time-between-eviction-runs-millis: 30000 # 30s检测一次
min-evictable-idle-time-millis: 180000 # 3min无活动则回收
# 连接有效性检测
validation-query: SELECT 1
test-while-idle: true
test-on-borrow: false # 借用时检测开销大,关闭
test-on-return: false
# 泄漏检测
remove-abandoned: true
remove-abandoned-timeout: 180 # 180s未归还则强制回收
log-abandoned: true
数据库端参数调整
# 云服务器上修改数据库配置文件
max_connections = 500 # 从151提升
# 顺便优化缓冲区
innodb_buffer_pool_size = 2G
innodb_log_file_size = 512M
sudo systemctl restart mysqld # 重启生效
效果对比
| 指标 | 调优前 | 调优后 |
|---|---|---|
| 最大活跃连接数 | 20 | 100 |
| 连接等待超时 | 60s | 5s |
| 连接泄漏监控 | 未开启 | 开启,180s强制回收 |
| 高峰期接口报错率 | ~15% | <0.5% |
| 平均响应时间 | 1.8s | 0.3s |
经验总结
-
连接池不是越大越好,但太小一定是灾难。 连接池耗尽时请求堆积,线程被锁死,系统整体瘫痪。max-active 需要结合预估并发量和服务器内存综合计算。
-
max-wait 设置过大等于给故障埋雷。 60s 的等待时间会让线程长时间挂起,建议生产环境设置为 3-5s,快速失败比无限等待好。
-
连接泄漏是隐藏的性能杀手。 开启 remove-abandoned 和 log-abandoned,一次
close()的遗漏能慢慢耗尽整个池。 -
validation-query 和 test-while-idle 必须开。 数据库连接空闲一段时间后可能被服务端主动关闭,客户端不知道,借用时会直接报错。
-
数据库侧的 max_connections 要留足余量。 默认值普遍偏低,支撑 100 个应用连接时务必调高。
⚠️ 再声明:以上经验仅供参考,不同场景参数差异可能很大,建议在测试环境验证后再上生产。
有问题欢迎留言交流 🚀