PostgreSQL中的进程和内存架构

PostgreSQL中的进程和内存架构

PostgreSQL 的进程架构

PostgreSQL 是 多进程架构(multi-process architecture),每个连接都有自己独立的 进程,由主进程 postmaster(也叫 postgres)派生。
进程按照功能不同分类:

类型 说明 示例进程
主进程(Postmaster Process) 数据库的“守护进程”,负责启动、停止、管理其他所有进程,监听客户端连接(默认端口 5432)。执行pg_ctl -D dbname -l logfile start 命令时启动。 postgres(启动时)
后台进程(Background Processes) 数据库后台维护任务,例如 WAL 写入、检查点、vacuum 等。 checkpointer, writer, walwriter, autovacuum launcher, archiver
后端进程(Backend Processes) 为每个客户端连接单独派生,执行 SQL 语句。 postgres: user dbname [idle]
辅助进程(Auxiliary Processes) 特殊的系统进程,支持复制、统计、恢复等。 wal receiver, logical replication launcher, stats collector
复制进程(Replication Processes) 支持主从(物理/逻辑)复制的数据流处理。 walsender, walreceiver, apply worker

PostgreSQL 的内存架构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
┌────────────────────────────────────────────────────────────┐
│ 操作系统(OS) │
│ ┌───────────────────────┐ ┌────────────────────────────┐ │
│ │ OS Page Cache / VFS │ │ Kernel page table / NUMA │ │
│ └───────────────────────┘ └────────────────────────────┘ │
│ ↑ ↑ │
│ │ │ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ Postgres Shared Memory │ │
│ │ ┌────────────┐ ┌────────────┐ ┌────────────┐ │ │
│ │ │ shared_buf │ │ wal_buf │ │ lock table │ ... │ │
│ │ └────────────┘ └────────────┘ └────────────┘ │ │
│ └────────────────────────────────────────────────────────┘ │
│ ↑ ↑ ↑ │
│ │ │ │ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ Backend / BG Processes │ │
│ │ per-backend memory: work_mem, temp_buffers, contexts │ │
│ │ relcache, syscache, executor state, local buffers │ │
│ └────────────────────────────────────────────────────────┘ │
└────────────────────────────────────────────────────────────┘

共享内存(Shared Memory)

共享内存由 PostgreSQL 在启动时申请,供所有进程访问。主要由 shared_buffers 控制其大小(可用 SHOW shared_buffers; 查看),用于缓存经常访问的数据页,以及预写日志(WAL)缓冲区(wal_buffers),用于在事务日志数据写入磁盘之前对其进行存储。
shared_buffers:
功能:缓存表/索引的 page(默认 8KB 页);几乎所有数据页的共享缓存都在这里优先查找。
结构:由 buffer descriptors(描述符)和实际 page 数据组成;每个 buffer descriptor 保存锁、标志、blkno、refcount 等。
特点:这是进程间共享的第一级缓存;避免频繁走 OS 页缓存。但 OS page cache 仍存在并协作。

进程私有内存(Per-Process Memory)

每个 backend(或 worker)有自己的一组私有内存,用于执行、临时数据、缓存等。
work_mem:
用途:排序、哈希表、合并操作等的临时内存。特点:这是 per-operationper-backend 的配额。一个查询里可能同时触发多个使用 work_mem 的操作(例如多个并行排序/哈希),所以最大同时使用量 = work_mem * 并发操作数 * 并发连接数。不要把 work_mem 设得太大而导致内存超配;可基于并发估算。

临时文件:
当排序或哈希操作超过 work_mem 时,会把中间数据溢出到磁盘上的临时文件(位于 base/pgsql_tmp),会导致磁盘 I/O。

与操作系统页缓存(OS Page Cache)的关系与交互

PostgreSQLshared_buffers 是自定义用户空间缓存;但当数据页被从磁盘读入 shared_buffers 时,底层读操作仍会经过 OS page cache(除非使用 direct I/O 或 kernel settings)。所以磁盘数据可能会同时被 OS cache 与 shared_buffers 缓存,造成内存冗余。
大多数生产系统依赖 OS 缓存 + 适度的 shared_buffers(例如 8GB-32GB 以下机器),也可在特定场景下禁用 OS cache 或使用 huge pages / direct I/O。

