FATAL: sorry, too many clients already。pg_stat_activity 统计连接状态、用户和来源。superuser_reserved_connections 为什么会掩盖连接耗尽。安装 Docker,本机 5433 端口空闲。实验使用独立容器,max_connections 故意设得很小。
docker run -d --name lab-pg \
-e POSTGRES_PASSWORD=labpass \
-e POSTGRES_DB=lab \
-p 5433:5432 \
postgres:16 -c max_connections=20 -c superuser_reserved_connections=3
until docker exec lab-pg pg_isready -U postgres >/dev/null 2>&1; do sleep 2; done
docker exec -i lab-pg psql -U postgres -d lab <<'SQL'
CREATE USER app WITH PASSWORD 'apppass';
GRANT CONNECT ON DATABASE lab TO app;
SQL
创建非超级用户 app 很关键,否则它会占用超级用户保留连接,实验现象不准确。
在容器内启动 20 个长期持有连接的客户端,每个执行 pg_sleep(600):
docker exec -d lab-pg bash -c \
'for i in $(seq 1 20); do PGPASSWORD=apppass psql -U app -d lab -c "SELECT pg_sleep(600)" >/dev/null 2>&1 & done; wait'
sleep 3
docker exec lab-pg psql -U app -d lab -c 'SELECT 1'
普通用户 app 的新连接应报 FATAL: sorry, too many clients already。20 个并发会话中只有约 17 个能建立,因为 3 个连接槽位保留给超级用户。
docker exec lab-pg psql -U postgres -d lab \
-c "SELECT count(*), state FROM pg_stat_activity GROUP BY state ORDER BY 2;"
docker exec lab-pg psql -U postgres -d lab -c "SHOW max_connections;"
docker exec lab-pg psql -U postgres -d lab -c "SHOW superuser_reserved_connections;"
docker exec lab-pg psql -U postgres -d lab \
-c "SELECT usename, application_name, client_addr, state, count(*)
FROM pg_stat_activity GROUP BY 1,2,3,4 ORDER BY 5 DESC;"
docker exec lab-pg psql -U app -d lab -c 'SELECT 1'
预期 pg_stat_activity 中 app 的 active 连接接近上限,postgres 仍能新建连接;普通用户的新连接被拒绝。连接状态以 active 为主,因为每个会话都在执行 pg_sleep。
too many clients already;服务未监听报 connection refused;认证失败报 password authentication failed。application_name、client_addr 聚合连接,判断是单个实例池过大、连接泄漏,还是多个应用叠加超过上限。state:idle in transaction 长期占用连接通常意味着事务未提交;active 过多要看慢查询和阻塞。superuser_reserved_connections 在起作用,不代表普通应用连接充足。另一个误判是直接调大 max_connections;每个后端都占内存,盲目调大可能把问题变成内存不足。ulimit -n,连接数上去后可能先在 OS 层失败。先终止实验连接,验证普通用户恢复:
docker exec lab-pg psql -U postgres -d lab \
-c "SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE usename = 'app';"
docker exec lab-pg psql -U app -d lab -c 'SELECT 1'
生产环境应使用 PgBouncer 等连接池,按应用实例数计算池上限,设置连接最大生命周期,修复连接泄漏,并配置 idle_in_transaction_session_timeout。只有在评估内存和连接来源后才考虑提高 max_connections。
docker exec lab-pg psql -U postgres -d lab \
-c "SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE usename = 'app';" \
2>/dev/null || true
docker rm -f lab-pg
容器删除后所有连接和数据消失。若容器仍在运行但连接打满,也可以直接用 docker restart lab-pg 强制断开所有会话,但生产环境不要这样做。
idle in transaction 连接过多通常说明什么?max_connections 前需要评估什么?superuser_reserved_connections 为超级用户保留了专用槽位,普通用户已被拒绝时超级用户仍能连接。