连接越多,越忙越慢
微服务拆了十几个,每个应用都默认开 100 个池连接,数据库 max_connections 干到 5000——结果大促时反而雪崩。这篇讲清连接池的三个反直觉事实,以及 HikariCP 的正确配法。连接池是复用资源,不是并行开关,这是全部结论的出发点。
一台 8 核机器该有几个活跃连接
一条 CPU 密集的 SQL 一次只占一个核;两条同时跑在 8 核机器上毫无损失,八条正好满载——第 9 条开始排队,第 33 条开始剧烈上下文切换,CPU 时间大量花在切换而非执行。PostgreSQL 社区那条著名经验公式放 MySQL 同样成立:
# 活跃连接参考:核数 × 2 + 磁盘数(SSD 时代第二项影响很小)
# 8 核服务器:约 17-25 个活跃连接足够跑满
connections = cores * 2 + effective_spindle_count对,你没看错——8 核数据库机器的甜点区就在二十来个活跃连接,上千连接只会互相踩踏。池子里多出的连接平时闲置,高峰期一拥而上才致命。
HikariCP 必调参数
maximumPoolSize: 20 # 池上限:从上面的甜点区倒推
minimumIdle: 20 # 与上限一致,避免流量波动时借还连接
connectionTimeout: 3000 # 借不到连接最多等多久(毫秒),快速失败
maxLifetime: 1500000 # 连接最长寿命(毫秒),必须小于 MySQL wait_timeout逐个解释:
- maximumPoolSize:重点不是拍 20 这个数,而是「所有应用实例的池上限之和 × 业务峰值倍数」要能被数据库消化。10 个服务实例 × 20 = 200 个连接上限,远低于 max_connections=1000,留足运维与中间件的余量;
- minimumIdle:HikariCP 官方建议与上限一致,固定池减少流量突增时的建连风暴;
- connectionTimeout:别用默认 30 秒。池子满了说明数据库或慢 SQL 出问题,3 秒快速失败给限流降级留活路,否则线程全挂在借连接上,服务假死;
- maxLifetime:MySQL 侧
wait_timeout(默认 8 小时)一到会单方面踢掉空闲连接,应用这边再用就是经典的 broken pipe。把 maxLifetime 设得明显小于 wait_timeout(比如 25 分钟),让池子主动轮换,永不踩线。
连接风暴:三连击事故
老王的雪崩链条复盘:慢 SQL 占住池连接 → 池子耗尽,新请求排队 30 秒超时 → 超时重试瞬间涌来三倍请求,池子重建连接打满 max_connections → 数据库 CPU 被上下文切换吃满,全体死亡。慢 SQL 才是第一因,连接池只是放大器——治理顺序永远是:先治 SQL(前 15 篇),再限流熔断,最后才动连接参数。
数据库侧的两个呼应
- max_connections:与所有应用池上限对账,别无脑给万级(第 14 篇的数学题);
- wait_timeout:确认值,确保所有应用的 maxLifetime 都小于它;中间有代理(ProxySQL 等)时注意代理自身的空闲超时更短的情况,以最短的为准。
性能优化三部曲(参数、仪表、连接池)收官。下一篇回到表本身:钱为什么不能用 double、手机号为什么别用 int——字段设计里全是这种「当时图省事、日后要人命」的坑。
咖啡凉了,记得趁热喝。
评论 (0)