其它内存参数
PostgreSQL 的内存参数里,shared_buffers、work_mem、maintenance_work_mem 是「大三样」,其余十来个参数单个都很小、平时几乎不用动——但它们的默认值并非总是最优,某些高并发或特定负载下会成为瓶颈。本节把它们分门别类讲清楚:本地缓冲区(每会话)、共享缓冲小项(全局)、大页与共享内存实现、以及并行/栈相关的边界参数。
前置知识
阅读本节前,建议先了解:maintenance_work_mem 维护内存
基础概念
这些参数该分几类
┌───────────────────────────────────────────────────────────────────┐
│ PostgreSQL 内存参数全景 │
├───────────────────────────────────────────────────────────────────┤
│ ① 共享内存区(整个实例一份) ← shared_buffers 已单独讲 │
│ ├─ shared_buffers │
│ ├─ WAL 缓冲区:wal_buffers │
│ ├─ 各类小缓冲:commit_timestamp / multixact / notify / ... │
│ └─ 大页与共享内存实现:huge_pages / shared_memory_type │
│ │
│ ② 每个后端进程的本地内存(每会话一份) │
│ ├─ work_mem(已讲) │
│ ├─ maintenance_work_mem(已讲) │
│ ├─ temp_buffers ← 本节 │
│ └─ max_stack_depth ← 本节 │
│ │
│ ③ 非内存但常一起调的配置 │
│ ├─ max_prepared_transactions(预留槽位) │
│ └─ max_connections(会话数上限,参与内存估算) │
└───────────────────────────────────────────────────────────────────┘一张表看清默认值
SELECT name, setting, unit, context, boot_val
FROM pg_settings
WHERE name IN (
'wal_buffers', 'temp_buffers', 'max_stack_depth', 'max_prepared_transactions',
'commit_timestamp_buffers', 'multixact_member_buffers',
'multixact_offset_buffers', 'notify_buffers', 'serializable_buffers',
'subtransaction_buffers', 'transaction_buffers',
'huge_pages', 'huge_page_size', 'shared_memory_type',
'dynamic_shared_memory_type'
)
ORDER BY name; name | setting | unit | context | boot_val
------------------------------+---------+------+-------------+----------
commit_timestamp_buffers | 0 | 8kB | postmaster | 0
dynamic_shared_memory_type | posix | | postmaster | posix
huge_page_size | 0 | | postmaster | 0
huge_pages | try | | postmaster | try
max_prepared_transactions | 0 | | postmaster | 0
max_stack_depth | 2048 | kB | superuser | 2048
multixact_member_buffers | 32 | 8kB | postmaster | 32
multixact_offset_buffers | 16 | 8kB | postmaster | 16
notify_buffers | 16 | 8kB | postmaster | 16
serializable_buffers | 32 | 8kB | postmaster | 32
shared_memory_type | mmap | | postmaster | mmap
subtransaction_buffers | 0 | 8kB | postmaster | 0
temp_buffers | 1024 | 8kB | user | 1024
transaction_buffers | 0 | 8kB | postmaster | 0
wal_buffers | -1 | 8kB | postmaster | -1
(15 rows)注意单位是「块(8kB)」的参数
上表中带 unit = 8kB 的参数,setting 返回的是块数,不是字节数。例如 temp_buffers = 1024 表示 1024 × 8kB = 8MB。这与 work_mem(单位 kB)的规则不同。
「0 表示自动」的约定
三个参数的默认值是 0,含义不是「关闭」,而是按 shared_buffers 自动推导:
commit_timestamp_buffers = 0
subtransaction_buffers = 0
transaction_buffers = 0
默认含义:shared_buffers / 512,上限 1024 块,下限 16 块-- 例如 shared_buffers = 128MB = 16384 块
-- 16384 / 512 = 32 块 → 在 [16, 1024] 范围内 → 取 32 块 = 256kB-- 直接读实际分配情况
SELECT name, setting, unit FROM pg_settings
WHERE name IN ('commit_timestamp_buffers','subtransaction_buffers','transaction_buffers'); name | setting | unit
---------------------------+---------+------
commit_timestamp_buffers | 32 | 8kB
subtransaction_buffers | 32 | 8kB
transaction_buffers | 32 | 8kB
(3 rows)与「自动清理」的 0 要区分
autovacuum_work_mem = -1 表示「跟随 maintenance_work_mem」;wal_buffers = -1 表示「按 shared_buffers 的 1/32 自动推导」;而这里的 0 表示「按 shared_buffers 自动推导(/512,16~1024 块)」。三者语义都不同,别混淆。
语法与用法
本地缓冲区(每会话)
# postgresql.conf
temp_buffers = '8MB' # 默认值,每个会话访问临时表用-- 会话内可调(但必须在首次使用临时表之前)
SET temp_buffers = '64MB';共享缓冲小项(需重启)
# postgresql.conf —— 这些都是 postmaster 上下文,改完必须重启
wal_buffers = -1 # -1 = 自动(shared_buffers/32,64kB ~ 一个 WAL 段)
commit_timestamp_buffers = 0 # 0 = 自动(shared_buffers/512,16~1024 块)
multixact_member_buffers = 32
multixact_offset_buffers = 16
notify_buffers = 16
serializable_buffers = 32
subtransaction_buffers = 0
transaction_buffers = 0大页与共享内存实现(需重启)
# postgresql.conf
huge_pages = try # try(默认)/ on / off
huge_page_size = 0 # 0 = 用系统默认大页大小
shared_memory_type = mmap # mmap(默认)/ sysv / windows
dynamic_shared_memory_type = posix会话与栈
-- 当前栈深度上限
SHOW max_stack_depth; max_stack_depth
-----------------
2MB-- 查内核真实栈限制ulimit -s8192内核限制 8192kB = 8MB,PostgreSQL 默认 2MB,留有安全余量。预备事务槽位
# postgresql.conf —— postmaster 上下文
max_prepared_transactions = 0 # 0 = 关闭两阶段提交-- 若要用两阶段提交,需设为 >= max_connections
-- max_prepared_transactions = 200与相关命令的对照
-- 查看所有「内存相关」参数的当前值
SELECT name, setting, unit, context, source
FROM pg_settings
WHERE category ILIKE '%memory%' OR name IN (
'temp_buffers','max_stack_depth','huge_pages','shared_memory_type',
'dynamic_shared_memory_type','max_prepared_transactions'
)
ORDER BY name;-- 查看哪些参数改动后需要重启
SELECT name, setting, unit, context
FROM pg_settings
WHERE context = 'postmaster' AND name IN (
'shared_buffers','wal_buffers','huge_pages','huge_page_size','shared_memory_type',
'dynamic_shared_memory_type','max_prepared_transactions',
'commit_timestamp_buffers','multixact_member_buffers','multixact_offset_buffers',
'notify_buffers','serializable_buffers','subtransaction_buffers','transaction_buffers'
)
ORDER BY name;详细说明
一、temp_buffers:临时表页缓存
temp_buffers 是每个会话用于访问临时表(temporary table)的本地缓冲区,默认 8MB。
SELECT name, setting, unit, context FROM pg_settings WHERE name = 'temp_buffers'; name | setting | unit | context
--------------+---------+------+---------
temp_buffers | 1024 | 8kB | user
(1 row)特点:
· 只服务于「临时表的页访问」
· 每个会话独享一份(不是共享内存)
· 按需分配 —— 只在真正用到时占内存
· 每个 buffer 描述符约 64 字节,实际用到一页才额外占 8kB-- 创建并使用临时表
BEGIN;
SET temp_buffers = '64MB';
CREATE TEMP TABLE t_stage (id int, val text);
INSERT INTO t_stage SELECT g, md5(g::text) FROM generate_series(1, 100000) g;
SELECT count(*) FROM t_stage;
COMMIT;temp_buffers 只在首次使用临时表之前生效
官方规定:temp_buffers「可以在单个会话中更改,但只能在该会话首次使用临时表之前更改;之后尝试更改该值将对该会话无效」。
-- 错误!此时已经用过临时表,改不生效
CREATE TEMP TABLE t1 (id int);
SET temp_buffers = '64MB'; -- 无效-- 正确顺序:先设置,再用临时表
SET temp_buffers = '64MB';
CREATE TEMP TABLE t1 (id int);内存占用是「按需」的
把 temp_buffers 设大是廉价的:每个 buffer 描述符只占约 64 字节。只有当某个 buffer 真被使用时,才会额外消耗 8kB(BLCKSZ)。所以对于「可能偶尔用大临时表」的会话,设大一点没有坏处。
二、commit_timestamp_buffers:提交时间戳缓存
PG15 引入。缓存 pg_commit_ts 目录的内容(提交时间戳)。
SELECT name, setting, unit, context, short_desc
FROM pg_settings WHERE name = 'commit_timestamp_buffers'; name | setting | unit | context | short_desc
---------------------------+---------+------+------------+--------------------------------------
commit_timestamp_buffers | 0 | 8kB | postmaster | Sets the number of dedicated shared buffers for transaction commit timestamps.
(1 row)默认 0 = 自动:shared_buffers / 512,上限 1024 块,下限 16 块
只在启用了 track_commit_timestamp 时才真正有用-- 查看是否启用了提交时间戳跟踪
SHOW track_commit_timestamp; track_commit_timestamp
------------------------
off
(1 row)三、multixact 相关缓冲
「多事务(multixact)」是同一条行记录被多个事务同时加锁(如 SELECT FOR SHARE 多会话并发)时使用的结构。它有两个缓存参数:
| 参数 | 默认值 | 缓存对象 |
|---|---|---|
multixact_member_buffers | 32 | pg_multixact/members |
multixact_offset_buffers | 16 | pg_multixact/offsets |
SELECT name, setting, unit FROM pg_settings
WHERE name IN ('multixact_member_buffers', 'multixact_offset_buffers'); name | setting | unit
----------------------------+---------+------
multixact_member_buffers | 32 | 8kB
multixact_offset_buffers | 16 | 8kB
(2 rows)高并发、大量 FOR SHARE / FOR UPDATE 共享锁的场景,
multixact 增长快,可适当调大这两个值。-- 查看 multixact 的积压情况
SELECT * FROM pg_stat_activity WHERE wait_event_type = 'LWLock'
AND wait_event ILIKE '%multixact%';四、notify_buffers:LISTEN/NOTIFY 缓存
缓存 pg_notify 目录,服务于 LISTEN / NOTIFY 机制。
SELECT name, setting, unit FROM pg_settings WHERE name = 'notify_buffers'; name | setting | unit
-----------------+---------+------
notify_buffers | 16 | 8kB
(1 row)-- LISTEN/NOTIFY 使用示例
LISTEN order_events;
-- 另一个会话
NOTIFY order_events, 'order:12345:paid';如果有大量 LISTEN/NOTIFY 消息(如应用内事件总线),
可适当调大 notify_buffers(需重启)。五、serializable_buffers:可串行化事务缓存
缓存 pg_serial 目录,服务于 SERIALIZABLE 隔离级别下的冲突检测。
SELECT name, setting, unit FROM pg_settings WHERE name = 'serializable_buffers'; name | setting | unit
----------------------+---------+------
serializable_buffers | 32 | 8kB
(1 row)-- 使用可串行化隔离级别
BEGIN ISOLATION LEVEL SERIALIZABLE;
SELECT ...;
COMMIT;用了 SERIALIZABLE 才需要关注
只有大量使用 SERIALIZABLE 隔离级别(如金融场景、需要强一致性的业务)时,这个参数才可能成为瓶颈。
六、subtransaction_buffers 与 transaction_buffers
这两个缓存分别服务于子事务(pg_subtrans)和事务提交状态(pg_xact)。
SELECT name, setting, unit FROM pg_settings
WHERE name IN ('subtransaction_buffers', 'transaction_buffers'); name | setting | unit
-------------------------+---------+------
subtransaction_buffers | 32 | 8kB
transaction_buffers | 32 | 8kB
(2 rows)默认 0 = 自动:shared_buffers / 512,上限 1024 块,下限 16 块大量保存点(SAVEPOINT)会加剧子事务压力
应用层如果频繁使用 SAVEPOINT / ROLLBACK TO SAVEPOINT(ORM 常见做法),子事务数量会快速增长。PG15 引入 subtransaction_buffers 正是为了缓解这类场景的 I/O 压力。
-- 子事务的压力可以从统计里观察到
SELECT name, setting FROM pg_settings WHERE name = 'subtransaction_buffers';-- 查看子事务历史的最大数量(间接反映压力)
SELECT * FROM pg_stat_database WHERE datname = current_database();七、huge_pages:大页
大页能减少页表项、降低 CPU 在内存管理上的开销,对使用大 shared_buffers 的实例效果明显。
SELECT name, setting, context FROM pg_settings WHERE name = 'huge_pages'; name | setting | context
-------------+---------+-----------
huge_pages | try | postmaster
(1 row)| 取值 | 含义 |
|---|---|
try(默认) | 尝试申请大页,失败则回退到普通页 |
on | 必须使用大页,申请失败会导致实例启动失败 |
off | 不申请大页 |
# postgresql.conf
huge_pages = try
huge_page_size = 0 # 0 = 用系统默认大页大小try 与 on 的取舍
try:安全,但你可能一直没真正用上大页,还以为是生效的。on:保证生效,但大页配置不足时实例直接起不来——这是生产事故的常见来源。
正确做法:先在测试环境用 on 验证大页配置充足,再用到生产;或保持 try 并通过 huge_pages_status 确认实际状态。
-- 查看大页的实际使用状态(PG14+)
SELECT name, setting FROM pg_settings WHERE name = 'huge_pages_status'; name | setting
-------------------+---------
huge_pages_status | off
(1 row)可能取值:on / off / unknown
(unknown 表示平台不支持或未启用)# 查看系统大页配置(Linux)
grep -i huge /proc/meminfoAnonHugePages: 0 kB
HugePages_Total: 2048
HugePages_Free: 2048
Hugepagesize: 2048 kB平台限制
官方说明:huge_pages 目前仅在 Linux 和 Windows 上支持。在 Linux 上,只有 shared_memory_type = mmap(默认)时才支持。其他系统上设为 try 会被忽略。
八、huge_page_size
SELECT name, setting, context FROM pg_settings WHERE name = 'huge_page_size'; name | setting | context
---------------+---------+-----------
huge_page_size| 0 | postmaster
(1 row)0 = 使用系统默认大页大小
常见大页大小:
Intel / AMD : 2MB、1GB
IBM POWER : 16MB、16GB
ARM : 64kB、2MB、32MB、1GB非默认值目前仅 Linux 支持
官方说明:huge_page_size 的非默认设置目前仅在 Linux 上支持。其他平台不要乱设。
九、shared_memory_type 与 dynamic_shared_memory_type
SELECT name, setting, context FROM pg_settings
WHERE name IN ('shared_memory_type', 'dynamic_shared_memory_type'); name | setting | context
----------------------------+---------+-----------
dynamic_shared_memory_type | posix | postmaster
shared_memory_type | mmap | postmaster
(2 rows)| 参数 | 取值 | 说明 |
|---|---|---|
shared_memory_type | mmap(多数平台默认) | 用 mmap 分配匿名共享内存 |
sysv | 用 shmget 分配 System V 共享内存(不推荐) | |
windows | Windows 共享内存 | |
dynamic_shared_memory_type | posix | 用 shm_open 分配 POSIX 共享内存 |
sysv | System V 共享内存 | |
windows | Windows |
sysv 不推荐
官方明确说明:sysv 选项「在任何平台上都不是默认值,通常不推荐使用,因为它通常需要非默认的内核设置才能支持大额分配」。
并行查询依赖动态共享内存
并行查询的工作进程之间通过动态共享内存通信。如果 dynamic_shared_memory_type 配置有问题,并行查询会退化甚至报错。
-- 确认动态共享内存后端在运行
SELECT count(*) FROM pg_stat_activity WHERE backend_type = 'parallel worker';十、max_stack_depth:执行栈上限
SELECT name, setting, unit, context FROM pg_settings WHERE name = 'max_stack_depth'; name | setting | unit | context
------------------+---------+------+-----------
max_stack_depth | 2048 | kB | superuser
(1 row)默认 2MB,对应内核栈限制减去约 1MB 安全余量。
为什么需要余量:
服务器并非在每个例程里都检查栈深度,
只在「可能递归」的关键例程里检查。
因此必须留出不能精确检查的部分所需的空间。# 查看内核栈限制
ulimit -s8192内核 8MB,PostgreSQL 默认 2MB(保守),余量充足。
若复杂 PL/pgSQL 函数报「stack depth limit exceeded」,
可在不超过内核限制的前提下调大 max_stack_depth。-- 调大(只影响本会话)
SET max_stack_depth = '6MB';设置超过内核限制会崩溃
官方警告:把 max_stack_depth 设得高于实际内核限制,会让失控的递归函数直接压垮后端进程(crash)。在不提供内核限制信息的平台上,服务器无法阻止你设成不安全的值。
十一、max_prepared_transactions:预备事务槽位
SELECT name, setting, context FROM pg_settings WHERE name = 'max_prepared_transactions'; name | setting | context
---------------------------+---------+-----------
max_prepared_transactions | 0 | postmaster
(1 row)0(默认)= 完全关闭两阶段提交(PREPARE TRANSACTION)
若使用两阶段提交(如分布式事务、某些中间件):
建议 >= max_connections
让每个会话都可能有预备事务挂起-- 查看当前是否有预备事务
SELECT gid, prepared, owner, database FROM pg_prepared_xacts;备库上必须 >= 主库
官方规定:运行备库(standby)时,max_prepared_transactions 必须设为与主库相同或更高的值,否则备库上将不允许执行查询。
# 主库
max_prepared_transactions = 200
# 备库(同值或更高)
max_prepared_transactions = 200十二、wal_buffers:WAL 缓冲区
WAL(Write-Ahead Log,预写式日志)在写入磁盘之前,先在共享内存里攒一批。wal_buffers 就是这块共享内存的大小。
SELECT name, setting, unit, context, boot_val FROM pg_settings WHERE name = 'wal_buffers'; name | setting | unit | context | boot_val
-------------+---------+------+------------+----------
wal_buffers | -1 | 8kB | postmaster | -1
(1 row)默认值 -1 = 自动选择:shared_buffers / 32(约 3%)
下限 64kB
上限「一个 WAL 段」的大小,通常 16MB-- 例:shared_buffers = 128MB = 16384 块
-- 16384 / 32 = 512 块 = 4MB → 在 [64kB, 16MB] 内 → 取 4MB| 规则 | 说明 |
|---|---|
-1(默认) | 自动 = shared_buffers 的 1/32,不小于 64kB、不超过一个 WAL 段(通常 16MB) |
正值且 < 32kB | 一律按 32kB 处理 |
| 不带单位 | 按 WAL 块理解,即 XLOG_BLCKSZ 字节,通常 8kB |
| 生效时机 | 只能 server start(postmaster 上下文) |
# postgresql.conf —— 必须重启
wal_buffers = '16MB'WAL 缓冲的内容「每次提交就写出去」
官方说明:WAL 缓冲的内容在每次事务提交时就写到磁盘,所以把它设得极大收益有限。但在「很多客户端同时提交」的繁忙服务器上,设为至少几 MB 可以提升写入性能。默认的 -1 自动取值在多数场景下已经是合理结果。
别把它当成「写缓存」来优化
wal_buffers 不是「攒得越多、写盘越少」的写缓存——它不能推迟持久化。真正的批量提交优化手段是 commit_delay / commit_siblings、以及把 synchronous_commit 调低。
-- 核对实际分配(pg_shmem_allocations 里名为 "XLOG Buffers")
SELECT name, pg_size_pretty(size) AS size
FROM pg_shmem_allocations WHERE name = 'XLOG Buffers'; name | size
---------------+---------
XLOG Buffers | 4194304
(1 row)4194304 字节 = 4MB,与 shared_buffers = 128MB 时 的 1/32 相符。调大 wal_buffers 的判断依据:
· 观察 pg_stat_wal(PG14+)的 wal_buffers_full 是否持续增长
· 若 wal_buffers_full 增长明显 → 可加大-- PG14+:WAL 活动统计
SELECT wal_records, wal_fpi, wal_bytes, wal_buffers_full, stats_reset
FROM pg_stat_wal; wal_records | wal_fpi | wal_bytes | wal_buffers_full | stats_reset
-------------+---------+------------+------------------+-------------------------------
4821003 | 120984 | 1284531200 | 421 | 2026-09-01 00:00:12.334521+08
(1 row)wal_buffers_full > 0 且持续增长 = 缓冲被写满、需要等待刷盘 → 可考虑调大
但即便是繁忙系统,一般也不需要超过 16MB(一个 WAL 段)十三、内存参数与并发会话的估算
所有「每会话」参数都要乘以并发数,做容量规划时必须一起算。
总内存需求 ≈ 共享内存(固定)
+ Σ 每会话内存 × 活跃会话数
+ 操作系统预留
共享内存 = shared_buffers
+ 各类小缓冲(commit_timestamp / multixact / notify / ...)
+ WAL 缓冲(wal_buffers)
+ 锁表等其它结构
每会话内存 = work_mem × 并发操作数
+ temp_buffers(仅用临时表时)
+ 其它小结构-- 查看共享内存实际分配明细(PG14+)
SELECT * FROM pg_shmem_allocations ORDER BY size DESC LIMIT 15; name | off | size | allocated_size
-------------------------------+-----------+-----------+----------------
Buffer Blocks | 1048576 | 134217728 | 134217728
XLOG Buffers | | 4194304 | 4194304
...
(15 rows)pg_shmem_allocations 是核对共享内存的好工具
它能列出每块共享内存结构的实际大小,帮你验证「shared_buffers + 各小缓冲」的真实占用,排查「共享内存比预期大」的问题。
实战示例
场景一:大量 LISTEN/NOTIFY 导致等待
-- 步骤 1:观察是否有 notify 相关等待
SELECT wait_event_type, wait_event, count(*)
FROM pg_stat_activity
WHERE wait_event IS NOT NULL
GROUP BY 1, 2
ORDER BY 3 DESC; wait_event_type | wait_event | count
-----------------+----------------+-------
LWLock | NotifyQueueLock| 12
(1 row)多个会话在 NotifyQueueLock 上等待 → notify 缓冲可能偏小。-- 步骤 2:确认当前配置
SHOW notify_buffers; notify_buffers
----------------
16-- 步骤 3:调大(需重启)
ALTER SYSTEM SET notify_buffers = 128;-- 步骤 4:确认需要重启
SELECT name, setting, pending_restart
FROM pg_settings WHERE name = 'notify_buffers'; name | setting | pending_restart
-----------------+---------+-----------------
notify_buffers | 128 | t
(1 row)# 步骤 5:在维护窗口重启
pg_ctl restart -D /var/lib/postgresql/18/main-- 步骤 6:核验
SELECT name, setting, pending_restart FROM pg_settings WHERE name = 'notify_buffers'; name | setting | pending_restart
-----------------+---------+-----------------
notify_buffers | 128 | f
(1 row)场景二:PL/pgSQL 递归函数报栈深度超限
-- 步骤 1:复现错误
CREATE OR REPLACE FUNCTION count_down(n int) RETURNS int
LANGUAGE plpgsql AS $$
BEGIN
IF n <= 0 THEN
RETURN 0;
END IF;
RETURN count_down(n - 1) + 1;
END;
$$;
SELECT count_down(500000);ERROR: stack depth limit exceeded
HINT: Increase the configuration parameter "max_stack_depth" (currently 2MB), after ensuring the platform's stack depth limit is adequate.# 步骤 2:先确认真实内核限制
ulimit -s8192内核 8MB → 可安全设到约 7MB(留 1MB 余量)-- 步骤 3:会话内临时调大(不重启)
SET max_stack_depth = '6MB';
SELECT count_down(500000); count_down
------------
500000
(1 row)-- 步骤 4:若确为常态需求,全局固化
ALTER SYSTEM SET max_stack_depth = '6MB';
SELECT pg_reload_conf();max_stack_depth 不要超过内核限制
只设到「内核限制 - 1MB」以内。设过头会导致递归失控时整个后端进程崩溃。
场景三:大 shared_buffers 实例启用大页
# 步骤 1:查看当前大页配置
grep -i huge /proc/meminfoHugePages_Total: 0
HugePages_Free: 0
Hugepagesize: 2048 kB-- 步骤 2:确认 PostgreSQL 当前未用上大页
SELECT name, setting FROM pg_settings WHERE name = 'huge_pages_status'; name | setting
-------------------+---------
huge_pages_status | off
(1 row)shared_buffers = 32GB,未用大页 → 页表开销可观,值得启用。# 步骤 3:计算需要多少大页
# 32GB shared_buffers,2MB 大页 → 约 16384 页,多留余量取 17000# 步骤 4:临时配置大页(测试)
sysctl -w vm.nr_hugepages=17000
grep -i huge /proc/meminfoHugePages_Total: 17000
HugePages_Free: 17000
Hugepagesize: 2048 kB# 步骤 5:先在 postgresql.conf 用 try 验证
huge_pages = try# 重启
pg_ctl restart -D /var/lib/postgresql/18/main-- 步骤 6:确认真正用上了大页
SELECT name, setting FROM pg_settings WHERE name = 'huge_pages_status'; name | setting
-------------------+---------
huge_pages_status | on
(1 row)# 步骤 7:确认稳定后,写进 sysctl.conf 持久化
echo 'vm.nr_hugepages=17000' >> /etc/sysctl.conf# 步骤 8:若追求「必须生效」,改为 on(大页不足会启动失败,需确保配置充足)
huge_pages = onon 之前务必确认大页充足
改 on 之后若大页数量不足,PostgreSQL 会直接启动失败。生产环境变更前,先确认 HugePages_Free 足够,并准备好回滚方案。
场景四:32GB 内存服务器的完整参数配置
把前面几节讲的参数串起来,给一台 32GB 内存、专用跑 PostgreSQL 18 的服务器写一份可落地的配置。假设负载是「中等并发 OLTP + 每日报表」,max_connections = 200。
内存预算(32GB 物理内存):
shared_buffers 8GB (25% 法则)
操作系统页缓存 约 16GB(把 effective_cache_size 设为它)
work_mem 峰值余量 约 3GB
maintenance_work_mem 1~2GB (按需,非长期占用)
wal_buffers 16MB
各小缓冲合计 < 10MB
预留余量 若干# ============================================================
# postgresql.conf —— PostgreSQL 18,32GB 内存专用服务器
# ============================================================
# ---------- 共享内存(postmaster,需重启) ----------
shared_buffers = '8GB' # 25% × 32GB
huge_pages = try # 大页:先 try,确认 huge_pages_status = on
huge_page_size = 0 # 0 = 系统默认(Linux 通常 2MB)
shared_memory_type = mmap # Linux 默认,且大页仅在 mmap 下可用
dynamic_shared_memory_type = posix
wal_buffers = -1 # 自动 = shared_buffers/32 = 256MB?
# 注意上限是一个 WAL 段(通常 16MB)
# 所以实际取 16MB;明确写 wal_buffers = '16MB' 亦可
# ---------- PG18 小缓冲(postmaster,需重启) ----------
commit_timestamp_buffers = 0 # 自动 = shared_buffers/512,16~1024 块
subtransaction_buffers = 0 # 自动
transaction_buffers = 0 # 自动
multixact_member_buffers = 32 # 默认
multixact_offset_buffers = 16 # 默认
notify_buffers = 16 # 默认;LISTEN/NOTIFY 密集时可加大
serializable_buffers = 32 # 默认;SERIALIZABLE 事务密集时可加大
# ---------- 查询执行内存(user,reload 生效) ----------
work_mem = '8MB' # OLTP 保守值;报表角色另用 ALTER ROLE 放大
hash_mem_multiplier = 2.0 # 默认;哈希操作额度 = work_mem × 2
# ---------- 维护内存(reload 生效) ----------
maintenance_work_mem = '1GB' # 手动 VACUUM / CREATE INDEX
autovacuum_work_mem = '256MB' # 与上面拆开,防止 autovacuum 放大
# ---------- 优化器内存估算(user,reload 生效) ----------
effective_cache_size = '24GB' # 约 75% 内存(含 shared_buffers)
# ---------- 会话内存 ----------
temp_buffers = '16MB' # 默认 8MB;有临时表场景可放大
max_stack_depth = '6MB' # 内核 ulimit -s 若为 8MB,留 2MB 余量
# ---------- 非内存但常一起调 ----------
max_connections = 200
max_prepared_transactions = 0 # 不用两阶段提交就保持 0-- 应用后逐项核对(分两批:reload 批 + 重启批)
SELECT name, setting, unit, context, source, pending_restart
FROM pg_settings
WHERE name IN (
'shared_buffers','wal_buffers','temp_buffers','max_stack_depth',
'work_mem','hash_mem_multiplier','maintenance_work_mem','autovacuum_work_mem',
'effective_cache_size','commit_timestamp_buffers','transaction_buffers',
'subtransaction_buffers','multixact_member_buffers','multixact_offset_buffers',
'notify_buffers','serializable_buffers','huge_pages','huge_pages_status',
'shared_memory_type','dynamic_shared_memory_type','max_prepared_transactions'
)
ORDER BY context, name; -- 关键点:pending_restart = t 的参数必须在维护窗口重启才生效-- 重启后确认大页真的用上了
SELECT name, setting FROM pg_settings WHERE name = 'huge_pages_status'; name | setting
-------------------+---------
huge_pages_status | on
(1 row)-- 核对共享内存真实占用(Buffer Blocks 应约为 8GB)
SELECT name, pg_size_pretty(size) AS size
FROM pg_shmem_allocations
WHERE name IN ('Buffer Blocks', 'XLOG Buffers')
ORDER BY size DESC; name | size
----------------+---------
Buffer Blocks | 8192 MB
XLOG Buffers | 16 MB
(2 rows)-- 最后确认工作内存类的实际值(用户会话视角)
SELECT name, setting, unit FROM pg_settings
WHERE name IN ('work_mem','maintenance_work_mem','effective_cache_size','temp_buffers')
ORDER BY name; name | setting | unit
-----------------------+---------+------
effective_cache_size | 3145728 | 8kB
maintenance_work_mem | 1048576 | kB
temp_buffers | 2048 | 8kB
work_mem | 8192 | kB
(4 rows)这份配置不是「抄了就好」
上面每个数字都依赖具体的并发量、查询形态与硬件。请按本站各节的判读方法(EXPLAIN 的 Sort Method / Batches、pg_stat_wal 的 wal_buffers_full、pg_shmem_allocations、huge_pages_status)验证后再固化,不要照搬。
注意事项
不要把 0 理解成「关闭」。 commit_timestamp_buffers = 0 等表示按 shared_buffers 自动推导,不是禁用。
-- 0 = 自动(shared_buffers/512,16~1024 块)
SELECT name, setting, unit FROM pg_settings
WHERE name IN ('commit_timestamp_buffers','transaction_buffers','subtransaction_buffers');不要忽略这些参数的单位是「块」而非「kB」。 与 work_mem 的规则不同。
-- temp_buffers = 1024 → 1024 × 8kB = 8MB,不是 1024kB
SELECT name, setting * 8192 AS bytes FROM pg_settings WHERE name = 'temp_buffers';不要以为改完这些参数不用重启。 上表里绝大多数小缓冲和内存类型参数都是 postmaster 上下文。
-- 检查是否有待重启的改动
SELECT name, setting, pending_restart FROM pg_settings WHERE pending_restart;不要在 huge_pages = on 且大页不足时重启。 会导致实例起不来。
# 危险:大页不足时直接启动失败
huge_pages = on
# 稳妥:先用 try 观察 huge_pages_status
huge_pages = try不要把 max_stack_depth 设得超过内核栈限制。
ulimit -s-- 设成「内核限制 - 1MB」以内
ALTER SYSTEM SET max_stack_depth = '6MB';不要试图在用过临时表之后再 SET temp_buffers。 该会话内已失效。
-- 错误!
CREATE TEMP TABLE t1 (id int);
SET temp_buffers = '64MB'; -- 无效不要在只有少量共享缓冲的实例上纠结 multixact/notify 缓冲。 这些项很小,通常不是瓶颈。
32 块 × 8kB = 256kB,量级极小
先优化 shared_buffers / work_mem,再回头看这些不要把 wal_buffers 当成「写缓存」来调大,也不要混淆三个 -1 与 0 的语义。
-- 判断是否真的需要调大:看 wal_buffers_full 是否持续增长(PG14+)
SELECT wal_buffers_full FROM pg_stat_wal;wal_buffers = -1 → 自动 = shared_buffers/32(64kB ~ 16MB)
autovacuum_work_mem = -1 → 跟随 maintenance_work_mem
commit_timestamp_buffers = 0 → 自动 = shared_buffers/512(16 ~ 1024 块)不要忽略备库上 max_prepared_transactions 必须 >= 主库。 否则备库不允许查询。
-- 核验主备一致
SHOW max_prepared_transactions;不要在未确认平台支持的情况下调 huge_page_size。 非默认值目前仅 Linux 支持。
-- 保持默认 0(用系统默认大页大小)最安全
SELECT name, setting FROM pg_settings WHERE name = 'huge_page_size';不要把 shared_memory_type 改成 sysv。 官方不推荐,且通常需要非默认内核设置。
# 保持默认
shared_memory_type = mmap不要忘记动态共享内存对并行查询的影响。 配置异常会导致并行退化。
-- 确认并行工作进程能起来
EXPLAIN (ANALYZE) SELECT ... ; -- 观察是否有 Parallel 节点不要只算 shared_buffers 就以为算清了共享内存。 还有各小缓冲、WAL 缓冲、锁表等。
-- 看真实明细
SELECT * FROM pg_shmem_allocations ORDER BY size DESC LIMIT 20;最佳实践
先优化「大三样」(shared_buffers、work_mem、maintenance_work_mem),再考虑这些细节参数。
SELECT name, setting, unit FROM pg_settings
WHERE name IN ('shared_buffers','work_mem','maintenance_work_mem','effective_cache_size')
ORDER BY name;temp_buffers 对「可能用大临时表」的会话可适当设大,成本很低。
# 每个 buffer 描述符仅约 64 字节,实际用到才占 8kB
temp_buffers = '8MB'用 pg_shmem_allocations 核对共享内存真实占用,做容量规划。
SELECT name, pg_size_pretty(size) AS size, pg_size_pretty(allocated_size) AS allocated
FROM pg_shmem_allocations ORDER BY size DESC LIMIT 20;wal_buffers 一般保持默认 -1;仅当 wal_buffers_full 持续增长时才显式设为几 MB ~ 16MB。
# 默认即自动:shared_buffers/32,64kB ~ 一个 WAL 段
wal_buffers = -1-- 依据一:WAL 缓冲写满的次数(PG14+)
SELECT wal_buffers_full FROM pg_stat_wal;
-- 依据二:共享内存里 XLOG Buffers 的实际大小
SELECT name, pg_size_pretty(size) FROM pg_shmem_allocations WHERE name = 'XLOG Buffers';大 shared_buffers 实例启用大页,并先用 try 验证。
huge_pages = try
huge_page_size = 0SELECT name, setting FROM pg_settings WHERE name = 'huge_pages_status';huge_pages = on 前务必确认大页充足,并准备回滚方案。
grep -i huge /proc/meminfomax_stack_depth 设为「内核栈限制 - 1MB」以内,且仅在确有需要时调。
ulimit -s# 内核 8MB → 设 6MB
max_stack_depth = '6MB'使用两阶段提交时,max_prepared_transactions 设为 >= max_connections,并保证主备一致。
max_prepared_transactions = 200大量 LISTEN/NOTIFY 或 SERIALIZABLE 事务的场景,适当调大对应的专用缓冲。
notify_buffers = 128
serializable_buffers = 64保持 shared_memory_type = mmap 与 dynamic_shared_memory_type = posix(Linux 默认)。
shared_memory_type = mmap
dynamic_shared_memory_type = posix把所有「需重启」的内存参数汇总,在维护窗口统一变更。
SELECT name, setting, context FROM pg_settings
WHERE context = 'postmaster' AND category ILIKE '%memory%'
ORDER BY name;把这些参数纳入配置版本库,并注明「需重启」。
# postgresql.conf(纳入 git)
# ---- 需重启生效 ----
huge_pages = try
shared_memory_type = mmap
max_prepared_transactions = 0
# ---- 会话级 ----
temp_buffers = '8MB'变更后用 pending_restart 确认状态,避免「以为生效了其实没有」。
SELECT name, setting, pending_restart FROM pg_settings WHERE pending_restart;下一节
继续学习:EXPLAIN 基础
参考链接
- PostgreSQL 官方文档:Resource Consumption / Memory(runtime-config-resource)
- PostgreSQL 官方文档:Preset Options / huge_pages_status(runtime-config-preset)
- PostgreSQL 官方文档:Managing Kernel Resources / Huge Pages(kernel-resources)
- PostgreSQL 官方文档:The pg_settings View(view-pg-settings)
- PostgreSQL 官方文档:Shared Memory Allocations(view-pg-shmem-allocations)
- PostgreSQL 官方文档:Connection Settings / max_connections(runtime-config-connection)
- PostgreSQL 官方文档:PREPARE TRANSACTION(sql-prepare-transaction)
- PostgreSQL 官方文档:PL/pgSQL Structure(plpgsql-structure)