Skip to content

监听地址与端口配置 ​

监听地址(Listen Address)与端口决定 PostgreSQL 在哪些网络接口上「开门迎客」。它是最基础的一道边界:门开得太大,pg_hba.conf 再严也只是第二道防线;门开得太小,应用又连不上。

前置知识

阅读本节前,建议先了解:pg_hba.conf 访问控制

基础概念 ​

PostgreSQL 服务进程默认只监听本机回环地址,这是「安全优先」的设计取舍。要让其他机器连上,必须同时满足三个条件:

层次配置项作用
监听层listen_addresses服务在哪些网卡上接受 TCP 连接
监听层port监听的 TCP 端口,默认 5432
准入层pg_hba.conf该来源 + 库 + 用户是否放行
网络层防火墙 / 安全组数据包能否到达该端口

只改 listen_addresses 而不改 pg_hba.conf,连接仍会被拒绝(报 no pg_hba.conf entry);反之只改 pg_hba.conf 而 listen_addresses 仍是 localhost,外部连 TCP 时会被直接拒绝(Connection refused)。两者必须同时正确。

listen_addresses 的取值语义:

取值含义典型场景
localhost仅回环(127.0.0.1 与 ::1),默认值单机应用,仅本机连接
*监听所有可用 IPv4 与 IPv6 接口需要外部访问,靠 pg_hba.conf 与防火墙收窄
0.0.0.0仅所有 IPv4 接口明确只支持 IPv4 的环境
::仅所有 IPv6 接口纯 IPv6 环境
10.0.0.5,127.0.0.1指定多个地址,逗号分隔推荐:只监听内网网卡 + 回环
空字符串 ''完全不监听 TCP,仅 Unix 套接字极高安全要求、仅本机连接

推荐做法是显式列出内网地址,而不是用 *。这样即使防火墙配错,公网网卡上也没有服务在监听。

术语英文名说明
监听地址Listen Addresspostgresql.conf 中的 listen_addresses 参数
回环地址Loopback Address127.0.0.1 / ::1,仅本机可达
通配监听Wildcard Listen*,监听所有接口
Unix 域套接字Unix Domain Socket本机进程间通信通道,路径由 unix_socket_directories 指定
连接槽Connection Slot每个连接占用一个服务端进程
最大连接数max_connections全局连接上限,默认 100
超级用户保留连接superuser_reserved_connections为超级用户预留的连接槽

语法与用法 ​

postgresql.conf 相关参数 ​

ini
# /var/lib/pgsql/18/data/postgresql.conf

# ── 监听地址 ──
# 仅本机(默认,最安全)
listen_addresses = 'localhost'

# 监听所有接口(需靠 pg_hba.conf 与防火墙收窄,慎用)
# listen_addresses = '*'
# 不推荐

# 推荐:只监听内网网卡 + 回环
# listen_addresses = '10.10.1.5,127.0.0.1'

# ── 端口 ──
port = 5432

# ── Unix 域套接字目录(本机连接,绕过 TCP,性能更好) ──
unix_socket_directories = '/var/run/postgresql,/tmp'

# ── 连接规模 ──
max_connections = 200
superuser_reserved_connections = 3
ini
# ── 与连接体验相关的参数 ──
# 客户端认证超时:防止空闲连接占用槽位,默认 60 秒
authentication_timeout = 30

# TCP keepalive:及时发现被网络中断的「僵尸连接」
tcp_keepalives_idle = 120
tcp_keepalives_interval = 30
tcp_keepalives_count = 5

# 空闲事务超时(在 postgresql.conf 中设置默认值)
idle_in_transaction_session_timeout = '10min'
idle_session_timeout = '30min'

重要:listen_addresses 与 port 属于需要重启的参数,reload 不生效。

bash
# 修改后必须重启
sudo systemctl restart postgresql

# 确认服务真的监听成功
sudo ss -lntp | grep 5432

查看当前生效值 ​

sql
-- 查看监听配置
SHOW listen_addresses;
SHOW port;
SHOW unix_socket_directories;
SHOW max_connections;

-- 从 pg_settings 中看更完整的元信息(含是否需重启、来源文件)
SELECT name, setting, unit, boot_val, source, pending_restart
FROM pg_settings
WHERE name IN ('listen_addresses', 'port', 'max_connections',
               'unix_socket_directories', 'superuser_reserved_connections')
ORDER BY name;
text
# 输出示例
            name             |     setting     | unit |  source  | pending_restart
