云数据库连接池参数怎么定:从「连接数打满」说起
连接数不是越大越好
遇到「数据库连接数打满」的告警,第一反应往往是调大实例的最大连接数。这个方向通常是错的:数据库的连接是重量级资源,每个连接都要占用服务端内存和文件描述符,连接数拉高之后,上下文切换和锁竞争的成本会把吞吐拉下来,表现是「连接够了但更慢了」。
正确的思路是先算清楚需要多少连接,再看实例规格能不能支撑。
连接数的计算
一个常用的估算公式:
所需连接数 ≈ 应用实例数 × 每实例并发线程数 × 单请求平均持锁时间占比
举个具体例子:4 个应用实例,每实例 Tomcat 最大线程 200,但真正同时访问数据库的峰值大约 10%,也就是每实例 20 个并发。那么池大小定在 20~30 之间就够,4 个实例合计 80~120 个连接。
如果实例规格支持的连接上限是 1000,那么 120 是安全的;如果是 200,就要考虑减少应用实例或者把池压小。
池参数之间的联动
连接池有一组参数必须一起看,单独调某一个往往会引入新问题:
- 最大连接数(max):池的上限,超过就排队或抛异常;
- 最小空闲(minIdle):池维持的常驻连接数,太小会导致流量突增时大量新建连接(建连本身很慢);
- 最大等待时间:拿不到连接时等多久,设太大会让请求堆积,设太小会误报失败;
- 空闲连接存活时间:超过这个时间没被使用的连接会被回收;
- 连接最大存活时间:连接的绝对寿命,到点强制重建。
这五个参数的合理组合是:minIdle 取峰值的 30%~50%,max 取峰值的 1.2 倍,maxLifetime 明显小于数据库侧的空闲超时,idleTimeout 小于 maxLifetime。
为什么空闲连接会「突然死掉」
这是最常见也最难查的问题:应用启动时一切正常,跑一段时间后开始零星报连接异常,重启应用又好了。
根因通常在链路上——数据库前面的负载均衡、代理层或者安全组,对空闲连接有超时回收策略。连接在应用侧看起来还活着,实际上对端已经关闭,下一次使用就会报错。
解决办法是让连接池主动做三件事:
- 把
maxLifetime设得比链路空闲超时短,比如链路 10 分钟回收,就把连接寿命设成 8 分钟; - 开启保活探测(validation query 或者 TCP keepalive),让空闲连接定期被验证;
- 开启取连接前校验(testOnBorrow),拿到的连接先 ping 一下,失效的直接丢弃重建。
第三点会带来一点额外开销,但在链路有中间层时几乎是必需的。
连接泄漏怎么发现
另一类问题是连接不释放:代码里开了事务没提交、异常路径漏了 close、或者手动拿了连接忘了归还。表现是池里的连接数持续缓慢上升,最终耗尽。
定位手段有两条:
- 打开池的泄漏检测(leakDetectionThreshold),设置一个明显大于正常请求耗时的阈值,超过就打印持有连接的调用栈;
- 在监控上看活跃连接数与空闲连接数的比例,正常情况下这个比例随流量波动,持续单调上升就是泄漏的信号。
有了调用栈,定位到具体代码行通常很快。没有的话只能靠猜,成本高一个量级。
监控指标该看哪些
连接池的问题几乎都能在指标上提前看到,建议至少采集四项:
- 活跃连接数 / 最大连接数——饱和度;
- 等待获取连接的线程数——排队情况;
- 获取连接的平均耗时和 P99——建连是否变慢;
- 连接创建和销毁的速率——是否在频繁重建。
其中「连接创建速率」最能暴露问题:正常运行时这个值应该接近零,如果持续大于零,说明连接在被反复掐断重建,回头去查 maxLifetime 和链路超时的关系。
小结
连接池参数的调整顺序应该是:先算清需要的连接数,再定 min/max,最后处理连接存活周期和链路超时的匹配。跳过第一步直接调大上限,只是把问题从「连接不够」变成「连接够了但更慢」。
- 点赞
- 收藏
- 关注作者
评论(0)