返回资讯中心
外部精选
软件工程
#开发者实时流

Postgres 的 log_hostname 设置会拖慢新连接建立速度

来源标题:The Postgres logging setting that slows down new connections

开启 PostgreSQL 的 log_hostname 参数会让每条新连接日志额外做一次反向 DNS 解析,从而拖慢连接建立。实测中一条连接从接收到就绪共耗时 13.17 毫秒,其中认证占 6.245 毫秒。默认情况下日志只打印客户端 IP,不做 DNS 查询。

Hacker News 最新StefanKP92026/11/2 · 2 天前30 分钟阅读

以下正文由 AI 翻译自 Hacker News 最新(原文版权归原站所有),可能存在偏差,重要内容请以原文为准。

2026-10-02 10:06:26.730 UTC [7207] [unknown]@[unknown]/[unknown] 10.42.1.5 LOG: connection received: host=10.42.1.5 port=60038 2026-10-02 10:06:26.742 UTC [7207] bench@postgres/[unknown] 10.42.1.5 LOG: connection authorized: user=bench database=postgres application_name=pgbench SSL enabled (protocol=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384, bits=256) 2026-10-02 10:06:26.742 UTC [7207] bench@postgres/pgbench 10.42.1.5 LOG: connection ready: setup total=13.170 ms, fork=0.513 ms, authentication=6.245 ms

这是我们测试中一个连接产生的三行日志。日志行前缀以 %h 结尾,圈出的地址就是 %h 打印的内容:连接客户端的 IP 地址,而不是服务器的地址。PostgreSQL 记录它时没有做任何 DNS 查询。

两个协同工作的设置

服务器日志可以显示每个连接来自哪里。有两个设置决定如何显示,它们分工不同。

PostgreSQL 18 文档对 log_hostname 的描述是这样的:“默认情况下,连接日志消息只显示连接主机的 IP 地址。打开此参数会同时记录主机名。请注意,取决于你的主机名解析配置,这可能带来不可忽视的性能损失。”

实际上有两点很关键。第一,log_hostname 本身不添加任何字段。如果 log_line_prefix 里没有 %h——或者 %r,它会加上端口——普通日志行根本不会显示客户端,无论 log_hostname 怎么设置都一样,而上游默认前缀 '%m [%p] ' 两者都没有。这个设置还会改变“connection received”消息中的 host= 字段(该消息在开启连接日志时出现),并填充 pg_stat_activity 的 client_hostname 列。第二,在我们的测试中,主机名替换了日志中的 IP 地址,而不是出现在它旁边。

反向 DNS 查询会向 DNS 询问某个 IP 地址对应哪个名称(PTR 记录)。打开 log_hostname 后,PostgreSQL 会为每个新的 TCP 连接执行这次查询,就在该连接的服务进程启动之后——在读取客户端的第一条消息之前,也在认证之前。连接会一直等待,直到 DNS 应答,或者系统的解析器放弃。通过本地 Unix socket 建立的连接不会做查询。

只要该设置打开,查询就会执行,即使你的配置中没有任何地方会打印名称。它的耗时也不计入 authentication_timeout。PostgreSQL 18 源码中查询旁边的注释直接说明了这一点:“PreAuthDelay and any DNS interactions above don't count against the time limit.”

我们在 Microsoft Azure 的两台虚拟机上测量了开销,它们位于同一个私有网络中:Ubuntu 24.04 上的 PostgreSQL 18.6(Standard_D4s_v5,4 vCPU)和一台独立的客户端机器(Standard_D2s_v5)。连接使用 TLS 1.3 和 SCRAM-SHA-256 密码认证,与许多生产环境一致。客户端运行 pgbench——PostgreSQL 自带的基准测试工具——使用一行自定义脚本(SELECT 1)和 -C 选项,该选项会为每个事务打开一个新连接。这使得连接建立成为主要工作,而这正是本测试关注的内容。它不是对真实工作负载的吞吐量测试。

Azure 的 DNS 服务对客户端地址的查询返回了名称 vm-logtalk-hostname-cli.internal.cloudapp.net。Ubuntu 默认运行本地 DNS 缓存(systemd-resolved)。没有这样的缓存时,每次查询都会发往 DNS 服务器,因此我们两种方式都测试了数据库服务器。

对于每个设置,表中显示 pgbench 在运行结束时打印的两个数字。连接时间是指打开一个连接平均花费的时间。每秒连接数是指客户端每秒连接、执行 SELECT 1 并断开的次数;pgbench 将其报告为每秒事务数,而在 -C 模式下每个事务就是一个连接。只有一个客户端时,每个连接都要等前一个完成后才开始,因此这两个数字朝相反方向变化:每个连接耗时越长,一秒内能完成的连接就越少。这里的每秒连接数指的是单个客户端反复连接的速度,而不是服务器能接受的最大连接数。

没有本地缓存时,每个新连接多花了约 6 ms:每个连接的时间增加了 43%,本测试中每秒连接数减少了 29%。服务器自身的计时也一致。PostgreSQL 18 可以记录每个连接的建立耗时(log_connections 配合 setup_durations 选项):平均值从 10.4 ms 增长到 16.3 ms,而认证部分在三种变体中均保持在 6.0 ms。额外的时间花在认证之前,也就是查询发生的地方。

有本地缓存时,差异为 0.3 ms。Azure DNS 给这个应答的生存时间(TTL)是 10 秒,因此大多数查询来自缓存。6 ms 就是从此服务器到 Azure DNS 一次往返的开销。在你的网络中,这个数字不同:取决于你的 DNS 服务器应答所需的时间。

健康的 DNS 是最好的情况。随后我们放慢 DNS,然后让它不可达——仅针对数据库服务器,且仅针对 DNS 流量——并用一个客户端运行了相同的测试。

关闭 log_hostname 时,DNS 问题没有造成任何差异,因为没有查询。打开时,每个新连接都在等待 DNS。当 DNS 延迟 100 ms 应答时,每个连接多花了约 109 ms。其中约 106 ms 是查询本身:我们添加的 100 ms,加上健康 DNS 下查询的 6 ms。剩下的 2-3 ms 分散在连接建立的其他环节,一次运行不足以解释它们。当 DNS 不应答时,每个连接都要等到解析器放弃:系统解析器(Ubuntu 24.04 上的 glibc 2.39)直接向 DNS 提问时是 20 秒——它发送了四次问题,间隔五秒——而通过 systemd-resolved 时是 10 秒。解析器选项可以改变这些数字,但改变不了模式:新连接的耗时取决于 DNS 应答所需的时间

(原文较长,此处译出前 6000 字,完整内容请点「打开原文」。)

正文由 探How 从来源站点 RSS 同步,内容未经改写;遇到排版缺失或需要图片、视频时请以原文为准。

打开原文
去社区讨论 →