PostgreSQL 连接故障通常表现为两种:connection timed out 和 connection refused。很多情况下两者被混在一起处理,导致排查方向出错。前者说明客户端在超时时间内没有收到任何 TCP 响应,比如 SYN 包发出后没有 SYN-ACK 回来;后者说明客户端收到了明确的拒绝响应,通常是目标端口没有服务监听,或防火墙返回了 RST。理解这一点后,再结合 pg_isready、nc、psql 等工具,就能快速确定问题在网络链路、服务端配置还是访问控制层。

一、先分清连接超时和拒绝连接
连接超时和拒绝连接虽然都导致客户端无法建立到 PostgreSQL 的会话,但它们在 TCP 层的表现完全不同。connection timed out 一般表示客户端发出的 SYN 包没有在超时窗口内收到 SYN-ACK,通常与网络不可达、防火墙静默丢弃、云安全组未放行、路由黑洞或服务端监听地址配置错误有关。而 connection refused 表示目标主机明确返回了 RST 或 ICMP 不可达,常见于 PostgreSQL 服务没有启动、端口号写错、服务监听在 Unix socket 而未监听 TCP,以及防火墙配置为 reject 而不是 drop。
快速区分的一种办法是使用 nc 或 pg_isready。如果 nc -zv 长时间无响应,大概率是网络链路或防火墙丢包;如果立刻返回 connection refused,则说明主机可达但端口没有服务。使用 pg_isready 可以得到更接近数据库层的状态:返回 0 表示可以连接,返回 1 表示拒绝,返回 2 表示超时或不可达。运维人员应当先记录下这个返回值,再决定下一步是查网络还是查 PostgreSQL 配置。
下面的命令可以一次完成基础探测:
# 检查 TCP 连接是否可建立 pg_isready -h 192.168.10.20 -p 5432 -d postgres # 查看端口是否有服务响应 nc -zv 192.168.10.20 5432 # 用 psql 测试连接,限制超时为 5 秒 psql "host=192.168.10.20 port=5432 dbname=postgres connect_timeout=5"
如果 pg_isready 返回 2,先不要直接修改 PostgreSQL 参数,而应确认网络路径。可以在数据库服务器本机执行 ss -lntp | grep 5432 查看端口监听状态,或使用 telnet 从客户端测试。只有确认 TCP 层正常后,再继续检查服务端配置。
二、从服务端配置排查监听与访问控制
当本机可以连接但远程客户端无法连接时,最常见的两个配置文件是 postgresql.conf 和 pg_hba.conf。在 postgresql.conf 中,listen_addresses 默认可能只包含 localhost,此时远程连接会表现为 connection refused 或超时,取决于网络结构。将其改为 * 或显式列出网卡地址后,需要重启或执行 pg_ctl reload 使配置生效。同时要确认 port 是否与客户端指定端口一致,以及 unix_socket_directories 是否覆盖了本地连接路径。
下面是一个常见的服务端监听配置片段:
# 监听所有网卡 listen_addresses = '*' # 监听端口 port = 5432 # 最大连接数 max_connections = 200 # 超级用户保留连接数 superuser_reserved_connections = 3
即使 TCP 监听正常,如果 pg_hba.conf 中没有允许客户端来源地址的规则,或者认证方法配置错误,也会出现连接失败。典型错误是远程主机匹配到 reject 规则,或者使用 md5 时密码在传输中被修改,导致服务器返回 password authentication failed。需要注意 pg_hba.conf 是按顺序匹配的,第一条匹配规则生效,因此应把更具体的地址段放在前面。
下面是一个允许内网网段的配置示例:
# TYPE DATABASE USER ADDRESS METHOD host all all 192.168.10.0/24 scram-sha-256 host all all 0.0.0.0/0 md5 local all all peer
修改 pg_hba.conf 后应执行 SELECT pg_reload_conf(); 或 pg_ctl reload,并查看 PostgreSQL 日志确认没有语法错误。日志中如果出现 no pg_hba.conf entry for host,说明来源地址没有被任何规则覆盖;如果出现 SSL off 相关提示,则可能是驱动和服务端 SSL 配置不一致。
三、客户端连接参数与连接池常见问题
很多连接超时问题并不在数据库服务端,而在客户端驱动或连接池的参数设置。以 JDBC 为例,connectTimeout 控制建立 TCP 连接的超时时间,socketTimeout 控制 socket 读取数据的超时时间,而 loginTimeout 控制认证阶段的等待时间。如果只设置了 socketTimeout 而没有设置 connectTimeout,在网络异常时客户端可能长时间阻塞。连接池中的 connectionTimeout 则表示从池中获取连接的最大等待时间,与数据库宕机无关,但常被误判为数据库超时。
如下 JDBC URL 同时指定了连接、读取和登录超时:
String url = "jdbc:postgresql://192.168.10.20:5432/postgres?" +
"connectTimeout=10&socketTimeout=30&loginTimeout=5";
Properties props = new Properties();
props.setProperty("user", "app_user");
props.setProperty("password", "secret");
Connection conn = DriverManager.getConnection(url, props);
另一个常见场景是连接池耗尽。当业务并发超过 max_connections 或连接池最大池大小,应用会报获取连接超时,例如 HikariCP 的 connection is not available, request timed out。此时即使 PostgreSQL 服务本身正常,也会表现为连接超时。数据库管理员应检查 pg_stat_activity 中的连接数、等待事件和空闲会话,确认是否有长事务或未关闭的连接占用了资源。
此外,如果客户端与数据库之间经过负载均衡或代理,还需要关注代理自身的超时设置。比如 pgBouncer 的 server_connect_timeout 和 query_timeout 会影响后端连接表现。修改连接参数后应进行压力验证,避免只调整客户端而忽略服务端资源限制。
四、系统化排查步骤与预防措施
为了减少故障恢复时间,建议按照从下到上的顺序排查:物理网络、云安全组或防火墙、操作系统端口监听、PostgreSQL 服务状态、配置文件、客户端参数和应用连接池。每一步都应记录明确结果,例如 nc -zv 是否返回成功、pg_isready 返回码、日志中的具体错误行。不要在没有确认端口可达的情况下直接修改 pg_hba.conf,否则可能掩盖真正问题。
可以通过以下 SQL 查看当前连接数和状态分布:
SELECT count(*) AS total,
count(*) FILTER (WHERE state = 'active') AS active,
count(*) FILTER (WHERE state = 'idle') AS idle
FROM pg_stat_activity;
SELECT count(*) AS used,
current_setting('max_connections')::int AS max_conn,
current_setting('superuser_reserved_connections')::int AS reserved
FROM pg_stat_activity;
如果连接数接近上限,优先排查是否存在 idle in transaction 的会话,这类会话会持有连接并阻塞其他请求。可以设置 idle_in_transaction_session_timeout 自动终止长时间空闲的事务。对于突发流量,可以引入 pgBouncer 连接池,将大量客户端连接收敛为少量后端连接,同时在应用侧限制连接池最大活跃连接数,避免数据库被打满。
最后,建议在监控系统中对 pg_stat_database 的连接数、等待事件和错误日志设置告警。例如当活跃连接超过最大连接数的 80% 时触发通知,当出现 connection refused 或 no pg_hba.conf entry 时立即通知数据库管理员。定期演练故障切换和网络隔离场景,也能帮助团队熟悉排查路径,减少线上恢复时间。

