← 中文资源

简体中文版本

PostgreSQL — 连接数耗尽

通过统计活动、空闲与泄漏会话,限制连接池并可靠释放客户端,诊断并修复 PostgreSQL 连接耗尽。

阅读英文原文

症状

出现 FATAL: sorry, too many clients already;新请求超时,而连接数接近 max_connections;负载停止后连接数仍持续上升,说明是泄漏而不是流量尖峰。

诊断

SELECT count(*), state, application_name
FROM pg_stat_activity
GROUP BY state, application_name
ORDER BY count DESC;

大量 idle 会话通常意味着池过大或客户端未释放;大量 active 会话通常意味着慢查询或锁等待。再看 SHOW max_connections; 与数据库大小:提高上限会消耗每连接内存,不是免费的修复。用下面的查询找出长事务和阻塞:

SELECT pid, state, wait_event_type, now() - query_start AS age, left(query, 80)
FROM pg_stat_activity
WHERE state <> 'idle'
ORDER BY age DESC;

根因

打开会话总数(包括空闲池与泄漏客户端)达到服务器 max_connections 上限。新连接在认证完成前即被拒绝,因此应用看到的是连接失败,而不是慢查询。

修复

每个进程使用一个有界连接池,设置较小的最大值;在所有路径(尤其是错误路径)用 finally 释放客户端;配置空闲超时和连接生命周期;实例很多时使用 PgBouncer 或托管连接池。先修复长事务,再考虑提高上限。

const pool = new Pool({
  max: 10,
  idleTimeoutMillis: 30_000,
  connectionTimeoutMillis: 2_000,
});

async function withClient(fn) {
  const client = await pool.connect();
  try {
    return await fn(client);
  } finally {
    client.release();
  }
}

验证

负载测试期间观察 pg_stat_activity,空闲会话应有界并在测试后回落到基线;重复请求循环不应增加连接数;不再出现 FATAL 错误。pg_terminate_backend 会中止事务并可能造成状态不一致,不应作为常规清理手段。