你的数据库正在疯狂新建连接,而你在疯狂重启服务

2026-05-25 12 0

凌晨两点,钉钉响了。报警信息显示:数据库连接数突破 5000,MySQL 开始拒绝连接。

你从床上蹦起来,揉着眼睛打开电脑,心里已经在构思辞职信怎么写。

结果排查了一圈,发现问题根源是:连接池漏了。

是的,就是那个你配完就再也没管过的连接池。

连接池是个什么东西?

简单说,连接池就是应用程序和数据库之间的一个连接缓存池

没有连接池的时候,每次查数据库都要走一遍:建立TCP连接 → 认证 → 执行SQL → 关闭连接。这个过程耗时 1-10ms 不等,高并发下光建连接就能把你耗死。

连接池就是在启动时预建一批连接,用完了还回去而不是真的关闭,下次再用直接从池里拿,省掉了建连接的开销。

听起来很美好对吧?但魔鬼都在细节里。

坑一:连接泄漏——最常见的生产事故

什么是连接泄漏?就是从池里借了连接,用完了忘记还回去。

听起来低级吧?但这绝对是生产环境排名第一的数据库问题。我见过的泄漏场景包括但不限于:

  • 代码抛异常了,没有执行 finally { conn.close(); }
  • 逻辑分支提前 return 了,还没还连接
  • 自定义线程池里用了数据库连接,线程结束连接没还

一次泄漏问题不大,池子大点能撑一阵。但如果是慢查询泄漏,每 10 秒漏一个,用不了多久池子就空了,后续请求全部排队等待。

来看一个典型的事故时间线:

23:00 - 上线新功能,某个异常分支忘记关闭连接
23:15 - 连接池 80% 已借出,开始出现等待
23:30 - 池子耗尽,新请求开始超时
23:45 - 监控报警,值班人员开始排查
00:20 - 定位到问题代码,热修复回滚
00:35 - 服务恢复

浪费了 35 分钟,这 35 分钟本可以通过配置改变。

怎么防?现代连接池基本都有泄漏检测功能。以 HikariCP 为例:

HikariConfig config = new HikariConfig();
config.setMaximumPoolSize(20);
config.setMinimumIdle(5);
// 连接泄漏检测:超过 60 秒未归还则警告
config.setLeakDetectionThreshold(60 * 1000);

开启这个参数后,如果一个连接被借出超过 60 秒还没还,HikariCP 会打印一条警告日志,里面包含借出连接的代码位置。这招在我手上救过好几次。

坑二:连接池大小——拍脑袋重灾区

连接池多大合适?这个问题我问过很多后端,答案五花八门:

  • 「50 应该够了吧」
  • 「我们设了 200,能撑一阵」
  • 「不知道,MySQL 默认 151,我设了 300 应该够了」

这些答案都有一个共同特点:没有思考过程。

连接池大小的计算其实有公式可循,但公式也要结合实际。核心逻辑是:连接数 = (核心数 * 2) + 硬盘速度因子

等等,这个公式哪来的?实际上是 Oracle 官方推荐的公式:

connections = ((核心数 * 2) + 磁盘数量)

这个公式假设应用是 IO 密集型。如果你的应用是 CPU 密集型(纯计算不 IO),连接数应该更低,大概和核心数差不多就够了。

但问题是,大多数应用既不是纯 IO 也不是纯 CPU,所以这个公式只能当基准线,最终还是要靠压测和监控来校准。

我见过最离谱的一个案例:有个服务设了 maximumPoolSize = 1000,理由是「万一并发量上来了呢」。结果数据库服务器连接受不暇,查询延迟从 5ms 飙升到 500ms,整个集群差点雪崩。

连接池不是越大越好!每个连接都要消耗数据库端的内存(InnoDB 每个连接约 2-4MB),设太大反而会把数据库拖死。

坑三:Initial_size 被忽视的真相

很多连接池默认会在启动时建立一定数量的初始连接,这个值叫 initialSizeminimumIdle

但我发现大量项目根本不配置这个值,或者设为 0,让连接池在运行时慢慢爬坡到最大值。

问题来了:如果你的 minimumIdle = 0maximumPoolSize = 50,那么低负载时池子是空的,第一个请求来的时候才建连接。对于老应用(启动后第一个用户请求可能要等好几秒),这种延迟是致命的。

正确做法是设置合理的 minimumIdle,让它等于你的日常并发基准。如果日常 QPS 约 100,50 个并发就够了,那就设 minimumIdle = 30,留点余量。

坑四:没有监控等于盲人骑瞎马

说了这么多,最最重要的一点:你没有监控你的连接池

连接池有哪些指标值得监控?

  • 活跃连接数(active connections)——真正在使用中的连接
  • 空闲连接数(idle connections)——池子里待命的连接
  • 等待获取连接的线程数——这个数字 > 0 就说明池子不够用了
  • 连接获取耗时——正常应该在 1ms 以内,超过 10ms 就该查查了

我的推荐是:Prometheus + Grafana 搭一个连接池监控面板,把这几个指标都加上告警。规则是这样的:

  • 活跃连接数 > 最大池大小的 80% → 告警
  • 等待线程数 > 0 → 告警
  • 连接获取耗时 > 50ms → 告警

有了这套监控,你可以在用户感知到问题之前 30 分钟就发现苗头。

说点真心话

连接池这个话题,技术上一点都不性感。没有高并发那样激动人心的数字,没有微服务那样听起来就很厉害的概念。

但就是这么一个「不起眼」的东西,毁掉了无数个工程师的周末和夜晚。

我现在的习惯是:新项目上线,第一件事配好连接池监控;老项目 review,第一件事看连接池配置。宁可少写一个功能,也要把这个基础设施弄明白。

不然下次凌晨两点响起的,可能就是你的手机了。

相关文章

懒得折腾?让小龙虾帮你一键部署 AI 神器,省心又省力!
懒得折腾?让小龙虾帮你一键部署 AI 神器,省心又省力!
你的API为什么要返回”系统繁忙”?——一个后端人的自我检讨
🦞 小龙虾喊你来白嫖代部署服务!一键搞定 AI 工具,省心省力还省钱
RESTful API 为什么把程序员逼成了”URL装修工”?
删了公司80%的接口文档后,我被夸了

发布评论