本文给出了 PostgreSQL wiki 发布的计算硬件理想并发的公式(生态系统中引用最多的 连接池大小计算公式),解释了为什么每个连接的成本比应用程序线程高,然后详细介绍了设置 max_connections 的过程,无需猜测。我们的 基准测试方法 记录了本博客上用于任何性能声明的测量协议。
要点
- 如果没有池, max_connections 应该覆盖 所有 并发客户端连接,而不仅仅是 Postgres 可以有效并行处理的那些连接。
- PostgreSQL wiki 参考公式:理想活跃并发数 = (物理核心数 × 2) + 高效磁盘数。通过测量验证的起点,而不是硬性限制。
- max_connections 是一个
postmaster上下文参数:更改它需要完全重新启动服务器,而不是简单的重新加载。 - 每个 Postgres 连接都是一个单独的系统进程,而不是轻量级线程:这就是连接数量一增加就会造成开销的原因。
- 在代码中验证:在其专用的 Postgres 集群上,Aurabase 根据集群的大小将 max_connections 从 50(免费层)到 400(企业层)变化。
为什么 Postgres 连接比应用程序线程花费更多
Postgres 的连接不使用轻量级线程池。每个客户端连接都会触发一个成熟的系统进程。
postmaster 进程为每次连接尝试创建一个新的(“分叉”),专用于该单个会话,直到它关闭。官方项目文档在其架构基础知识章节中准确地描述了这种机制(postgresql.org/docs/current/connect-estab.html,“连接语义”部分,2026 年 8 月 24 日访问)。
这种选择有一个真正的优势:一个连接的崩溃不会影响其他连接,每个进程都与服务器的其余部分隔离。它还具有直接成本:每个额外的连接都会增加一个要调度的整个操作系统进程,以及其自己的内存空间和自己的内核上下文切换开销。
在没有池的情况下打开 500 个与 Postgres 的直接连接的应用程序会强制服务器管理 500 个并发系统进程,即使其中绝大多数在两个请求之间保持空闲状态。
连接实际消耗的内容:共享内存和work_mem
两种不同的机制会影响记忆,混淆它们几乎总是会导致误诊。
第一个是固定的。启动时,Postgres 会根据 max_connections 的值保留共享内存结构(锁、进程表),无论这些连接随后是否打开。该设置的官方文档明确指出:增加它可能需要比操作系统默认配置允许的更多的系统共享内存(postgresql.org/docs/current/runtime-config-connection.html,于 2026 年 8 月 24 日访问)。
第二个是可变的,并且在规模上更加危险:work_mem 不是每个连接分配一次,而是查询计划中的每个排序或散列操作分配一次。官方文档在这一点上很明确:一个复杂的查询可以并行启动其中多个操作,多个会话可以同时执行相同的操作,因此实际使用的内存可能是 work_mem 的几倍(postgresql.org/docs/current/runtime-config-resource.html,访问于 2026 年 8 月 24 日)。
威胁服务器内存的不仅仅是 max_connections × work_mem。它是 max_connections × work_mem × 每个查询的并发操作数。正是这个产品解释了服务器在 max_connections 增加后交换或耗尽内存的情况被认为是无害的。
PostgreSQL wiki 大小调整公式
官方 PostgreSQL 项目 wiki 记录了一个基准公式,用于计算您的硬件可以有效并行处理的活动 连接 数量,而不是总共打开的连接数量(wiki.postgresql.org/wiki/Number_Of_Database_Connections,于 2026 年 8 月 24 日访问)。
理想的活跃并发数=(物理核心数×2)+高效磁盘。核心数量不包括超线程。在现代 SSD 存储上,有效磁盘的数量仍然接近 1,其中单独的物理磁盘(“主轴”)的概念失去了其原始含义。
在具有 8 个物理核心和 SSD 存储的服务器上,该公式给出在吞吐量开始下降之前的 (8 × 2) + 1 = 17 个活动连接。这个数字常常令人惊讶:与应用程序在实践中打开的数百个连接相比,它似乎很小。这正是下一段的主题。
The number calculated by the formula measures the concurrency that the CPU and disk can absorb, not the number of client connections your application needs to open. A fleet of 20 application processes, each with its own pool of 10 connections, opens 200 simultaneous connections to Postgres even if only 17 of them are actively working at any given time. Without a pooler, max_connections must cover the 200, not the 17. It is this gap that pushes most architectures to add a pooler in transaction mode, even if it means choosing which one (see our comparison PgBouncer, Supavisor and PgCat).
如何更改 max_connections(以及为什么需要重新启动)
max_connections 不支持热插拔。这是一个 postmaster 上下文参数:Postgres 在启动时读取一次,以调整其共享内存的大小。重新加载配置(pg_reload_conf() 或 SIGHUP)是不够的,您必须重新启动服务器。
首先检查当前值及其上下文,以确认是否需要重新启动:
然后应用新值,然后重新启动:
max_connections 默认包括 superuser_reserved_connections (默认为 3):这些连接是为超级用户保留的,以防饱和,它们永远不可用于您的应用程序,即使尚未达到全局计数器也是如此。
Aurabase 如何预算其 Postgres 集群上的 max_connections
调整 max_connections 的大小不仅仅是一个理论练习。以下是 Aurabase 如何在其托管 Postgres 集群上进行预算:
专用集群:每个项目一个 Postgres 集群
在这个级别上(请参阅我们的比较 专用与共享基础),每个项目都会收到自己的 CloudNativePG 集群和自己的 max_connections 预算,其大小取决于实例的大小:
| 免费(专用) | 最大连接数 50 | 1 个实例 · 500m vCPU · 512Mi |
|---|---|---|
| 专业版(默认) | 最大连接数 200 | 2 个实例 · 1 个 vCPU · 2Gi |
| 团队 | 最大连接数 300 | 3 个实例 · 2 个 vCPU · 3Gi |
| 商业 | 最大连接数 400 | 3 个实例 · 2 个 vCPU · 4Gi |
共享集群:一个组织的多个项目,共享预算
在第二条路径上,同一组织的所有项目都通过共享主节点前面的 CNPG 池化器(PgBouncer,transaction模式)进行连接:
| 免费 | 最大连接数 50 | 最大客户端连接数 100 | 最大用户连接数 20 |
|---|---|---|---|
| 亲 | 最大连接数 100 | 最大客户端连接数 200 | 最大用户连接数 60 |
| 团队 | 最大连接数 200 | 最大客户端连接数 400 | 最大用户连接数 150 |
组织中的所有项目都通过共享应用程序角色进行连接。 因此, max_user_connections 单独限制了该角色可以在整个集群中打开的服务器连接总数:这是真正的集群全局保护,而不是 max_client_conn,后者仅限制与池化器本身的客户端连接。
但是,该池化器仅提供 SDK 应用程序流量。 PostgREST 就其本身而言,仍然直接连接到主服务(-rw服务):事务模式下的池化会破坏其模式重新加载机制,该机制侦听名为 pgrst的专用 LISTEN 通道。因此,它自己的连接(共享级别上每个副本 2 个,专用级别上每个副本 10 个)直接计入主节点的 max_connections 预算中,在任何池化器之外,正是下面过程的步骤 1 必须包含的那种“被遗忘”连接。
该代码明确地将这些共享池预算记录为在实际条件下通过测量负载下的 pg_stat_activity 进行校准的起始值,而不是作为已发布基准的固定数字。这与我们的 基准测试方法中描述的规则相同:先测量后调整,而不是猜测然后希望。这些集群在 PostgreSQL 16 上运行,我们的 Postgres 16 vs 17 vs 18比较中记录了这一选择。
无需池化即可调整 max_connections 大小的 5 步过程
此过程不依赖于任何特定工具:它适用于任何托管或自托管的 Postgres 服务器。
- 计算您的实际客户端连接数。 应用程序进程数乘以其内部池的大小,加上管理工具、复制和监控。设置 max_connections 下限的是这个数字,而不是公式。
- 使用 PostgreSQL wiki 中的公式计算硬件的理想并发性:(物理内核 × 2)+ 高效磁盘。该数字表明有多少个连接实际上可以并行工作而不降低吞吐量。
- 将 max_connections 设置为高于步骤 1 的实际需要,并为
superuser_reserved_connections以及在应用程序外部打开自己的连接的任何管理工具留出余量。 - 使用 ALTER SYSTEM SET 应用更改,然后重新启动服务器。 这是一个 postmaster 参数:简单的重新加载是不够的,如上所述。
- 随着时间的推移监控 pg_stat_activity。 如果空闲连接数大大超过活动连接数,这不是 max_connections 问题:这是您需要在服务器前面设置池化器的信号,而不是更高的数字。
第5步的监控请求,直接可用:
当公式不再足够时:您需要池化器的迹象
当 max_connections 单独不再足够时,无论其值如何,都会系统地返回三个信号。
FATAL: sorry, too many clients already错误出现在峰值负载期间,而pg_stat_activity显示的大多数连接都处于空闲状态。- 该应用程序在无服务器环境中运行,或者使用临时工作线程(边缘函数、短作业)运行,其打开和关闭连接的速度比 Postgres 的每连接进程模型的设计速度要快得多。
- 上述公式和过程已经应用,并且客户端连接的实际需求继续超出在不危及work_mem或shared_buffers的情况下可以分配的可用内存。
在这三种情况下,正确的答案几乎总是位于应用程序和 Postgres 之间的池化器,而不是更高的 max_connections。我们的 比较 PgBouncer、Supavisor 和 PgCat 详细介绍了这三个选项,我们的 交易模式指南 解释了池化器就位后最常见的折衷方案。对于连接之外的所有 Postgres 调整,请参阅我们的 生产 Postgres 调整清单。