-----------------------------+-----------------+------+----------+-----------------
 listen_addresses            | localhost       |      | default  | f
 max_connections             | 200             |      | file     | f
 port                        | 5432            |      | file     | f
 superuser_reserved_connections | 3            |      | default  | f
 unix_socket_directories     | /var/run/postgresql,/tmp | | file   | f

查看实际连接来源 ​

sql
-- 查看当前所有连接:从哪来、用什么方式认证
SELECT pid,
       usename,
       application_name,
       client_addr,
       client_port,
       ssl,
       state,
       backend_start
FROM pg_stat_activity
WHERE backend_type = 'client backend'
ORDER BY backend_start;

-- 按客户端地址聚合,快速看出有哪些机器在连
SELECT coalesce(client_addr::text, 'local socket') AS client,
       count(*)                                    AS conns,
       string_agg(DISTINCT usename, ', ')          AS users
FROM pg_stat_activity
WHERE backend_type = 'client backend'
GROUP BY 1
ORDER BY conns DESC;

详细说明 ​

Unix 域套接字 vs TCP ​

本机连接有两条路径,优先使用 Unix 域套接字:

维度Unix 域套接字TCP(回环)
连接字符串省略 host,或用 host=/var/run/postgresqlhost=127.0.0.1
路径参数unix_socket_directories—
性能略高(无网络栈开销)略低
认证方式支持 peer 与 ident只支持口令 / 证书类
pg_hba.conf 类型localhost / hostssl
可被网络嗅探否明文时是(sslmode=disable)
适合单机应用、管理脚本容器、跨主机
bash
# 使用 Unix 域套接字(默认行为)
psql -h /var/run/postgresql -U app_user -d demo

# 或在连接串中显式指定
psql "host=/var/run/postgresql dbname=demo user=app_user"

# 使用 TCP 回环
psql "host=127.0.0.1 port=5432 dbname=demo user=app_user"
sql
-- 在服务端区分连接来源
SELECT pid, usename,
       CASE WHEN client_addr IS NULL THEN 'unix socket' ELSE client_addr::text END AS via
FROM pg_stat_activity
WHERE backend_type = 'client backend';

端口冲突与多实例 ​

bash
# 检查端口占用(Linux)
sudo ss -lntp | grep -E ':5432|:5433'
sudo lsof -iTCP -sTCP:LISTEN -P -n | grep postgres

# macOS
sudo lsof -nP -iTCP:5432 -sTCP:LISTEN
bash
# 多实例共存:为每个集群分配不同端口与 socket 目录
# 实例 A(主库,默认端口)
# postgresql.conf: port = 5432
# postgresql.conf: unix_socket_directories = '/var/run/postgresql'

# 实例 B(测试库)
# postgresql.conf: port = 5433
# postgresql.conf: unix_socket_directories = '/var/run/postgresql-18b'

# 用 pg_ctl 分别启动
sudo -u postgres pg_ctl -D /var/lib/pgsql/18/data      -o "-p 5432"  start
sudo -u postgres pg_ctl -D /var/lib/pgsql/18b/data     -o "-p 5433"  start
bash
# 连接非默认端口
psql -h 127.0.0.1 -p 5433 -U app_user -d demo

# 或使用环境变量
PGPORT=5433 psql -h 127.0.0.1 -U app_user -d demo

连接数规划 ​

连接数不是「越大越好」:每个连接是一个操作系统进程,占用内存(work_mem 等按连接分配)与调度开销。

参数默认值说明
max_connections100全局上限,修改需重启
superuser_reserved_connections3为超级用户预留,普通角色无法占用
reserved_connections0预留槽位,配合 pg_use_reserved_connections 使用
rolconnlimit-1单角色上限(在角色上设置,见 postgres-user)
sql
-- 检查连接预算:理论可用槽位 = max_connections - superuser_reserved_connections
SELECT current_setting('max_connections')::int                            AS max_conn,
       current_setting('superuser_reserved_connections')::int             AS reserved,
       current_setting('max_connections')::int
         - current_setting('superuser_reserved_connections')::int         AS usable;

-- 连接占用率(生产建议持续监控,超过 80% 就该告警)
SELECT count(*)                                                   AS used,
       current_setting('max_connections')::int                     AS max_conn,
       round(100.0 * count(*) / current_setting('max_connections')::int, 1) AS pct
FROM pg_stat_activity;

-- 按状态细分,识别连接是否需要连接池
SELECT state, count(*)
FROM pg_stat_activity
GROUP BY state
ORDER BY count(*) DESC;
text
# 输出示例
        state        | count
