连接数为什么成为瓶颈
数据库连接是稀缺资源:每个连接占用服务端内存与少量调度开销,单实例可承载的连接数有上限。麻烦在于需求会成倍放大——应用实例数乘以单实例连接池大小就是总连接数,应用层扩容一次,数据库连接需求同步翻倍。更隐蔽的是长事务与慢查询会长期占用连接,让连接池在高负载时被少数请求耗尽。治理的抓手是:收敛池大小、用代理层复用连接、控制请求准入。
基本信息
| 问题 | N 个应用实例 × 池大小 M = 总连接数,易超限 |
|---|---|
| 手段 | 数据库代理复用连接;池内收敛空闲连接 |
| 配套 | 慢查询与长事务会长期占用连接,需一并治理 |
| 核算前提 | 先确认单实例 max_connections 与单连接内存开销 |
典型使用场景
- 应用实例扩容:扩容前按倍数换算数据库连接需求并核对上限
- Serverless 或批量任务:实例数弹性变化时连接数会瞬间冲高
- 多租户系统:单个租户的慢查询占满连接池,影响其他租户
- 读写分离架构:写库连接需求与读库不同,需分开核算与限制
- 数据库迁移或升级:切换期间连接建立变慢,池预热策略要相应调整
连接数放大测算(示例)
| 层 | 实例数 | 单实例池 | 总连接 |
|---|---|---|---|
| 应用直连 | 20 | 20 | 400 |
| 扩容后直连 | 40 | 20 | 800 |
| 经数据库代理复用 | 40 | 池化收敛 | 约 100–200 |
排查要点(PostgreSQL 示例)
排查要点(PostgreSQL 示例)
-- 当前连接数与状态分布
SELECT state, count(*) FROM pg_stat_activity GROUP BY state;
-- 按来源统计,找出占用最多的应用或客户端
SELECT client_addr, count(*) FROM pg_stat_activity GROUP BY 1 ORDER BY 2 DESC;
-- 长期处于 idle in transaction 的连接是重点排查对象
SELECT pid, now() - state_change AS dur, query
FROM pg_stat_activity WHERE state = 'idle in transaction' ORDER BY dur DESC;治理要点
- 先核算 max_connections 与单连接内存开销,再决定扩容上限
- 代理层(如 PgBouncer)按事务或语句模式复用后端连接
- 长事务与空闲连接按来源统计,防止池被少数调用方耗尽
- 扩容应用实例前按倍数换算数据库连接需求并预留余量
- 把连接池等待时间做成指标:等待时间上升通常早于连接耗尽告警
连接数告警要区分活跃与空闲:只看总量会把池预热误判为异常,而真正危险的是长时间处于事务中空闲状态的连接。
连接池调大不是免费的:更多并发连接意味着数据库内部锁竞争与上下文切换增加,超出拐点后整体吞吐可能下降而非上升。