数据库连接瓶颈

数据库连接数是稀缺资源:单实例可承载连接有限,应用实例扩张会成倍放大连接需求;用连接池收敛、代理层复用与请求准入控制管理连接数。

连接数为什么成为瓶颈

数据库连接是稀缺资源:每个连接占用服务端内存与少量调度开销,单实例可承载的连接数有上限。麻烦在于需求会成倍放大——应用实例数乘以单实例连接池大小就是总连接数,应用层扩容一次,数据库连接需求同步翻倍。更隐蔽的是长事务与慢查询会长期占用连接,让连接池在高负载时被少数请求耗尽。治理的抓手是:收敛池大小、用代理层复用连接、控制请求准入。

基本信息

问题N 个应用实例 × 池大小 M = 总连接数,易超限
手段数据库代理复用连接;池内收敛空闲连接
配套慢查询与长事务会长期占用连接,需一并治理
核算前提先确认单实例 max_connections 与单连接内存开销

典型使用场景

  • 应用实例扩容:扩容前按倍数换算数据库连接需求并核对上限
  • Serverless 或批量任务:实例数弹性变化时连接数会瞬间冲高
  • 多租户系统:单个租户的慢查询占满连接池,影响其他租户
  • 读写分离架构:写库连接需求与读库不同,需分开核算与限制
  • 数据库迁移或升级:切换期间连接建立变慢,池预热策略要相应调整

连接数放大测算(示例)

层实例数单实例池总连接
应用直连2020400
扩容后直连4020800
经数据库代理复用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)按事务或语句模式复用后端连接
  • 长事务与空闲连接按来源统计,防止池被少数调用方耗尽
  • 扩容应用实例前按倍数换算数据库连接需求并预留余量
  • 把连接池等待时间做成指标:等待时间上升通常早于连接耗尽告警
连接数告警要区分活跃与空闲:只看总量会把池预热误判为异常,而真正危险的是长时间处于事务中空闲状态的连接。
连接池调大不是免费的:更多并发连接意味着数据库内部锁竞争与上下文切换增加,超出拐点后整体吞吐可能下降而非上升。

易错点

  • 每扩容一次应用实例,数据库连接数同步翻倍,触发连接上限报错
  • 长事务占用连接,高峰期连接池耗尽,新请求排队等待或直接失败
  • 连接池配置过大,数据库侧锁与调度开销上升,吞吐不增反降
  • 只监控连接总量不区分状态,空闲连接被当作正常,实际池已被长事务占满
  • 数据库代理按会话模式复用,连接并未真正收敛,复用效果远低于预期

数据来源与参考

  • PostgreSQL 文档:连接管理与 PgBouncer
  • HikariCP 文档:About Pool Sizing
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