
PostgreSQL 的内存结构是其高效运行的核心,合理配置和监控内存组件对性能、稳定性和并发能力至关重要。下面详细拆解其内存架构:
一、内存结构概览
PostgreSQL 内存主要分为三大类:
- 共享内存 (Shared Memory):由
Postmaster进程在启动时分配,供所有后台进程和客户端后端进程共同访问。 - 进程私有内存 (Process Private Memory):每个后端进程(包括后台辅助进程和客户端连接进程)独立拥有的内存区域。
- 操作系统磁盘缓存 (OS Page Cache):虽不由 PostgreSQL 直接管理,但对性能影响巨大,用于缓存从磁盘读取的数据文件。
二、共享内存组件详解
1. 共享缓冲区 (Shared Buffers)
- 作用功能:
- 核心缓存池!缓存从数据文件(表、索引) 中读取的数据块(页)。
- 所有进程读写数据文件必须通过共享缓冲区(
O_DIRECT模式除外)。修改先在缓冲区中进行,再由后台进程异步刷盘。 - 极大减少物理 I/O,提升查询速度。
- 参数:
shared_buffers(单位通常为 MB 或 GB) - 出问题的影响:
- 设置过小:
- 缓存命中率低: 频繁从磁盘读取数据,查询性能急剧下降。
- Checkpointer/Background Writer 压力大: 脏页积压快,检查点风暴,I/O 压力剧增,影响整体吞吐量。
- 举例:
SELECT pg_stat_statements_reset();后运行典型查询,反复执行SELECT * FROM pg_stat_database WHERE datname = 'yourdb';观察blks_read(物理读) 远高于blks_hit(缓存命中) 或pg_statio_user_tables中heap_blks_read很高。
- 设置过大:
- 浪费内存: 超出实际热数据集大小的部分无效。
- 操作系统缓存减少: 挤占 OS Page Cache 空间,可能降低 WAL、临时文件等操作的效率。
- 管理开销增加: 内部锁(
buffer pin/buffer content lock)竞争可能加剧(在极高并发或超大shared_buffers时)。
- 设置过小:
- 排查命令:
- 命中率:
SELECT (sum(blks_hit)) / nullif(sum(blks_hit + blks_read), 0) AS hit_ratio FROM pg_stat_database; -- 目标 > 99%, 低于 95% 通常表示太小 - 缓冲区使用详情 (需要
pg_buffercache扩展):CREATE EXTENSION IF NOT EXISTS pg_buffercache; -- 查看各对象在缓冲区中的占比 SELECT c.relname, count(*) AS buffers, round(count(*) * 100.0 / (SELECT setting FROM pg_settings WHERE name = 'shared_buffers')::numeric, 2) AS percent FROM pg_buffercache b JOIN pg_class c ON b.relfilenode = pg_relation_filenode(c.oid) GROUP BY c.relname ORDER BY buffers DESC LIMIT 10; -- 查看脏页 SELECT * FROM pg_buffercache WHERE isdirty; - 表/索引 I/O 统计:
SELECT * FROM pg_statio_user_tables; SELECT * FROM pg_statio_user_indexes; -- 关注 `heap_blks_read` (物理读) vs `heap_blks_hit` (缓存命中)
- 命中率:
- 配置建议:
- 初始值:通常为系统总内存的 15%-25%。例如 32GB 内存的机器,可设 4GB - 8GB。
- 大型专用数据库:可增至 30%-40%,但需密切监控 OS Cache 使用和命中率。
- 超过 40GB 需谨慎测试,管理开销可能抵消收益。超大内存系统(>128GB)可能受益于更高比例,但非绝对。
2. WAL 缓冲区 (WAL Buffers)
- 作用功能:
- 临时缓存 WAL (Write-Ahead Log) 记录。
- 事务提交时,其对应的 WAL 记录必须从 WAL Buffer 刷新到磁盘 (
pg_wal目录),确保事务持久性。 - 批量提交时减少对 WAL 文件的频繁 I/O。
- 参数:
wal_buffers(单位通常为 MB,默认 -1 表示自动管理,通常约为shared_buffers的 1/32,上限为XLOG_BLCKSZ的倍数)。 - 出问题的影响:
- 设置过小:
- WAL 写入更频繁: WalWriter 进程或后端进程被迫更频繁地刷盘,增加 I/O 压力,可能导致事务提交延迟 (
commit_delay设置无效时)。 - 高并发写入瓶颈: 大量并发提交时,可能因缓冲区满而短暂阻塞。
- WAL 写入更频繁: WalWriter 进程或后端进程被迫更频繁地刷盘,增加 I/O 压力,可能导致事务提交延迟 (
- 设置过大: 通常收益有限,浪费少量内存。崩溃时可能丢失更多未刷盘的 WAL (但不会破坏一致性)。
- 设置过小:
- 排查命令:
- 监控 WAL 活动:
SELECT * FROM pg_stat_wal; -- PostgreSQL 14+ -- 或 SELECT pg_current_wal_lsn(), pg_walfile_name(pg_current_wal_lsn()); -- 查看当前 WAL 位置 - 观察日志:如出现
WAL buffers written at high frequency类警告。
- 监控 WAL 活动:
- 配置建议: 通常 保持默认 (-1) 让 PostgreSQL 自动管理即可。如果遇到高并发写入提交延迟,且
wal_writer_delay调优无效,可显式设置为 16MB 左右。
3. CLOG (Commit Log) 缓冲区
- 作用功能:
- 缓存事务提交状态 (Committed, Aborted, In Progress)。最终会持久化到
pg_xact目录下的文件。 - 加速事务状态的查询。
- 缓存事务提交状态 (Committed, Aborted, In Progress)。最终会持久化到
- 管理: 完全由 PostgreSQL 内部管理,无用户可配置参数。
- 出问题的影响: 内部机制,通常对用户透明。内存需求很小。崩溃恢复时会重放 WAL 重建 CLOG。
4. 锁空间 (Lock Space)
- 作用功能: 存储所有 锁信息 (行锁、页锁、表锁、事务锁等) 的共享内存区域。
- 参数:
max_locks_per_transaction(间接影响锁空间大小),max_connections(影响锁对象总数上限)。 - 出问题的影响:
- 锁空间耗尽:
- 严重错误! 新事务无法获取锁,报错
ERROR: out of shared memory/HINT: You might need to increase max_locks_per_transaction。 - 原因: 超长事务持有大量锁;
max_locks_per_transaction或max_connections设置过低;大量分区表或子分区。
- 严重错误! 新事务无法获取锁,报错
- 锁空间耗尽:
- 排查命令:
- 查看当前锁信息:
SELECT * FROM pg_locks; SELECT pg_blocking_pids(pid); -- 查找阻塞源 SELECT * FROM pg_stat_activity WHERE state = 'idle in transaction'; -- 长空闲事务 - 监控锁使用:无直接视图,耗尽时日志报错明确。
- 查看当前锁信息:
- 配置建议: 默认
max_locks_per_transaction = 64通常够用。如果使用大量分区表(如数百/数千),需 显著增加此值 (如 256, 512 甚至更高)。增加后需重启。
5. 其他共享内存组件
- 进程通信区 (ProcArray): 存储所有活动事务的 XID 和快照信息。用于 MVCC 可见性判断。
- 共享目录缓存: 缓存系统目录(如
pg_class,pg_attribute)信息,减少磁盘访问。 - 共享谓词锁: 用于 SERIALIZABLE 隔离级别。
- 管理: 这些组件大小由内部逻辑或
max_connections等参数间接决定,通常无需直接配置。
三、进程私有内存组件详解
1. 工作内存 (work_mem)
- 作用功能:
- 供单个后端进程执行 排序 (ORDER BY, DISTINCT, GROUP BY)、哈希连接 (Hash Join)、位图操作 (Bitmap Heap Scan) 和 临时表 等操作使用。
- 每个操作都可能使用最多
work_mem的内存。一个复杂查询可能同时进行多个排序/哈希操作。
- 参数:
work_mem(单位 KB, MB) - 出问题的影响:
- 设置过小:
- 大量磁盘临时文件: 排序或哈希操作无法在内存完成,被迫使用磁盘上的临时文件 (
$PGDATA/base/pgsql_tmp)。性能灾难! 查询慢数倍甚至数十倍。 - 举例:
EXPLAIN (ANALYZE, BUFFERS)查看执行计划,出现Sort Method: external merge Disk或HashAggregate Batches很大,且temp read/writeI/O 很高。pg_stat_statements中temp_blks_(read|written)巨大。
- 大量磁盘临时文件: 排序或哈希操作无法在内存完成,被迫使用磁盘上的临时文件 (
- 设置过大:
- 内存浪费/耗尽: 单个查询占用过多内存,尤其在并发高时,可能导致 OOM (Out-Of-Memory) 杀死 PostgreSQL 进程或触发操作系统 OOM Killer。
- 加剧内存碎片化。
- 设置过小:
- 排查命令:
- 识别使用临时文件的查询:
-- 使用 pg_stat_statements 扩展 CREATE EXTENSION IF NOT EXISTS pg_stat_statements; SELECT queryid, query, calls, temp_blks_read, temp_blks_written, temp_blk_read_time, temp_blk_write_time FROM pg_stat_statements ORDER BY (temp_blks_read + temp_blks_written) DESC LIMIT 10; - 分析执行计划:
EXPLAIN (ANALYZE, BUFFERS) <Your Slow Query>; -- 注意输出中的 `Sort`/`HashAggregate`/`Hash Join` 节点,是否有 `Disk` 字样或高 `Batches` 值 - 查看会话内存使用 (Linux):
# 找到对应后端的 PID (来自 pg_stat_activity.pid) ps -p <PID> -o rss,size,vsize # 或使用 pg_top/pg_activity 等工具
- 识别使用临时文件的查询:
- 配置建议:
- 计算:
work_mem = (可用物理内存 - shared_buffers) / (max_connections * 并行操作平均数量)可用物理内存:总内存减去 OS 和其他应用预留。并行操作平均数量:保守估计 2-3,复杂报表系统可能 4-6。
- 示例: 64GB 内存,
shared_buffers=16GB,max_connections=100,假设并行操作数 3:
work_mem = (64GB - 16GB) / (100 * 3) ≈ 48GB / 300 ≈ 163MB - 动态调整: 可在会话级或用户级覆盖
SET work_mem = '256MB';,用于特定需要大内存的查询。
- 计算:
2. 维护工作内存 (maintenance_work_mem)
- 作用功能:
- 供 维护操作 使用:
VACUUM (FULL)、CREATE INDEX、REINDEX、ALTER TABLE ADD FOREIGN KEY、CLUSTER。 - 通常比
work_mem大得多。
- 供 维护操作 使用:
- 参数:
maintenance_work_mem(单位 KB, MB, GB) - 出问题的影响:
- 设置过小:
- 维护操作极慢:
VACUUM清理索引慢;CREATE INDEX需要多次合并排序段,I/O 高耗时长。 - 举例:
VACUUM (VERBOSE)报告index scan took XXX seconds很长。CREATE INDEX进度缓慢。
- 维护操作极慢:
- 设置过大: 浪费内存,通常影响较小,除非同时运行多个大型维护操作(如多个并发
CREATE INDEX)。
- 设置过小:
- 排查命令:
- 监控维护操作时长 (
pg_stat_progress_create_index,pg_stat_progress_vacuum)。 - 观察维护操作时的系统内存使用 (
free,top)。
- 监控维护操作时长 (
- 配置建议:
- 通常设为 系统总内存的 5%-10% 或 1GB - 4GB。大型数据库可设 8GB+。
- 比
work_mem大一个数量级是常见做法。例如work_mem=128MB->maintenance_work_mem=1GB。 - 对
autovacuum特别重要!增大它能显著加速自动清理,减少膨胀和抗回绕风险。
3. 临时缓冲区 (Temp Buffers)
- 作用功能: 为每个后端进程缓存 临时表 (Temporary Tables) 的数据块。
- 参数:
temp_buffers(单位通常为 8KB blocks, 默认 8MB = 1024 blocks)。 - 出问题的影响:
- 设置过小: 频繁访问的临时表数据可能需要多次加载,影响使用临时表的查询性能。
- 设置过大: 浪费内存,临时表用得少时尤其明显。
- 配置建议: 默认值通常足够。如果应用重度依赖大型临时表,可适当增加 (如 32MB - 128MB)。可在会话中
SET temp_buffers = ...动态调整。
4. 其他私有内存
- 查询解析/规划器状态: 存储解析树、执行计划等。通常不大,但复杂查询或大量绑定变量时会增加。
- 连接状态 (libpq buffer, row storage): 客户端通信缓冲区、结果集缓存。
- 栈空间 (Stack): 每个进程的调用栈。由操作系统和
ulimit -s控制。
四、操作系统磁盘缓存 (OS Page Cache)
- 作用功能:
- 操作系统内核将未被
shared_buffers缓存的、频繁访问的 数据文件、WAL 文件、临时文件 等缓存起来。 - 对 PostgreSQL 性能至关重要! 尤其是当
shared_buffers无法容纳整个工作集时。
- 操作系统内核将未被
- 管理: 完全由操作系统管理。PostgreSQL 通过标准文件 I/O 接口 (
read/write) 与之交互。 - 出问题的影响: 如果系统总内存不足,OS Page Cache 会被挤占:
- 导致更多 真正的物理 I/O (直接读盘)。
- 严重影响 WAL 写入、Checkpoint 刷盘、临时文件操作、顺序扫描大表等的速度。
- 排查命令 (OS Level):
- Linux:
free -h # 查看 total, used, free, buff/cache (重点!) sar -r 1 # 监控内存使用趋势 sar -B 1 # 监控 Page In/Out (缺页中断) iostat -dxm 1 # 监控磁盘 I/O 利用率、等待队列、读写量 - 通用: 监控磁盘 I/O 延迟 (
await,svctminiostat),高延迟往往是内存不足或磁盘瓶颈的标志。
- Linux:
- 优化: 确保系统有充足的可用内存供 OS Cache 使用。避免在数据库服务器上运行其他内存消耗大的应用。
shared_buffers不宜过大以免过度挤占 OS Cache。
五、关键全局内存参数总结
| 参数名 | 作用域 | 主要功能 | 典型建议值 | 风险 |
|---|---|---|---|---|
shared_buffers | Global (重启生效) | 核心数据缓存 | 总内存 15%-40% (专用DB) | 过小: 性能差;过大: 浪费/挤占OS Cache |
wal_buffers | Global (重启生效) | WAL 记录缓存 | 默认 -1 (自动) 或 ~16MB | 过小: 增加WAL I/O/提交延迟 |
max_locks_per_transaction | Global (重启生效) | 控制锁空间大小 | 默认 64;大量分区表需增加 (256+) | 过小: 锁空间耗尽错误 |
work_mem | Session/User | 排序/哈希/临时表操作内存 | (总内存 - shared_buffers)/(连接数*并行因子) | 过小: 磁盘临时文件/慢;过大: OOM |
maintenance_work_mem | Global/Session | 维护操作内存 (VACUUM, CREATE INDEX) | 1GB - 8GB+ (总内存5%-10%) | 过小: 维护极慢;过大: 浪费 |
temp_buffers | Session | 临时表缓存 | 默认 8MB;重度临时表可增至 32-128MB | 通常风险低 |
max_connections | Global (重启生效) | 最大并发连接数 | 按需设置! 避免过高 (数百) | 过高: 内存耗尽 (每个连接有私有内存开销) |
六、内存问题综合排查流程
- 监控 OS 内存:
free,top,vmstat查看总内存、OS Cache、Swap 使用。确保available内存充足,swap usage低。 - 监控 PostgreSQL 共享内存:
pg_buffercache看缓冲区分布和命中率。- 检查是否有锁空间耗尽错误 (日志 /
ERROR: out of shared memory)。 - 监控 WAL 活动 (
pg_stat_wal).
- 监控后端进程私有内存:
pg_stat_statements识别高temp_blks_*查询。EXPLAIN (ANALYZE, BUFFERS)分析慢查询的执行计划,关注排序/哈希是否用磁盘。- OS 工具 (
ps,top) 或pg_top/pg_activity查看后端进程 RSS。
- 监控维护操作:
pg_stat_progress_vacuum/pg_stat_progress_create_index看速度和资源使用。- 确保
maintenance_work_mem足够大。
- 检查配置: 使用
SHOW <parameter>;或查询pg_settings确认关键内存参数值是否合理。 - 分析日志:
tail -f数据库日志,查找 OOM 错误、锁空间错误、性能警告等。
七、常见内存相关故障与处理
- OOM (Out-Of-Memory) / 被 OOM Killer 杀死:
- 现象: PostgreSQL 进程突然消失,系统日志 (
/var/log/messages/journalctl) 有Out of memory或Killed process记录。 - 原因:
work_mem或maintenance_work_mem设置过大 + 高并发。max_connections过高,大量空闲连接占用私有内存。- 复杂查询消耗过多内存。
- 操作系统本身内存耗尽。
- 处理:
- 紧急恢复: 重启 PostgreSQL (如果主进程被杀)。
- 定位: 分析系统日志和 PostgreSQL 日志,确定被杀进程和可能原因。
- 调优:
- 降低全局
work_mem/maintenance_work_mem。 - 降低
max_connections,使用连接池 (如pgbouncer)。 - 优化消耗内存的查询 (如减少排序量、避免大哈希连接)。
- 增加系统物理内存。
- 调整 OOM Killer 优先级 (Linux:
oom_score_adj),但非根本解决。
- 降低全局
- 现象: PostgreSQL 进程突然消失,系统日志 (
- 锁空间耗尽:
- 现象:
ERROR: out of shared memory/HINT: You might need to increase max_locks_per_transaction。新事务无法开始。 - 处理:
- 紧急恢复: 终止持有大量锁的长事务或空闲事务 (
SELECT pg_terminate_backend(pid);)。如果找不到,需重启并增大max_locks_per_transaction。 - 根本解决: 增加
max_locks_per_transaction(重启生效)。优化应用,避免超长事务和持有锁过久。减少不必要的分区数量。
- 紧急恢复: 终止持有大量锁的长事务或空闲事务 (
- 现象:
- 大量磁盘临时文件导致查询慢:
- 现象: 特定查询(尤其是涉及排序、分组、哈希连接、大 CTE/临时表)极慢。
EXPLAIN ANALYZE显示DiskI/O。pg_stat_statements.temp_blks_*高。 - 处理:
- 适当增加该查询或会话的
work_mem(SET work_mem = '256MB';)。 - 优化查询:
- 添加索引避免排序。
- 重写查询减少中间结果集大小。
- 使用
LIMIT提前过滤。 - 考虑更合适的连接方式 (如用
Nested Loop替代Hash Join当驱动表小)。
- 如果全局问题,评估增加全局
work_mem(需考虑并发和总内存)。
- 适当增加该查询或会话的
- 现象: 特定查询(尤其是涉及排序、分组、哈希连接、大 CTE/临时表)极慢。
- VACUUM / CREATE INDEX 极慢:
- 现象: 维护操作耗时过长,影响业务。
pg_stat_progress_*显示缓慢。 - 处理: 显著增加
maintenance_work_mem。这是最有效的加速手段。在会话中执行时可临时SET maintenance_work_mem = '2GB';。对于autovacuum,需在配置文件或ALTER SYSTEM SET中增大。
- 现象: 维护操作耗时过长,影响业务。
总结:
- PostgreSQL 内存结构复杂,需理解 共享内存 (核心缓存、WAL、锁) 和 进程私有内存 (排序/哈希、维护操作、临时表) 的区别。
- 关键参数 (
shared_buffers,work_mem,maintenance_work_mem,max_connections,max_locks_per_transaction) 需精心配置和持续监控。 - 操作系统磁盘缓存 (OS Page Cache) 是性能的无名英雄,确保系统有足够空闲内存。
- 监控是基石: 使用
pg_stat_*视图、EXPLAIN ANALYZE、pg_buffercache、pg_stat_statements和 OS 工具 (free,top,iostat)。 - 常见故障 (OOM, 锁耗尽, 磁盘临时文件, 维护慢) 通常由参数配置不当或资源竞争引起,需结合监控和日志精准定位并调整。
欢迎关注我的公众号《IT小Chen》
5万+

被折叠的 条评论
为什么被折叠?