---------------------+-------
 idle                |   142
 active              |     9
 idle in transaction |     3
(3 rows)

idle 数量远大于 active,通常意味着应用侧缺少连接池,而不是数据库需要更多连接。

实战示例 ​

示例一:从「仅本机」改造成「内网可访问」 ​

目标:让 10.10.0.0/16 网段的应用服务器可以连接本机的 demo 库。

bash
# 第 0 步:确认本机内网地址(不要凭记忆写)
ip -4 addr show | grep -w inet
#    inet 10.10.1.5/24 brd 10.10.1.255 scope global eth0

# macOS
ipconfig getifaddr en0
ini
# 第 1 步:编辑 postgresql.conf(改这两项都要重启)
# listen_addresses = '10.10.1.5,127.0.0.1'
# port = 5432
text
# 第 2 步:编辑 pg_hba.conf(reload 即可生效)
# TYPE   DATABASE  USER      ADDRESS         METHOD
hostssl  demo      app_user  10.10.0.0/16    scram-sha-256
host     all       all       10.10.0.0/16    reject
bash
# 第 3 步:防火墙放行(只对本网段开放)
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="10.10.0.0/16" port port="5432" protocol="tcp" accept'
sudo firewall-cmd --reload

# Ubuntu / ufw
sudo ufw allow from 10.10.0.0/16 to any port 5432 proto tcp

# 第 4 步:重启并验证监听
sudo systemctl restart postgresql
sudo ss -lntp | grep 5432
#   LISTEN 0  244  10.10.1.5:5432  0.0.0.0:*
#   LISTEN 0  244  127.0.0.1:5432  0.0.0.0:*
bash
# 第 5 步:从应用服务器验证连通性(顺序:端口 → 认证 → 权限)
# 5.1 端口是否可达
nc -zv 10.10.1.5 5432
# 5.2 认证是否通过
psql "host=10.10.1.5 dbname=demo user=app_user sslmode=require" -c "SELECT current_user, inet_server_addr();"
# 5.3 权限是否足够
psql "host=10.10.1.5 dbname=demo user=app_user sslmode=require" -c "\dt"

示例二:排查「外部连不上」 ​

按顺序逐层排除,不要跳步:

bash
# 第 1 层:服务是否在监听预期地址
sudo ss -lntp | grep 5432
# 若只有 127.0.0.1 → listen_addresses 未包含内网地址(需改配置并重启)

# 第 2 层:本机能否自连该地址(排除防火墙)
psql "host=10.10.1.5 dbname=demo user=app_user" -c "SELECT 1;"
# 若失败而 127.0.0.1 成功 → 防火墙或路由问题

# 第 3 层:对端到本机的可达性
nc -zv 10.10.1.5 5432
# 若 connection refused → 端口未监听或被防火墙 DROP
# 若 timeout → 安全组 / 网络 ACL 拦截

# 第 4 层:数据库日志
sudo tail -n 50 /var/log/postgresql/postgresql-18-main.log
text
# 常见报错与对应层次
# Connection refused
#   → 端口没监听:listen_addresses 不含该地址,或服务未启动,或防火墙 REJECT
# FATAL: no pg_hba.conf entry for host "10.10.5.20", user "app_user", database "demo"
#   → 监听已通,pg_hba.conf 未放行该来源
# FATAL: password authentication failed for user "app_user"
#   → pg_hba 已匹配,密码或 SCRAM 存储有问题
# FATAL: remaining connection slots are reserved for ...
#   → 连接数耗尽
sql
-- 在数据库内确认「我看到的客户端地址」是什么(NAT 环境下常与预期不同)
SELECT inet_client_addr() AS client_ip,
       inet_server_addr() AS server_ip,
       inet_server_port() AS server_port;

-- 查看是否有连接数耗尽的风险
SELECT count(*) AS total,
       count(*) FILTER (WHERE state = 'idle') AS idle_cnt,
       count(*) FILTER (WHERE state = 'active') AS active_cnt
FROM pg_stat_activity;

示例三:用脚本固化一份监听与端口基线检查 ​

bash
#!/usr/bin/env bash
# check-listen.sh:上线前的监听与端口基线检查
set -euo pipefail

PSQL="psql -d postgres -tAc"

echo "── 1) 监听地址应为内网地址 + 回环 ──"
LISTEN=$(${PSQL} "SHOW listen_addresses;")
echo "listen_addresses = ${LISTEN}"
if [ "${LISTEN}" = "*" ]; then
  echo "警告:监听所有接口,请确认防火墙与 pg_hba.conf 已收窄"
