连接池核心参数详解
连接池的配置,说白了就是一堆数字的博弈。我刚入行那会儿,觉得连接池无非就是设个最大连接数,够用就行。结果线上一次故障让我彻底改了想法——连接池的参数,每一个都藏着坑,调不对就是事故。
这节课,我把几个最关键的参数掰开揉碎讲清楚。你以后配置连接池,心里就有底了。
核心参数全景
先来看一张图。这张图把连接池的核心参数串起来了,你一眼就能看出各参数之间的关系。
这张图把参数分成三组:容量参数、时间参数、校验参数。下面我逐个拆解。
最大连接数(maximumPoolSize)
这是最核心的参数,没有之一。它决定了连接池里最多能有多少个连接同时工作。
核心原则:最大连接数不是越大越好!
为什么这么说?我在一个高并发项目里遇到过,团队把最大连接数设成了 500,结果数据库直接被压垮,CPU 飙到 100%。
你想想看,数据库处理连接是有成本的。一个连接对应一个数据库进程或线程,太多连接会导致上下文切换频繁、锁竞争加剧,最后吞吐量反而下降。
那设置多大合适?我建议用这个公式估算:
最大连接数 = (核数 * 2) + 有效磁盘数
这是一个经典公式。举个例子:一个 8 核服务器,SSD 磁盘(有效磁盘数算 1),那初始值就是 (8*2)+1=17 个连接。然后再根据实际压力测试上下调整。
| 应用场景 | 参考值 | 说明 |
|---|---|---|
| 低并发应用 | 5-10 | 后台管理、报表系统 |
| 中等并发应用 | 10-30 | 一般Web服务 |
| 高并发应用 | 30-50 | 电商核心、实时系统 |
| 极高并发应用 | 50-200 | 需配合数据库优化 |
避坑指南:我曾经见过一个团队把最大连接数设成 1000,结果数据库连接池满了后,应用直接 OOM。因为每个连接都要消耗内存和文件描述符,连接数过大,操作系统先扛不住。
最小空闲连接数(minimumIdle)
这个参数控制连接池里至少保持多少个空闲连接。它的作用就是应对突发流量。
我个人习惯把 minimumIdle 设成和 maximumPoolSize 一样。这样系统启动后就会把连接全部创建好,流量来了直接复用,不会有创建连接的开销。
但这种方式也有代价——连接是空闲的,数据库那边却要一直维护它。如果服务 QPS 很低,就会浪费资源。
所以,这里有个权衡:
- 追求响应速度:minimumIdle 设大一点,最好等于 maximumPoolSize
- 追求资源节约:minimumIdle 设小一点,比如 2-5 个
- 折中方案:minimumIdle = maximumPoolSize / 2
小技巧:如果服务要求启动快,别把 minimumIdle 设太大。否则启动时要创建大量连接,反而拖慢启动速度。可以先设一个较小的 initialPoolSize,等流量上来后让连接池自动扩容。
连接超时(connectionTimeout)
这个参数的意思是:应用从连接池里拿连接时,最多愿意等多久。等不到就抛异常。
超时时间太短,系统很容易误报故障。太长呢,用户就在那里傻等着,体验极差。
我处理过一个案例:一个团队把 connectionTimeout 设成了 500 毫秒。数据库偶尔抖动一下,应用就报连接获取超时,整条链路都在报错。后来我们改成 5 秒,抖动时系统就扛过去了。
| 场景 | 推荐值 | 理由 |
|---|---|---|
| 快速失败 | 1-3 秒 | 对延迟敏感,宁可失败也不等待 |
| 常规服务 | 3-5 秒 | 兼顾响应与可用性 |
| 慢查询场景 | 5-15 秒 | 数据库处理本身就慢,多给点时间 |
注意:connectionTimeout 只控制“从池中获取连接”的等待时间,不控制 SQL 执行时间。SQL 执行时间超时要用 statementTimeout。
连接存活时间(maxLifetime)
每个连接在池子里待久了,就可能被数据库那边断开。比如 MySQL 的 wait_timeout 默认 8 小时,如果你不设 maxLifetime,连接就会变成一个坏连接,但应用不知道。
我个人建议 maxLifetime 设成小于数据库的 wait_timeout。比如数据库 wait_timeout 是 30 分钟,那 maxLifetime 就设 25 分钟。这样在数据库断开之前,连接池主动把连接淘汰掉,换新的。
// HikariCP 推荐配置示例
spring.datasource.hikari.maxLifetime=1800000 // 30分钟
注意:maxLifetime 不能设得太短,否则连接频繁被换掉,造成不必要的创建开销。一般 15-30 分钟是比较合理的区间。
空闲超时(idleTimeout)
如果一个连接空闲太久了,连接池会把它关掉,释放资源。这个参数只在 minimumIdle 小于 maximumPoolSize 时有意义。
为什么?因为如果 minimumIdle 等于 maximumPoolSize,空闲连接数是固定的,永远不会有“多余的空闲连接”,idleTimeout 就没用。
我通常把 idleTimeout 设为 maxLifetime 的一半。比如 maxLifetime 是 30 分钟,idleTimeout 就设 15 分钟。这样空闲连接不会一直占着资源,又能应对突发的小流量。
spring.datasource.hikari.idleTimeout=600000 // 10分钟
连接校验参数
这几个参数决定连接池怎么判断一个连接是不是好的。坏连接千万别发给应用,否则就是一堆 SQLException。
- testOnBorrow:拿连接时校验。最严格,但每次拿连接都有开销
- testOnReturn:归还时校验。能保证还回来的连接是好的
- testWhileIdle:后台线程定时校验。不影响业务线程,推荐
对于高性能场景,我建议关闭 testOnBorrow 和 testOnReturn,只开 testWhileIdle。坏连接被后台线程发现后直接淘汰,下次拿到的都是好连接,不会有性能损耗。
最佳实践:高性能场景推荐方案
testOnBorrow = false
testOnReturn = false
testWhileIdle = true
validationQuery = SELECT 1
参数调优的黄金法则
说了这么多,我最后给你几条铁律。这些是我踩过坑后总结出来的:
- 先压测,再调参。没有压力测试数据,所有参数都是猜的
- 一次只改一个参数。改了连接数又改超时时间,出了问题你根本不知道是谁的锅
- 监控连接池状态。看活跃连接数、空闲连接数、等待获取连接的线程数,这些数据比参数本身更值钱
- 参数要和业务匹配。读多写少的服务和写多读少的服务,参数配置完全不同
我参与过一个项目,上线前把所有连接池参数都翻了一遍,全部按理论值设好。结果上线后压测直接就挂了。后来我们把所有参数调回默认值,只改了一个 maxPoolSize,反而好了。所以记住:默认值通常是最好的起步点。
调参是一场持续的优化过程。你不可能一次就找到最佳值。把监控做好,有了数据支撑,再慢慢调整,那才是正确的路子。