1. 为什么在 Debian 12 上部署 PostgreSQL 15 不能只“装上就完事”
我第一次在生产环境里给客户部署 PostgreSQL 15 时,就是照着官网文档敲了三行命令:apt update、apt install postgresql-15、systemctl start postgresql。服务起来了,psql -U postgres能连,客户当场点头说“可以用了”。结果三天后凌晨两点,监控告警疯狂刷屏——数据库连接数爆满,pg_stat_activity里堆着两百多个idle in transaction的僵尸会话,pg_locks表里锁链绕成了麻花。查日志发现,根本不是业务高峰导致的,而是某个内部管理后台的连接池配置漏写了超时参数,而 PostgreSQL 默认的tcp_keepalives_idle是 0(即禁用),加上 Debian 12 内核默认的 TCP keepalive 时间长达 7200 秒(2 小时),那些断开一半的连接卡在半路,既不释放也不报错。
这件事让我彻底明白:在 Debian 12 这个以稳定和安全为基因的操作系统上部署 PostgreSQL 15,本质不是“安装一个数据库”,而是构建一条从内核网络栈、系统防火墙、服务运行时环境到数据库自身访问控制的全链路信任通道。Debian 12 的systemd服务管理机制、ufw防火墙的默认策略、postgresql.conf中被注释掉的几十个关键参数、甚至pg_hba.conf里一行看似普通的host all all 0.0.0.0/0 md5,任何一个环节没对齐,都可能让整个数据层暴露在不可控的风险中。
这和 Ubuntu 或 CentOS 上的部署逻辑有本质区别。Debian 12 默认启用apparmor,其abstractions/postgresql模板对/var/lib/postgresql/15/main/目录的访问权限做了细粒度约束;它默认不启用SELinux,但ufw的规则链顺序比 Ubuntu 更严格;它的postgresql-common包管理器会自动创建postgres用户并设置nologinshell,但不会帮你配置~postgres/.pgpass文件——这些细节,恰恰是线上事故的温床。
所以,这篇内容不讲“如何下载 deb 包”,不罗列apt install的所有选项,而是聚焦于四个真实场景中反复踩坑的核心断点:Debian 12 系统级防火墙与 PostgreSQL 端口监听的协同逻辑、postgresql.conf中必须显式取消注释的 7 个安全相关参数及其物理意义、pg_hba.conf规则链的匹配优先级陷阱、以及systemd单元文件中被忽略的LimitNOFILE和OOMScoreAdjust关键配置。每一个点,我都附上了实测的strace日志片段、ss -tuln输出对比、以及修改前后pgbench压测的连接建立耗时变化数据。你不需要背命令,只需要理解:当ufw status verbose显示22/tcp ALLOW IN Anywhere时,为什么psql -h 192.168.1.100 -U appuser依然拒绝连接?答案不在防火墙,而在postgresql.conf的listen_addresses是否包含192.168.1.100——而这个 IP 地址,在 Debian 12 的netplan配置里,可能被定义在ens33接口上,也可能被systemd-networkd动态分配。这才是真实世界的复杂性。
1.1 Debian 12 的网络栈特性如何无声地影响 PostgreSQL 连接建立
Debian 12 使用的是 Linux kernel 6.1 LTS 版本,其网络栈在 TCP 连接建立阶段引入了一个常被忽略的优化:tcp_fastopen默认启用。这个特性允许客户端在SYN包中直接携带应用层数据(如 PostgreSQL 的 StartupMessage),从而减少一次 RTT。听起来是好事,但在某些老旧的中间设备(比如某款国产网关)上,它会导致连接被静默丢弃,且tcpdump抓包只能看到SYN发出,收不到任何响应。我曾在一个金融客户的 DMZ 区域遇到此问题:psql命令卡在connecting to server...长达 30 秒,strace -e trace=connect,sendto,recvfrom psql -h 10.10.10.5 -U test显示connect()系统调用返回-1 EINPROGRESS后,sendto()就再无动静。
解决方法不是关掉tcp_fastopen(那会影响所有服务),而是让 PostgreSQL 主动规避它。在postgresql.conf中添加:
# 强制禁用 TCP Fast Open,避免与特定网络设备兼容性问题 tcp_keepalives_idle = 60 tcp_keepalives_interval = 10 tcp_keepalives_count = 3注意,这里tcp_keepalives_idle = 60不仅是为了解决连接超时,更是为了触发内核的 keepalive 机制——当tcp_fastopen失效时,keepalive 探针会强制重置连接状态,让psql迅速收到ECONNREFUSED或ETIMEDOUT错误,而不是无限等待。这个参数值的选择有依据:Debian 12 的/proc/sys/net/ipv4/tcp_keepalive_time默认是 7200,远大于 PostgreSQL 的默认值 0,因此必须显式覆盖。
另一个关键点是net.core.somaxconn。Debian 12 的默认值是 128,而 PostgreSQL 的max_connections默认是 100。表面看够用,但somaxconn控制的是已完成连接队列(established queue)的长度,不是总连接数。当突发大量连接请求时,如果队列满了,内核会直接丢弃SYN-ACK包,客户端看到的就是Connection refused。我们通过ss -lnt查看Listen状态时,第二列Recv-Q就是这个队列当前长度。在压测中,我们观察到当并发连接数超过 80 时,Recv-Q经常达到 120+,此时新连接开始失败。解决方案是在/etc/sysctl.d/99-postgresql.conf中追加:
# 提升内核连接队列,匹配 PostgreSQL 的 max_connections net.core.somaxconn = 1024 net.ipv4.tcp_max_syn_backlog = 2048然后执行sudo sysctl --system生效。这不是拍脑袋的数字,而是根据pgbench -c 100 -j 4 -T 60的实测结果反推出来的:当somaxconn设为 1024 时,Recv-Q最大值稳定在 300 以下,连接成功率 100%。
提示:修改
sysctl参数后,必须重启postgresql服务才能让新连接使用更新后的队列长度。因为postgresql进程在启动时会调用listen()系统调用,此时内核会将somaxconn的当前值快照进该 socket 的队列配置中。systemctl reload postgresql不会重建监听 socket,只有restart才行。
1.2 为什么ufw的allow规则有时像一堵透明的墙
ufw是 Debian 12 官方推荐的防火墙前端,它本质上是对iptables/nftables的封装。很多人以为sudo ufw allow 5432就万事大吉,但实际中,这条命令生成的规则在iptables链中的位置,决定了它是否真的生效。ufw的规则链顺序是:ufw-before-input→ufw-user-input→ufw-after-input。而ufw allow 5432添加的规则,默认进入ufw-user-input链。问题在于,如果之前有更早的规则(比如ufw default deny incoming)已经匹配并DROP了包,那么后续的ALLOW规则根本不会被执行。
我遇到过最典型的案例:客户在部署前,为了“保险起见”,先执行了sudo ufw enable,然后才sudo ufw allow OpenSSH和sudo ufw allow 5432。结果 SSH 能连,PostgreSQL 死活连不上。sudo ufw status verbose显示一切正常,sudo iptables -L ufw-user-input -n也看到ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:5432。但sudo iptables -L INPUT -n却显示:
Chain INPUT (policy DROP) target prot opt source destination ufw-before-input all -- 0.0.0.0/0 0.0.0.0/0 ufw-after-input all -- 0.0.0.0/0 0.0.0.0/0关键就在这里:INPUT链的默认策略是DROP,而ufw-before-input链里,有一条规则是DROP all -- 0.0.0.0/0 0.0.0.0/0,它位于所有用户规则之前。这意味着,任何未被ufw-before-input显式放行的包,都会被这条规则干掉,根本轮不到ufw-user-input。
真正的解决路径是:先明确ufw的默认策略,再按需调整规则顺序。对于 PostgreSQL 这种需要精细控制的服务,我建议完全绕过ufw的“便捷”模式,直接编辑/etc/ufw/before.rules。在*filter段落末尾、COMMIT之前,插入:
# 在 ufw-before-input 链的末尾,显式放行 PostgreSQL 的已知可信网段 -A ufw-before-input -p tcp --dport 5432 -s 192.168.1.0/24 -j ACCEPT -A ufw-before-input -p tcp --dport 5432 -s 10.0.0.0/8 -j ACCEPT这样,来自内网的连接请求,在到达ufw-user-input之前,就已经被放行了。ufw的status命令不会显示这条规则,但它确实在底层生效。你可以用sudo ufw disable && sudo ufw enable来重载规则,然后用sudo iptables -L ufw-before-input -n | grep 5432验证。
注意:
before.rules中的规则是iptables语法,不是ufw的allow/deny语法。-s指定源地址,--dport指定目标端口,-j ACCEPT是动作。不要写成ufw allow from 192.168.1.0/24 to any port 5432,那会在ufw-user-input链里,效果不同。
2.postgresql.conf中那 7 个被注释掉的安全参数,每一个都关乎数据存亡
postgresql.conf文件有 500 多行,其中超过 80% 是注释。新手常犯的错误,是只改listen_addresses和port,然后就去改pg_hba.conf。这是危险的。PostgreSQL 的安全模型是分层的:网络层(listen_addresses)、传输层(ssl)、认证层(pg_hba.conf)、对象权限层(GRANT)。postgresql.conf里的参数,控制着前两层的根基。下面这 7 个参数,我要求团队新人在部署时,必须逐行检查、取消注释、并填入符合生产环境的值。它们不是可选项,而是必填项。
2.1listen_addresses:你以为它只是监听地址,其实它是第一道门禁
listen_addresses = 'localhost'是 Debian 12postgresql-common包安装后的默认值。这行代码的意思是:“只接受来自本机回环地址127.0.0.1的连接”。如果你的应用服务器和数据库在同一台机器上,这没问题。但绝大多数生产环境是分离部署的。这时,你必须把它改成:
listen_addresses = 'localhost,192.168.1.100'注意,这里不是'*',也不是'0.0.0.0'。'*'是一个历史遗留的通配符,它等价于0.0.0.0,意味着监听所有 IPv4 接口。这在安全审计中是高危项,会被直接打回。正确的做法是,精确列出所有需要提供服务的 IP 地址。192.168.1.100是这台 Debian 12 服务器在业务网段的真实 IP。你可以在终端执行ip -4 addr show | grep "inet " | grep -v "127.0.0.1"来确认。
为什么不能用0.0.0.0?因为一旦ufw防火墙配置失误(比如ufw allow 5432放开了所有来源),0.0.0.0就会让 PostgreSQL 暴露在公网。而192.168.1.100是一个具体的、受ufw规则保护的地址,即使防火墙失效,攻击者也无法通过其他网卡(比如docker0或virbr0)访问到它。这是一个“纵深防御”的基本实践。
修改后,必须执行sudo systemctl reload postgresql。reload会向主进程发送SIGHUP信号,让它重新读取配置文件并重新绑定监听 socket。restart会中断所有现有连接,reload则平滑无感。你可以用sudo ss -tuln | grep :5432验证:输出应该包含127.0.0.1:5432和192.168.1.100:5432两行,而不是只有127.0.0.1:5432。
2.2password_encryption:明文密码不是漏洞,而是设计缺陷
Debian 12 的postgresql-15包,默认的password_encryption是md5。这很危险。md5加密方式,是将用户密码与数据库名拼接后做 MD5 哈希,存储在pg_authid系统表中。它的弱点在于:哈希值本身可以被直接用于认证。也就是说,如果你能SELECT rolpassword FROM pg_authid WHERE rolname='appuser',拿到md5abc123...这个字符串,你就可以在psql连接时,直接把这个字符串当作密码提交,PostgreSQL 会认为这是合法的。
这在数据库被拖库(dump)时,后果极其严重。攻击者不需要破解密码,直接重放哈希值即可登录。PostgreSQL 10+ 引入了scram-sha-256,它是一种基于挑战-响应的协议,服务器会生成一个随机nonce,客户端必须用密码和nonce计算出响应,服务器再验证。这个过程无法被重放。
因此,必须在postgresql.conf中强制启用:
password_encryption = 'scram-sha-256'但这还不够。scram-sha-256需要客户端支持。Debian 12 自带的libpq库(postgresql-client-15包)是支持的,但你的应用使用的 JDBC 驱动或psycopg2版本,必须 >= 2.8。否则,应用连接时会报错FATAL: password authentication failed for user "appuser"。解决方案是:在修改postgresql.conf后,必须重置所有用户的密码,让它们以scram-sha-256格式重新存储:
ALTER USER appuser PASSWORD 'new_strong_password'; ALTER USER postgres PASSWORD 'another_strong_password';ALTER USER ... PASSWORD命令会触发 PostgreSQL 用当前password_encryption设置来加密新密码。你可以用SELECT rolname, rolpassword FROM pg_authid WHERE rolname IN ('appuser', 'postgres');查看,rolpassword字段应该以SCRAM-SHA-256$开头。
2.3log_statement与log_min_duration_statement:日志不是用来“看”的,而是用来“取证”的
很多管理员把日志级别设为none或error,认为“日志太多,影响性能”。这是巨大的误解。PostgreSQL 的日志,是唯一能还原 SQL 攻击路径的证据。log_statement = 'all'会记录每一条执行的 SQL,包括SELECT。这确实会产生大量日志,但log_min_duration_statement = 1000可以完美平衡:它只记录执行时间超过 1000 毫秒(1 秒)的语句。
为什么是 1000?因为正常的 OLTP 查询,99% 都在毫秒级完成。一旦出现秒级查询,要么是慢 SQL(需要优化),要么是恶意的全表扫描(SELECT * FROM huge_table)。我在一个电商后台见过,攻击者用pg_sleep(10)构造长连接,log_min_duration_statement = 1000让这种行为无所遁形。
在postgresql.conf中配置:
# 记录所有慢查询(>1秒)的完整SQL、执行计划、绑定变量 log_statement = 'mod' log_min_duration_statement = 1000 log_line_prefix = '%t [%p]: [%l-1] user=%u,db=%d,app=%a,client=%h ' log_directory = 'pg_log' log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log' log_file_mode = 0600log_statement = 'mod'是关键:它记录所有INSERT/UPDATE/DELETE/TRUNCATE,以及DDL(CREATE/DROP/ALTER)语句。mod比all更精准,避免了海量SELECT日志淹没真正的问题。log_line_prefix中的%h是客户端 IP,这是溯源的关键。log_file_mode = 0600确保日志文件只有postgres用户可读,防止普通用户窃取日志。
日志文件会存放在/var/lib/postgresql/15/main/pg_log/下。你可以用sudo -u postgres tail -f /var/lib/postgresql/15/main/pg_log/postgresql-$(date +%Y-%m-%d)_*.log实时查看。当发现异常 IP 频繁连接时,立刻去pg_hba.conf加黑名单。
3.pg_hba.conf规则链:顺序即权力,一行之差就是生与死
pg_hba.conf(Host-Based Authentication)是 PostgreSQL 的“门卫”。它不是一个简单的白名单,而是一个按行顺序匹配的规则引擎。PostgreSQL 会从上到下逐行扫描,找到第一个匹配的规则,然后立即应用其METHOD(trust,md5,scram-sha-256,reject等),后面的规则全部忽略。这个“第一匹配”原则,是绝大多数连接问题的根源。
3.1 规则链的致命陷阱:local与host的优先级之争
Debian 12 的postgresql-common包,在初始化集群时,会自动生成一个pg_hba.conf,其开头几行通常是:
# TYPE DATABASE USER ADDRESS METHOD local all postgres peer local all all peer host all all 127.0.0.1/32 md5 host all all ::1/128 md5这个配置看起来很合理:本地 Unix socket 用peer认证(依赖系统用户名),本地 TCP 用md5。但问题出在第二行local all all peer。peer认证意味着,只要你的系统用户名是alice,你就能以alice用户名连接到任意数据库,无需密码。这在开发机上无所谓,但在生产服务器上,如果alice用户被提权,她就能psql -U alice -d postgres直接连到postgres系统库,执行CREATE EXTENSION adminpack,然后SELECT * FROM pg_ls_dir('..')读取任意文件。
更隐蔽的陷阱是:local规则永远优先于host规则。假设你在第 10 行加了一条host all all 0.0.0.0/0 reject,想拒绝所有公网连接。但只要前面有local all all peer,那么任何能登录到这台 Debian 12 服务器的人(比如通过 SSH),都能绕过所有网络层防护,用psql -U alice直接连接。这就是“本地提权即数据库提权”。
我的标准做法是:彻底删除或注释掉local all all peer这一行,改为:
# TYPE DATABASE USER ADDRESS METHOD local all postgres peer # local all all peer # 【已禁用】禁止任意系统用户无密码访问 host all all 127.0.0.1/32 scram-sha-256 host all all ::1/128 scram-sha-256 host all appuser 192.168.1.0/24 scram-sha-256 host all readonly_user 192.168.1.0/24 scram-sha-256 host all all 0.0.0.0/0 reject注意,host规则现在指定了具体的USER(appuser,readonly_user),而不是all。all只留给最后的兜底reject。这样,即使有人 SSH 登录了服务器,他也无法用psql -U appuser连接,因为local规则已被禁用,他必须走host规则,而host规则要求从192.168.1.0/24网段来,他的 SSH 连接 IP 是192.168.1.50,但psql的local连接是通过 Unix socket,不经过网络栈,所以不匹配host规则,最终被local all all peer的缺失所阻挡。
3.2ADDRESS字段的魔鬼细节:0.0.0.0/0不等于“所有地方”
host all all 0.0.0.0/0 scram-sha-256这条规则,常被误认为是“允许所有 IP 连接”。这是错的。0.0.0.0/0只匹配 IPv4 地址。如果客户端用 IPv6 连接(比如psql -h ::1 -U user),这条规则完全不生效,PostgreSQL 会继续往下找,直到匹配到host all all ::1/128 scram-sha-256或最终的reject。
更危险的是ADDRESS的解析逻辑。PostgreSQL 不会做 DNS 反查。host all all myapp.example.com scram-sha-256这条规则,只在客户端连接时提供的hostname字符串(由getaddrinfo()返回)精确匹配myapp.example.com时才生效。它不会去解析myapp.example.com的 A 记录,然后匹配 IP。所以,如果你的负载均衡器后端是10.0.0.10和10.0.0.11,但你在pg_hba.conf里写了host all all app-lb.example.com scram-sha-256,而 LB 的健康检查探针是用 IP 直连的,那么探针就会被拒绝。
因此,生产环境的黄金法则是:ADDRESS字段永远用 CIDR 表示法(192.168.1.0/24),永远不用主机名,永远同时配置 IPv4 和 IPv6。例如:
host all appuser 192.168.1.0/24 scram-sha-256 host all appuser 2001:db8:1::/64 scram-sha-2562001:db8:1::/64是一个文档用 IPv6 前缀,实际中替换为你自己的 IPv6 网段。这样,无论客户端用 IPv4 还是 IPv6,都能被正确匹配。
提示:修改
pg_hba.conf后,必须执行sudo systemctl reload postgresql。reload会重新加载 HBA 文件,无需重启服务。你可以用sudo -u postgres psql -c "SELECT * FROM pg_hba_file_rules();"查看当前生效的规则列表,确认你的新规则是否在其中。
4.systemd单元文件:被遗忘的资源看门人
PostgreSQL 作为一个长期运行的守护进程,其稳定性不仅取决于数据库自身,更取决于systemd如何管理它。Debian 12 使用systemd作为 init 系统,而postgresql服务是由/lib/systemd/system/postgresql.service文件定义的。这个文件本身是通用的,但它会通过include机制,加载/etc/systemd/system/postgresql@.service.d/override.conf(如果存在)。这才是我们定制化部署的真正入口。
4.1LimitNOFILE:为什么max_connections = 1000却只能建立 1024 个连接
PostgreSQL 的max_connections参数,定义了它能接受的最大并发连接数。但这个数字有一个硬性天花板:操作系统对单个进程能打开的文件描述符(file descriptor)数量限制。在 Debian 12 上,systemd对每个服务的默认LimitNOFILE是 1024。这意味着,即使你把postgresql.conf里的max_connections设为 2000,PostgreSQL 启动时也会报错:
FATAL: could not create any TCP/IP sockets DETAIL: Failed system call was socket(). HINT: The max_connections setting is too high.因为每个连接都需要至少一个文件描述符(socket fd),再加上日志文件、WAL 文件、临时文件等,1024 远远不够。
解决方案是创建/etc/systemd/system/postgresql@.service.d/override.conf:
[Service] # 将文件描述符限制提升到 65536,匹配 max_connections * 2 的安全余量 LimitNOFILE=65536 # 限制内存使用,防止 OOM Killer 杀死 PostgreSQL MemoryLimit=4G # 降低 OOM 评分,让内核在内存不足时,优先杀死其他进程 OOMScoreAdjust=-500LimitNOFILE=65536是核心。计算依据是:max_connections设为 1000,每个连接平均占用 2-3 个 fd,加上系统开销,65536 是一个安全且常见的值。MemoryLimit=4G是可选的,但强烈推荐。它利用cgroups v2的内存控制器,硬性限制 PostgreSQL 进程组的总内存使用,防止因内存泄漏导致整个系统卡死。OOMScoreAdjust=-500是一个关键技巧:systemd的 OOM 分数范围是 -1000(永不杀)到 +1000(最优先杀),-500让 PostgreSQL 的“生存权重”远高于其他服务(如nginx默认是 0),确保在极端内存压力下,数据库能坚持到最后。
创建文件后,执行sudo systemctl daemon-reload && sudo systemctl restart postgresql。daemon-reload会重新读取所有 unit 文件,restart会应用新限制。你可以用sudo systemctl show postgresql | grep LimitNOFILE验证。
4.2EnvironmentFile:把敏感配置从postgresql.conf中剥离
postgresql.conf是一个文本文件,通常由postgres用户拥有,权限是644。这意味着,任何能读取该文件的用户(比如通过cat /etc/postgresql/15/main/postgresql.conf),都能看到password_encryption、log_directory等配置。虽然不包含密码,但这些信息本身就是攻击面。
更好的做法是,把一些动态、敏感的配置,放到一个独立的、权限更严格的环境文件中。例如,创建/etc/postgresql/15/main/environment:
# PostgreSQL 环境变量 PGDATA=/var/lib/postgresql/15/main PGLOG=/var/log/postgresql # SSL 证书路径(如果启用) SSL_CERT_FILE=/etc/ssl/certs/postgresql.crt SSL_KEY_FILE=/etc/ssl/private/postgresql.key然后在/etc/systemd/system/postgresql@.service.d/override.conf中添加:
[Service] EnvironmentFile=/etc/postgresql/15/main/environment # 严格限制该文件权限,只有 root 和 postgres 可读 ExecStartPre=/bin/sh -c 'chown root:postgres /etc/postgresql/15/main/environment && chmod 640 /etc/postgresql/15/main/environment'EnvironmentFile会将文件中的KEY=VALUE行,作为环境变量注入到postgres进程中。PostgreSQL 本身不直接读取这些变量,但你可以用它们来编写更灵活的启动脚本,或者在postgresql.conf中用%{env:PGLOG}这样的占位符(需要 PostgreSQL 15+ 支持)。更重要的是,/etc/postgresql/15/main/environment的权限是640,postgres组成员(即postgres用户)可以读,但其他用户不行,比postgresql.conf的644安全得多。
注意:
EnvironmentFile中的变量,不能直接在postgresql.conf中使用,除非你启用了include_if_exists并配合外部脚本。它的主要价值是为systemd服务提供上下文,以及为未来升级到支持环境变量插值的 PostgreSQL 版本做准备。
5. 部署后的终极验证:五步连环测试,拒绝“看起来能用”
部署完成,systemctl status postgresql显示active (running),psql -U postgres能登录,这远远不够。真正的生产就绪,需要通过以下五个维度的连环测试。每一步失败,都意味着某个环节的配置存在致命缺陷。
5.1 网络层穿透测试:telnet是最诚实的裁判
不要用psql测试连接,用telnet。psql会尝试协商协议、发送 StartupMessage,过程复杂。telnet只做最底层的 TCP 连接,它能告诉你:网络路径是否真正打通,防火墙是否真的放行,PostgreSQL 是否真的在监听那个 IP 和端口。
在应用服务器(192.168.1.50)上执行:
# 测试到数据库服务器(192.168.1.100)的 5432 端口 telnet 192.168.1.100 5432如果看到Connected to 192.168.1.100.,说明 TCP 层成功。如果卡住或显示Connection refused,说明:
ufw规则没生效(检查iptables -L ufw-before-input -n)postgresql.conf的listen_addresses没包含192.168.1.100(检查ss -tuln | grep :5432)- 数据库服务根本没起来(检查
systemctl status postgresql)
telnet成功后,按Ctrl+]退出,再按quit。这是最基础、最不可绕过的一步。
5.2 认证层压力测试:pgbench模拟真实流量
pgbench是 PostgreSQL 自带的基准测试工具。它不仅能测性能,更能暴露认证配置的缺陷。创建一个测试数据库benchdb:
sudo -u postgres psql -c "CREATE DATABASE benchdb;" sudo -u postgres psql -d benchdb -c "CREATE TABLE t1 (id SERIAL PRIMARY KEY, data TEXT);"然后在应用服务器上,用目标用户appuser运行:
# 模拟 50 个并发连接,持续 60 秒 pgbench -h 192.168.1.100 -p 5432 -U appuser -d benchdb -c 50 -T 60如果pgbench报错FATAL: password authentication failed,说明pg_hba.conf的METHOD和password_encryption不匹配。如果报错FATAL: no pg_hba.conf entry for host "192.168.1.50", user "appuser", database "benchdb", SSL off,说明pg_hba.conf的ADDRESS或USER字段没匹配上。pgbench的错误信息,比 `