PostgreSQL max_connections 与 work_mem 的内存风险

7次阅读

max_connections 设置过高会直接压垮服务器内存,因其与work_mem及固定开销相乘导致内存超限,引发OOM、进程被杀或严重swap。

PostgreSQL max_connections 与 work_mem 的内存风险

max_connections 设置过高会直接压垮服务器内存

postgresql 每个连接都会独占一份 work_mem(以及少量固定开销),所以总内存消耗 ≈ max_connections × (work_mem + 约 10–20MB 固定开销)。比如设成 max_connections = 500work_mem = 64MB,仅这部分就可能吃掉 30GB+ 内存,远超物理 RAM,触发 OOM killer 杀进程或严重 swap。

常见错误现象:out of memory 错误、PostgreSQL 进程被系统 kill、查询响应突然变慢甚至卡死。

  • 不要按“最大并发用户数”设 max_connections,而应按“真实长期存活的后端连接数”估算(例如连接池如 PgBouncer 的 pool_size)
  • 生产环境建议初始值 ≤ 200;超过 300 必须同步压测内存与 swap 行为
  • max_connections 修改需重启,不能热更新,别在线乱调

work_mem 动态放大效应比想象中更危险

work_mem 是每个 *操作节点*(如排序、哈希表、位图合并)独立分配的内存上限,不是每连接只用一份。一个复杂查询含 5 个排序 + 2 个哈希连接?可能瞬间申请 7 × work_mem。它还会在并行查询中被 worker 进程重复使用——1 个查询开 4 个 parallel worker,就可能占 4 × work_mem × 节点数。

典型踩坑场景:报表类查询在低峰期跑得通,高峰时因并发多、单查又重,集体触发内存争抢和频繁 swap。

  • 全局设 work_mem 到 128MB 以上前,务必用 EXPLaiN (ANALYZE, BUFFERS) 观察实际内存使用峰值
  • 对 OLAP 查询,优先用 SET LOCAL work_mem = '512MB' 在事务内临时调高,避免污染全局
  • 注意 work_mem 单位是 KB/MB/GB,写错单位(如写成 work_mem = 64)会导致实际只有 64KB,反而引发大量磁盘排序

shared_buffers 和 effective_cache_size 不是“越大越好”

这两个参数常被误当作“能填多少填多少”,但它们和 max_connectionswork_mem 共享同一块物理内存。linux 内核对 shared memory(shared_buffers)有硬限制(/proc/sys/kernel/shmmax),超限会导致 PostgreSQL 启动失败并报错 could not create shared memory segment

  • shared_buffers 建议设为物理内存的 25% 左右(但不超过 8GB),再大收益极小且增加 checkpoint 压力
  • effective_cache_size 是优化器用的统计值,不是真实分配,设太高会让优化器误判“磁盘读很快”,从而选错执行计划(比如放弃索引走 seq scan)
  • 修改 shared_buffers 同样需要重启,且首次启动会清空 OS page cache,可能引发短暂性能抖动

真正安全的调优路径是分层控制

靠单个参数硬扛并发不现实。关键是在连接层、查询层、系统层做隔离和限制:

  • 用 PgBouncer 或 pgbouncer-rr 做连接池,把 max_connections 控制在 100–200,让池内连接复用
  • statement_timeoutlock_timeout 防住长查询和锁等待雪崩
  • resource_queue(通过 pg_partman 或第三方扩展)或原生 ALTER ROLE … SET 对不同业务角色设不同 work_mem
  • 监控 pg_stat_activity/proc//status 中的 VmRSS,比看 free -h 更准

最常被忽略的一点:PostgreSQL 的内存压力不会立刻表现为查询失败,而是先悄悄退化成磁盘排序、buffer miss、checkpoint 频繁——等看到 OOM,往往已经晚了两轮监控周期。

text=ZqhQzanResources