连接池大小应设为单实例峰值 QPS×平均耗时的 1.5–2 倍,且不超过 max_connections/ 实例数的 80%;wait_timeout 控制服务端空闲连接存活时间,connectionTimeout 控制客户端建连超时;maxLifetime 应比 wait_timeout 短 30–60 秒。

连接池大小设多少才不拖慢数据库
连接池不是越大越好,超过数据库实际处理能力反而会堆积等待、触发拒绝或超时。关键看 max_connections(数据库侧上限)和应用单实例的平均并发请求量。比如 PostgreSQL 默认 max_connections = 100,若你有 4 个应用实例,每个池子设 30 就大概率抢光连接,导致其他实例或后台任务连不上。
实操建议:
- 先查数据库当前最大连接数:
SHOW max_connections;(PostgreSQL)或SELECT @@max_connections;(MySQL) - 估算单实例峰值 QPS × 平均查询耗时(秒),例如 200 QPS × 0.1s = 20 —— 这是理论最小活跃连接需求
- 池大小建议设为该值的 1.5–2 倍(留缓冲),但单实例别超
max_connections / 实例数的 80% - Java HikariCP 中直接配
maximumPoolSize;Go sqlx 用db.SetMaxOpenConns(n)
wait_timeout 和 connectionTimeout 到底谁先生效
这是最容易混淆的一对:数据库端的 wait_timeout(MySQL)或 tcp_keepalives_idle(PostgreSQL)管“空闲连接活多久”,而客户端的 connectionTimeout(如 HikariCP 的 connection-timeout)管“新连接建立等多久”。前者防连接被数据库主动断开,后者防网络卡住时无限挂起。
常见错误现象:
- 应用日志反复出现
Connection refused或Communications link failure,但数据库没重启 → 很可能是连接被服务端静默回收,客户端还当它活着 - 偶尔报
Unable to acquire JDBC Connection,且集中在流量低谷后 → 池里残留了过期连接,首次复用就失败
实操建议:
- MySQL:把
wait_timeout设成 300(5 分钟),客户端connectionTimeout设成 3000(3 秒),再配validationTimeout=2500和keepAliveTime=60000 - PostgreSQL:启用
tcp_keepalives_idle=60+tcp_keepalives_interval=10,客户端用healthCheckSource或定期执行SELECT 1 - 永远不要让客户端超时 > 服务端超时,否则必然出现“连接已死但池子不知情”
maxLifetime 设太长会导致连接突然失效
maxLifetime 是连接在池中存活的绝对上限(比如 HikariCP 默认 30 分钟),超时后连接会被强制关闭并重建。设得太长(如 24 小时),可能撞上中间件(NAT、LB)的连接空闲踢出策略,或数据库因主从切换、滚动重启清掉旧连接,结果应用复用一个“逻辑存在但物理断开”的连接,直接报错。
使用场景:
- 云环境(AWS RDS、阿里云 PolarDB)普遍有 30–60 分钟的连接空闲清理机制
- Kubernetes Pod 重启、Service IP 变更也会中断底层 TCP 连接
实操建议:
- 统一把
maxLifetime设为比数据库wait_timeout少 30–60 秒,例如 DB 是 300 秒,这里设 240000(毫秒) - 避免设为 0(永不过期)或 > 1800000(30 分钟)—— 大多数生产环境不安全
- 如果用了连接池自带的
isValid()检查,确保它走的是轻量探测(如 MySQL 的ping),而非真实 SQL
并发突增时连接池打满的真实表现
不是所有“获取连接超时”都意味着池子小。当 maximumPoolSize 被占满,新请求会在队列里等,等不到就抛 CannotAcquireConnectionException(HikariCP)或阻塞线程(Druid 默认行为)。但更隐蔽的问题是:线程卡在等待连接上,导致 Tomcat 线程池也打满,整个 HTTP 请求开始排队甚至超时,错误日志里却只看到数据库相关异常。
性能影响:
- 连接等待时间计入接口 P99,但监控里可能只显示“DB 响应慢”,掩盖了其实是连接争抢
- 若配置了
acquireRetryDelay或重试逻辑,会放大雪崩风险
实操建议:
- 开启连接池等待指标:HikariCP 的
metricsTrackerFactory,观察poolWait直方图 - 限制单次请求最大连接占用时间,比如 MyBatis 加
timeout属性,或用@Transactional(timeout = 5) - 别依赖“自动扩容”—— HikariCP 不支持运行时调大
maximumPoolSize,改完要重启
真正难调的从来不是数字本身,而是数据库负载、网络稳定性、应用线程模型三者之间的咬合点。一个参数改完没立刻出问题,不等于它在流量毛刺下依然可靠。