3. 连接参数与资源消耗
PostgreSQL 采用“每连接一进程”的模型,客户端每建立一条连接,postmaster 就会 fork 出一个后端进程(backend)。这意味着连接数并不是一个抽象数字,而是直接对应操作系统层面的进程数、内存占用和文件描述符消耗。理解 max_connections、superuser_reserved_connections 以及它们背后的资源账本,是连接管理从“能连上”走向“连得稳”的关键一步。
一、max_connections 与 superuser_reserved_connections 的关系
max_connections 是数据库允许同时存在的客户端连接总数上限,默认值通常为 100。它并不等于“普通用户可用连接数”,因为其中一部分被预留给超级用户。PostgreSQL 规定:普通用户可用的连接数为 max_connections - superuser_reserved_connections,而超级用户始终可以额外使用被预留的槽位。默认 superuser_reserved_connections 为 3。
这个预留机制的意义在于:当连接池耗尽、普通用户无法连接时,DBA 仍能以超级用户身份登录,执行 pg_terminate_backend() 或调整参数来恢复服务。因此,即使业务侧连接数已经打满,也不应把预留槽位挪给普通用户。
需要特别注意的是,max_connections 是实例级参数,修改后必须重启 PostgreSQL 才能生效。它还会影响共享内存的分配:每个连接槽位都会在共享内存中占用一定结构(如 PGPROC、信号量等),因此把 max_connections 设得过大,会推高共享内存需求,甚至导致启动失败。
二、连接的内存开销:不止是 work_mem
很多人误以为“一个连接的内存 = work_mem”。实际上,后端进程的内存由多部分组成:
- 进程基础开销:每个 backend 进程本身有栈、堆、代码段映射,通常几 MB 到十几 MB。
- 会话级缓存:如 catalog cache、relcache、plan cache,随访问对象增多而增长。
- 排序/哈希类操作:受
work_mem控制,但它是“每个排序/哈希节点”的上限,一个复杂查询可能同时使用多个work_mem。 - 维护类操作:
maintenance_work_mem用于 VACUUM、CREATE INDEX 等,通常只有少数连接会触发。
因此,估算连接内存时不能简单用 max_connections × work_mem。更稳妥的做法是:先测出单个空闲连接的实际 RSS,再叠加典型查询的峰值增量。例如,假设单个空闲 backend 约 8 MB,典型 OLTP 查询峰值再增加 4 MB,那么 500 个连接的基础内存约为 500 × 12 MB ≈ 6 GB,这还不包括 shared_buffers 和操作系统缓存。
shared_buffers = 4GB,max_connections = 500,单连接综合开销按 12 MB 估算,则仅连接相关内存约 6 GB,加上共享内存后,实例总内存需求已接近 10 GB 量级。实际值需以 pg_stat_activity 与操作系统监控为准。三、文件描述符:最容易被忽略的硬上限
每个 PostgreSQL 连接至少占用一个客户端 socket 文件描述符;此外,后端进程还会打开数据文件、WAL 文件、临时文件等。因此,操作系统对 postmaster 进程的 nofile 限制必须显著大于 max_connections。经验上,nofile 至少应为 max_connections × 2 + 系统预留,高并发场景建议直接设为 65535 或更高。
在 Linux 上,可通过以下方式检查和调整:
# 查看当前 postmaster 进程的限制
cat /proc/$(head -1 /var/run/postgresql/*.pid 2>/dev/null || pgrep -o postgres)/limits | grep "open files"
# systemd 管理的实例,编辑 override
systemctl edit postgresql
# 加入:
[Service]
LimitNOFILE=65535
# 重新加载并重启
systemctl daemon-reload
systemctl restart postgresql
如果 nofile 不足,典型现象是:连接数尚未达到 max_connections,但新连接开始报 “could not open file” 或 “too many open files”,已有连接也可能在打开新文件时失败。这类故障往往被误判为“连接数满了”,实际根因在文件描述符。
四、内核参数调优:让连接真正可用
仅有 max_connections 还不够,内核层面的几个参数决定了高并发连接能否稳定建立和维持:
| 参数 | 作用 | 建议方向 |
|---|---|---|
net.core.somaxconn | 监听队列最大长度 | 提高到 1024 或更高,避免瞬时连接被丢弃 |
net.ipv4.tcp_max_syn_backlog | SYN 队列长度 | 与 somaxconn 匹配,提升握手抗压能力 |
net.ipv4.ip_local_port_range | 客户端可用端口范围 | 扩大范围,避免客户端端口耗尽 |
net.ipv4.tcp_tw_reuse | 复用 TIME_WAIT 连接 | 客户端侧可设为 1,缓解短连接端口压力 |
vm.overcommit_memory | 内存分配策略 | 数据库场景常设为 2,配合 overcommit_ratio 控制 |
这些参数修改后通常需要 sysctl -p 生效。它们不改变 PostgreSQL 内部的连接上限,但决定了“连接洪峰到来时,系统是否接得住”。
五、把参数、内存与 fd 放在一张图里看
下面的关系图把本节的核心对象串起来:max_connections 决定连接槽位总数,superuser_reserved_connections 从中划出保留区,每个连接同时消耗内存和文件描述符,而内核参数则决定连接建立阶段是否顺畅。
六、配置检查清单
在调整连接相关参数时,建议按以下顺序核对,避免只改一个参数而留下短板:
- 确认
max_connections与业务峰值连接数匹配,并预留 20% 余量。 - 确认
superuser_reserved_connections未被调小到 0,保留至少 2~3 个恢复入口。 - 用
SELECT pg_size_pretty(sum(...))或操作系统监控估算连接内存,确保不超过物理内存的合理比例。 - 检查 postmaster 的
nofile,确保大于max_connections × 2。 - 检查
somaxconn、tcp_max_syn_backlog是否与连接洪峰匹配。 - 修改
max_connections后重启实例,并用SHOW max_connections;与pg_stat_activity验证。
max_connections 来解决连接不足。连接数越高,上下文切换、内存和锁竞争成本越大。更合理的路径是引入连接池(如 PgBouncer),把数据库侧连接控制在数百级别,由池化层承接成千上万的客户端连接。总结来说,max_connections 是“槽位总数”,superuser_reserved_connections 是“应急通道”,内存和文件描述符是“每个槽位的真实成本”,内核参数则是“连接建立阶段的路况”。只有把这四者放在同一张资源账本里核算,连接管理才具备工程意义上的可控性。