“too many clients” 不是网络不通,也不是账号密码错了,而是 PostgreSQL 已经不允许新的客户端连接进来。此时继续重试、重启应用或加连接池参数,通常都不会让报错消失,真正要做的是先判断连接被谁占满,再决定是清理、限流还是调整上限。本文提供一个可执行的判断路径:先确认场景,再按顺序处理,最后验证结果,并说明哪些边界不能忽略。
遇到 “too many clients” 时,先不要调大 max_connections。应先在还能连上的会话里查询 pg_stat_activity,确认占用连接的是业务连接池、空闲事务还是长查询,并核对 max_connections 与保留连接参数。处理顺序通常是:清理明显异常的会话、修复连接泄漏、再评估是否调整上限。调大上限只是把压力推给内存和调度,需要结合机器规格确认,且不能替代连接池治理。
先确认当前场景和目标
这个问题成立的前提是:报错来自 PostgreSQL 服务端,而不是应用侧连接池自己的上限。应用报 “too many clients already” 一般说明服务端拒绝新建连接;如果只是某个连接池提示池已满,而数据库端还有余量,那问题在应用侧排队策略,处理方式完全不同。
动手前先确认几件事:
- 报错原文和出现位置:是应用日志、驱动异常,还是 psql 直接连接时报的。psql 也连不上,说明服务端连接槽位确实耗尽;只有某个应用报错,则可能是该应用池配得过小或泄漏。
- 当前 max_connections 和 superuser_reserved_connections 的实际值。保留连接数决定超级用户还能挤进去几个,排查时通常靠它登录。
- 是否有连接池中间件(如 PgBouncer)在数据库前面。有池的话,数据库侧连接数应相对稳定,暴涨往往意味着池配置或会话模式有问题。
- 业务是否处于发布、批量任务、报表导出等高峰时段,这决定是临时峰值还是持续性泄漏。
目标也要先定清楚:是撑过当前故障让业务恢复,还是降低长期连接占用。前者允许先清理会话止血,后者必须动连接池和代码,两者不要混为一谈。
按这个顺序处理
第一步,用超级用户或保留连接登录,观察连接分布。下面这条查询按状态和来源聚合,先看整体结构,不要一上来就逐个杀会话:

SELECT state, count(*) AS conns,
max(now() - state_change) AS max_idle
FROM pg_stat_activity
GROUP BY state
ORDER BY conns DESC;
如果 idle 占比很高,通常是应用拿了连接不释放,或者连接池的 idle 超时设置过长;如果 idle in transaction 明显,说明有事务开着却不在干活,这类会话既占连接又可能持有锁,优先处理;如果是 active 且持续增长,则要看是不是慢查询或并发突增。
第二步,定位具体来源。按应用名、用户、客户端地址分组,找出是谁占的:
SELECT application_name, usename, client_addr,
state, count(*) AS conns
FROM pg_stat_activity
GROUP BY application_name, usename, client_addr, state
ORDER BY conns DESC
LIMIT 20;
第三步,对确认异常的会话做定向清理。只终止明确属于泄漏或卡死的会话,不要整批杀,避免影响正常业务:
SELECT pg_terminate_backend(pid)
FROM pg_stat_activity
WHERE state = 'idle in transaction'
AND now() - state_change > interval '10 minutes'
AND pid <> pg_backend_pid();
时间阈值要结合业务事务的正常耗时来定,不能照搬。执行前先用同样的 WHERE 条件 SELECT 一遍,确认要终止的列表符合预期。
第四步,回到根因。应用侧检查连接是否在异常分支里没有 close 或归还池;连接池的最大连接数乘以实例数,是否已经超过数据库的 max_connections;有没有把连接池配置成“每个请求新建连接”。这些问题不修,清理完很快会再次占满。

第五步,再评估是否调整服务端参数。调整 max_connections 需要重启或 reload 才能生效,并且每个连接都会占用一定内存,连接数越高,内存和调度压力越大。通常建议先通过连接池把数据库侧并发收敛到一个可控范围,再考虑小幅上调,而不是直接把上限翻倍。
改完后怎么验证
清理或改配置后,不要只看“当前能连上”就结束。可以先做几个观察动作:
- 重复执行按 state 聚合的查询,确认
idle in transaction的数量回落到接近零,总连接数低于 max_connections 的八成左右,留出余量应对突发。 - 用普通业务账号新建一次连接,确认不再报 too many clients;同时确认原有业务连接没有被误杀。
- 观察一段时间内的连接数曲线,看是否呈锯齿状反复冲高。如果清理后短时间内又涨回上限,说明泄漏或池配置问题仍在。
- 检查数据库日志中是否还有连接被拒的记录,以及是否有 FATAL 级别的认证或资源报错。
如果调整过参数,用 SHOW max_connections; 确认实际生效值,并确认应用侧连接池上限与数据库上限之间的倍数关系符合预期。
容易忽略的边界
第一,调大 max_connections 不是无损操作。它受内存约束,连接数过高会加剧上下文切换,某些场景下反而让整体响应变慢。在没有确认内存余量和实际并发需求前,不建议大幅上调。

第二,superuser_reserved_connections 是排查时的保命通道,不建议为了腾出连接而把它改成 0,否则故障时将无法用管理员身份登录处理。
第三,托管数据库通常对 max_connections 有实例规格绑定,可调范围有限,且修改可能需要重启。云上环境要先确认控制台允许的参数范围,不要直接照搬自建库的经验。
第四,终止会话属于有损操作。被终止的会话对应的事务会回滚,长事务回滚可能耗时并占用资源。对承载写入的会话执行前,应确认可以接受该事务丢失。
第五,PgBouncer 的 transaction 模式能显著降低数据库侧连接数,但它会改变会话级特性(如会话级临时表、部分 SET 行为)的语义,迁移前需要结合应用用法确认兼容性。若无法确认,回退到 session 模式或维持原状,比贸然切换更稳妥。