fi

echo "── 2) 端口应为非默认或显式登记 ──"
${PSQL} "SHOW port;"

echo "── 3) 连接预算检查 ──"
${PSQL} "SELECT count(*) || ' / ' || current_setting('max_connections')
        AS conn_usage FROM pg_stat_activity;"

echo "── 4) 是否存在非预期的远程连接来源 ──"
${PSQL} "SELECT coalesce(client_addr::text,'unix socket') || ' x ' || count(*)
        FROM pg_stat_activity
        WHERE backend_type = 'client backend'
        GROUP BY 1 ORDER BY 1;"

echo "检查完成"
bash
chmod +x check-listen.sh
./check-listen.sh

注意事项 ​

  • listen_addresses 与 port 需要重启:SELECT pg_reload_conf() 对这两项无效。改完必须 systemctl restart postgresql,并用 ss -lntp 确认监听结果。
  • * 与 0.0.0.0 都是「全开」:0.0.0.0 只覆盖 IPv4,:: 只覆盖 IPv6,只有 * 同时覆盖两者。在只有 IPv4 的机器上三者常用效果相同,但语义不同,别混用。
  • 云环境要看安全组:在 AWS / 阿里云等环境,本机 iptables 往往不是最终裁决者,安全组与 VPC 网络 ACL 才是。排查时不要只在机器上查。
  • 容器里的 localhost 是容器自己:Docker 容器内的 listen_addresses = 'localhost' 只监听容器回环,宿主机无法访问。容器内应设为 *,再用 -p 127.0.0.1:5432:5432 限制暴露范围。
  • listen_addresses 不改变 pg_hba.conf 的必要性:即使只监听内网,pg_hba.conf 仍需正确配置;反过来,pg_hba.conf 再严也无法阻止服务在公网网卡上监听。
  • NAT / 负载均衡下地址会变:经过 NAT 或代理后,inet_client_addr() 返回的是代理地址而非真实客户端 IP。这会导致 pg_hba.conf 的地址匹配失效,需要改用代理后的统一网段。
  • 改端口后别忘更新所有下游:应用连接串、监控采集、备份脚本、pg_hba.conf 的复制规则、容器编排的端口映射。
  • max_connections 调大要先算内存:每个连接至少占用数 MB(含 work_mem、维护内存、栈等)。盲目从 100 调到 1000 可能直接触发 OOM,正确做法是加连接池。
sql
-- 估算:每个连接的基础内存开销(粗略)
SELECT name, setting, unit
FROM pg_settings
WHERE name IN ('work_mem', 'maintenance_work_mem', 'shared_buffers',
               'temp_buffers', 'max_connections');

最佳实践 ​

  • 显式指定地址,不用 *:listen_addresses = '10.10.1.5,127.0.0.1' 既满足内网访问,又天然阻断了公网。若必须用 *,请确保防火墙与之同步收窄。
  • 保留回环监听:始终把 127.0.0.1 写进列表,保证本机监控、备份、迁移工具能连上。
  • 本机应用走 Unix 域套接字:省去加密开销,且可用 peer 认证,避免在磁盘上留任何口令。
  • 用连接池而不是加连接数:应用连接数超过 max_connections 的一半时,先上 PgBouncer(事务级池化)而非调大参数。
  • 为连接设超时:idle_in_transaction_session_timeout 与 idle_session_timeout 能自动清理「忘记提交的事务」,避免长事务阻塞 VACUUM。
  • 把监听配置纳入巡检:定期核对 ss -lntp 实际监听与配置是否一致,及时发现被手工放开的接口。
  • 变更走完整链路检查:改完监听参数后,按「服务端监听 → 防火墙 → 客户端连通 → 认证 → 权限」五步逐层验证,而不是只在本机试一下。
bash
# 一键完成五步验证的模板
HOST=10.10.1.5; PORT=5432; DB=demo; USER=app_user
set -e
sudo ss -lntp | grep ":${PORT}"                                    # 1 监听
nc -zv "${HOST}" "${PORT}"                                         # 2 网络
psql "host=${HOST} port=${PORT} dbname=${DB} user=${USER} sslmode=require" -c "SELECT 1;"   # 3 连通+认证
psql "host=${HOST} port=${PORT} dbname=${DB} user=${USER}" -c "\dt public.*" | head -5      # 4 权限
echo "全部检查通过"

下一节

继续学习:SSL 连接初始化

参考链接 ​