记一次数据库连接池调优:高并发场景下的实战复盘

作者 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

经验总结

  1. 连接池不是越大越好,但太小一定是灾难。 连接池耗尽时请求堆积,线程被锁死,系统整体瘫痪。max-active 需要结合预估并发量和服务器内存综合计算。

  2. max-wait 设置过大等于给故障埋雷。 60s 的等待时间会让线程长时间挂起,建议生产环境设置为 3-5s,快速失败比无限等待好。

  3. 连接泄漏是隐藏的性能杀手。 开启 remove-abandoned 和 log-abandoned,一次 close() 的遗漏能慢慢耗尽整个池。

  4. validation-query 和 test-while-idle 必须开。 数据库连接空闲一段时间后可能被服务端主动关闭,客户端不知道,借用时会直接报错。

  5. 数据库侧的 max_connections 要留足余量。 默认值普遍偏低,支撑 100 个应用连接时务必调高。

⚠️ 再声明:以上经验仅供参考,不同场景参数差异可能很大,建议在测试环境验证后再上生产。

有问题欢迎留言交流 🚀

发表评论

苏ICP备18039580号-2