常用内存相关查询命令

1
2
3
4
5
6
## 查询关键参数
SHOW shared_buffers;
SHOW work_mem;
SHOW maintenance_work_mem;
SHOW wal_buffers;
SHOW max_connections;
1
2
3
4
5
6
# 查看进程。
ps aux | grep postgres
# 查看内存映射与占用(Linux)。
pmap -x <pid> / cat /proc/<pid>/smaps
# 观察 swap 与内存压力。
vmstat / free -m

缓冲区管理(Buffer)

缓冲区(Buffer)PostgreSQL 用于缓存数据库文件中数据页(data page)的内存区域。
缓冲池(Buffer Pool / Shared Buffers) 是所有缓冲区的集合,它位于 共享内存(Shared Memory) 中,由所有后台进程共同使用。

PostgreSQL 的数据是以固定大小的“页(page)”为单位读写的,默认每页大小是 8KB。
缓冲池的任务就是将磁盘上的这些数据页缓存到内存中,避免频繁的磁盘 I/O。

缓冲区的生命周期

1. 查询访问数据页时:
若页在缓冲池中(称为 命中,Buffer Hit),直接使用。若不在缓冲池中(称为 未命中,Buffer Miss),则从磁盘读取页,若缓冲池已满,则通过替换算法淘汰旧页,将新页载入到缓冲池,更新缓冲描述表。

2. 更新数据时:
修改缓冲区中的页,标记为脏页(dirty),由后台进程(如 Background Writer)异步写回磁盘。

缓冲替代算法

PostgreSQL 使用 Clock-Sweep 算法 来替换缓冲页(类似 LRU,但更高效)。
其思想是:为每个缓冲页设置一个访问标识 usage count(使用计数),内存中的所有页面以链表的形式组成一个环形队列,当某页被访问时,其访问标识usage count就被加1 当扫描指针循环扫过缓冲区时:
如果 usage_count > 0,则减 1;
如果 usage_count = 0,表示该页最近没被使用,可以被替换;
当缓冲区需要装载新页时,从 usage_count=0 的页中选择一个替换。

脏页与写回

当缓冲页被修改后,它与磁盘上的数据就不一致,此时称为脏页
PostgreSQL 不会立即写回磁盘,而是异步地由后台进程处理:

进程 作用
Background Writer 定期将脏页写回磁盘,减少 Checkpoint 压力。
Checkpoint Process 在检查点时强制将所有脏页写回磁盘。
WAL Writer 负责将日志(WAL)写入磁盘,以确保崩溃恢复能力。

这种延迟写机制能够显著的提高性能。

其实pg作为学术派db,其缓冲替代算法是对脏页进行了优化的。

如果替换算法选中了一个脏页作为“牺牲页”,那在装入新页之前必须先把脏页写回磁盘,这会导致 I/O 阻塞,大大降低性能。

于是pg选择在算法层面“优先选择干净页(clean page)”进行替换,从而避免在用户查询路径上发生写盘阻塞。具体来说就是Clock-Sweep 算法在 PostgreSQL 中被改造,使它在扫描缓冲区时遵循以下优先级:优先淘汰 usage_count = 0 且干净(clean)的页

pg_buffercache

pg_buffercachePostgreSQL 自带的一个扩展(在 contrib/ 目录中),它提供一个系统视图 pg_buffercache,用于查看数据库 缓冲池(shared_buffers) 的实时状态(是否脏、是否被使用、属于哪个数据库)。
可以通过以下启用该拓展:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
CREATE EXTENSION pg_buffercache;

安装完成后,会生成一个系统视图:
\d+ pg_buffercache

输出类似:
View "public.pg_buffercache"
Column | Type | Description
--------------+-----------+-------------
bufferid | integer | 缓冲区编号
relfilenode | oid | 物理文件节点
reltablespace| oid | 表空间OID
reldatabase | oid | 所属数据库OID
relforknumber| smallint | 数据文件类型(main, fsm, vm)
relblocknumber| bigint | 页号
isdirty | boolean | 是否为脏页
usagecount | smallint | 使用计数(Clock Sweep算法计数)
pinning_backends | integer | 当前固定该页的进程数

  • Copyrights © 2023-2025 Hexo

请我喝杯咖啡吧~

支付宝
微信