PostgreSQL笔记19:进程与内存架构精解

纲要

  • PostgreSQL 系统架构概览
    • 客户端/服务器(C/S)模型
    • postmaster 守护进程
    • Backend 进程与 fork() 机制
  • 内存架构
    • 共享内存(Shared Memory
      • 固定共享内存(shared_bufferswal_buffers 等)
      • 动态共享内存(Dynamic Shared MemoryDSM
    • 本地内存(Local Memory
      • work_mem
      • maintenance_work_mem
      • temp_buffers
  • 后台进程
    • Checkpointer(检查点进程)
    • Background Writer(后台写进程)
    • WAL Writer(预写日志写进程)
    • Autovacuum Launcher / Autovacuum Worker(自动清理)
    • Stats Collector(统计信息收集器,PostgreSQL 15 起移除)
    • WAL Sender / WAL Receiver(物理复制)
    • Logger(日志收集器)
    • Archiver(归档进程)
  • 进程查看与验证(ps 命令)

系统架构概览

PostgreSQL 采用经典的 客户端/服务器(C/S)模型 。客户端可以是 psql 命令行工具、JDBC 驱动程序、ODBC 驱动程序,以及 DBeaver、pgAdmin 等图形化管理工具。服务端则以 postmaster 为核心枢纽,统一管理连接请求、共享内存分配和后台进程的启停。
#mermaid-svg-xAvmjmofun8bqvIs{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-xAvmjmofun8bqvIs .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-xAvmjmofun8bqvIs .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-xAvmjmofun8bqvIs .error-icon{fill:#552222;}#mermaid-svg-xAvmjmofun8bqvIs .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-xAvmjmofun8bqvIs .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-xAvmjmofun8bqvIs .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-xAvmjmofun8bqvIs .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-xAvmjmofun8bqvIs .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-xAvmjmofun8bqvIs .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-xAvmjmofun8bqvIs .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-xAvmjmofun8bqvIs .marker{fill:#333333;stroke:#333333;}#mermaid-svg-xAvmjmofun8bqvIs .marker.cross{stroke:#333333;}#mermaid-svg-xAvmjmofun8bqvIs svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-xAvmjmofun8bqvIs p{margin:0;}#mermaid-svg-xAvmjmofun8bqvIs .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-xAvmjmofun8bqvIs .cluster-label text{fill:#333;}#mermaid-svg-xAvmjmofun8bqvIs .cluster-label span{color:#333;}#mermaid-svg-xAvmjmofun8bqvIs .cluster-label span p{background-color:transparent;}#mermaid-svg-xAvmjmofun8bqvIs .label text,#mermaid-svg-xAvmjmofun8bqvIs span{fill:#333;color:#333;}#mermaid-svg-xAvmjmofun8bqvIs .node rect,#mermaid-svg-xAvmjmofun8bqvIs .node circle,#mermaid-svg-xAvmjmofun8bqvIs .node ellipse,#mermaid-svg-xAvmjmofun8bqvIs .node polygon,#mermaid-svg-xAvmjmofun8bqvIs .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-xAvmjmofun8bqvIs .rough-node .label text,#mermaid-svg-xAvmjmofun8bqvIs .node .label text,#mermaid-svg-xAvmjmofun8bqvIs .image-shape .label,#mermaid-svg-xAvmjmofun8bqvIs .icon-shape .label{text-anchor:middle;}#mermaid-svg-xAvmjmofun8bqvIs .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-xAvmjmofun8bqvIs .rough-node .label,#mermaid-svg-xAvmjmofun8bqvIs .node .label,#mermaid-svg-xAvmjmofun8bqvIs .image-shape .label,#mermaid-svg-xAvmjmofun8bqvIs .icon-shape .label{text-align:center;}#mermaid-svg-xAvmjmofun8bqvIs .node.clickable{cursor:pointer;}#mermaid-svg-xAvmjmofun8bqvIs .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-xAvmjmofun8bqvIs .arrowheadPath{fill:#333333;}#mermaid-svg-xAvmjmofun8bqvIs .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-xAvmjmofun8bqvIs .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-xAvmjmofun8bqvIs .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-xAvmjmofun8bqvIs .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-xAvmjmofun8bqvIs .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-xAvmjmofun8bqvIs .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-xAvmjmofun8bqvIs .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-xAvmjmofun8bqvIs .cluster text{fill:#333;}#mermaid-svg-xAvmjmofun8bqvIs .cluster span{color:#333;}#mermaid-svg-xAvmjmofun8bqvIs div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-xAvmjmofun8bqvIs .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-xAvmjmofun8bqvIs rect.text{fill:none;stroke-width:0;}#mermaid-svg-xAvmjmofun8bqvIs .icon-shape,#mermaid-svg-xAvmjmofun8bqvIs .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-xAvmjmofun8bqvIs .icon-shape p,#mermaid-svg-xAvmjmofun8bqvIs .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-xAvmjmofun8bqvIs .icon-shape .label rect,#mermaid-svg-xAvmjmofun8bqvIs .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-xAvmjmofun8bqvIs .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-xAvmjmofun8bqvIs .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-xAvmjmofun8bqvIs :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 服务端
fork
fork
fork
连接请求
刷盘
存储层
PGDATA 数据目录
base/
pg_wal/
pg_log/
共享内存区域
shared_buffers
wal_buffers
锁/信号量
动态共享内存
后台进程
Checkpointer
Background Writer
WAL Writer
Autovacuum Launcher
Stats Collector

PG 15 起移除
客户端层
psql
JDBC/ODBC
DBeaver/pgAdmin
postmaster 守护进程
Backend 进程 1
Backend 进程 2
Backend 进程 N

PostgreSQL 与 MySQL 的核心区别之一在于进程模型 :PostgreSQL 采用多进程架构 ,每个客户端连接对应一个独立的 Backend 进程;而 MySQL 采用多线程模型。

postmaster:一切进程的父进程

postmaster 是 PostgreSQL 实例启动时最先运行的进程。其主要职责包括:

  1. 加载 postgresql.conf 等配置文件
  2. 分配共享内存区域
  3. 启动各类后台辅助进程
  4. 在指定 TCP/IP 端口监听客户端连接请求

Backend 进程:每个连接一个进程

当客户端发起连接请求时,postmaster 首先通过 pg_hba.conf 进行认证检查,认证通过后调用 fork() 系统调用创建一个新的子进程------即 Backend 进程。此后,该 Backend 进程专门负责与对应客户端的所有通信和 SQL 执行,postmaster 不再参与其中。
共享内存 Backend 进程 postmaster 客户端 共享内存 Backend 进程 postmaster 客户端 #mermaid-svg-FWW2gCSWaN00RLAD{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-FWW2gCSWaN00RLAD .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-FWW2gCSWaN00RLAD .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-FWW2gCSWaN00RLAD .error-icon{fill:#552222;}#mermaid-svg-FWW2gCSWaN00RLAD .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-FWW2gCSWaN00RLAD .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-FWW2gCSWaN00RLAD .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-FWW2gCSWaN00RLAD .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-FWW2gCSWaN00RLAD .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-FWW2gCSWaN00RLAD .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-FWW2gCSWaN00RLAD .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-FWW2gCSWaN00RLAD .marker{fill:#333333;stroke:#333333;}#mermaid-svg-FWW2gCSWaN00RLAD .marker.cross{stroke:#333333;}#mermaid-svg-FWW2gCSWaN00RLAD svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-FWW2gCSWaN00RLAD p{margin:0;}#mermaid-svg-FWW2gCSWaN00RLAD .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-FWW2gCSWaN00RLAD text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-FWW2gCSWaN00RLAD .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-FWW2gCSWaN00RLAD .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-FWW2gCSWaN00RLAD .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-FWW2gCSWaN00RLAD .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-FWW2gCSWaN00RLAD #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-FWW2gCSWaN00RLAD .sequenceNumber{fill:white;}#mermaid-svg-FWW2gCSWaN00RLAD #sequencenumber{fill:#333;}#mermaid-svg-FWW2gCSWaN00RLAD #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-FWW2gCSWaN00RLAD .messageText{fill:#333;stroke:none;}#mermaid-svg-FWW2gCSWaN00RLAD .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-FWW2gCSWaN00RLAD .labelText,#mermaid-svg-FWW2gCSWaN00RLAD .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-FWW2gCSWaN00RLAD .loopText,#mermaid-svg-FWW2gCSWaN00RLAD .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-FWW2gCSWaN00RLAD .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-FWW2gCSWaN00RLAD .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-FWW2gCSWaN00RLAD .noteText,#mermaid-svg-FWW2gCSWaN00RLAD .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-FWW2gCSWaN00RLAD .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-FWW2gCSWaN00RLAD .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-FWW2gCSWaN00RLAD .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-FWW2gCSWaN00RLAD .actorPopupMenu{position:absolute;}#mermaid-svg-FWW2gCSWaN00RLAD .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-FWW2gCSWaN00RLAD .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-FWW2gCSWaN00RLAD .actor-man circle,#mermaid-svg-FWW2gCSWaN00RLAD line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-FWW2gCSWaN00RLAD :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 连接请求 pg_hba.conf 认证 fork() 创建子进程 连接成功,转交 BE 发送 SQL 查询 读取/写入数据 返回结果集

内存架构

PostgreSQL 的内存分为两大类别:共享内存(Shared Memory)本地内存(Local Memory)

共享内存(Shared Memory)

共享内存是 PostgreSQL 实例中所有进程共同访问的内存区域,是进程间通信(IPC)的核心载体。共享内存可进一步细分为两种类型:

固定共享内存

在实例启动时,postmaster 根据配置参数一次性分配固定大小的共享内存。主要组成部分包括:

组件 配置参数 用途
共享缓冲区 shared_buffers 缓存表和索引的数据页
WAL 缓冲区 wal_buffers 暂存 WAL 日志记录
锁空间 --- 轻量级锁、共享锁、排他锁等
元数据缓存 --- 系统表和对象元数据

shared_buffers 是 PostgreSQL 最核心的内存区域,相当于数据库的"二级缓存"。当执行查询需要读取数据时,Backend 进程会优先从 shared_buffers 中查找;若缓存未命中,再从磁盘读取并放入缓冲区。shared_buffers 的推荐配置为系统物理内存的 25% 左右

sql 复制代码
-- 查看当前 shared_buffers 配置
SHOW shared_buffers;

-- 查看当前 wal_buffers 配置
SHOW wal_buffers;

-- 动态修改 shared_buffers(需重启)
ALTER SYSTEM SET shared_buffers = '4GB';
动态共享内存(Dynamic Shared Memory, DSM)

动态共享内存是 PostgreSQL 9.6 版本引入的特性,用于支持并行查询(Parallel Query)。并行查询允许一个 SQL 语句利用多个 CPU 核心协同计算------即"人多力量大"。

并行查询执行时,leader 进程会动态创建 DSM 段,并启动多个 worker 进程。这些 worker 进程通过 DSM 进行通信和元组传递,查询执行完毕后 DSM 随即释放。

sql 复制代码
-- 启用并行查询(默认开启)
SET max_parallel_workers_per_gather = 4;

-- 查看并行查询相关的 DSM 使用情况
SELECT * FROM pg_stat_activity WHERE backend_type = 'parallel worker';

本地内存(Local Memory)

每个 Backend 进程都拥有独立的本地内存区域,用于存放该会话执行 SQL 时的中间结果和临时数据结构。本地内存不与其他进程共享。主要参数包括:

参数 默认值 用途
work_mem 4MB 排序(ORDER BYDISTINCT)、哈希连接(hash join)、聚合操作的内存
maintenance_work_mem 64MB VACUUMCREATE INDEXREINDEX 等维护操作的内存
temp_buffers 8MB 临时表的缓冲区

需要注意的是,work_mem每个操作 的内存上限,而非每个会话。一个复杂查询可能包含多个排序或哈希操作,每个操作都会独立申请 work_mem 内存。

sql 复制代码
-- 会话级调整 work_mem(仅对当前会话生效)
SET work_mem = '64MB';

-- 查看当前会话的 work_mem
SHOW work_mem;

-- 查看 maintenance_work_mem
SHOW maintenance_work_mem;

后台进程

PostgreSQL 在启动时会自动启动一组后台进程,各司其职,协同完成数据库的日常维护工作。

Checkpointer(检查点进程)

检查点(Checkpoint) 是确保数据库一致性的关键机制。Checkpointer 进程定期将 shared_buffers 中的所有脏页(Dirty Pages)写入磁盘。检查点完成后,意味着数据库在该时间点达到了一致性状态------所有已提交事务的修改均已持久化到磁盘。
#mermaid-svg-zLIHv6g0mSwY6App{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-zLIHv6g0mSwY6App .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-zLIHv6g0mSwY6App .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-zLIHv6g0mSwY6App .error-icon{fill:#552222;}#mermaid-svg-zLIHv6g0mSwY6App .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-zLIHv6g0mSwY6App .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-zLIHv6g0mSwY6App .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-zLIHv6g0mSwY6App .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-zLIHv6g0mSwY6App .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-zLIHv6g0mSwY6App .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-zLIHv6g0mSwY6App .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-zLIHv6g0mSwY6App .marker{fill:#333333;stroke:#333333;}#mermaid-svg-zLIHv6g0mSwY6App .marker.cross{stroke:#333333;}#mermaid-svg-zLIHv6g0mSwY6App svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-zLIHv6g0mSwY6App p{margin:0;}#mermaid-svg-zLIHv6g0mSwY6App .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-zLIHv6g0mSwY6App .cluster-label text{fill:#333;}#mermaid-svg-zLIHv6g0mSwY6App .cluster-label span{color:#333;}#mermaid-svg-zLIHv6g0mSwY6App .cluster-label span p{background-color:transparent;}#mermaid-svg-zLIHv6g0mSwY6App .label text,#mermaid-svg-zLIHv6g0mSwY6App span{fill:#333;color:#333;}#mermaid-svg-zLIHv6g0mSwY6App .node rect,#mermaid-svg-zLIHv6g0mSwY6App .node circle,#mermaid-svg-zLIHv6g0mSwY6App .node ellipse,#mermaid-svg-zLIHv6g0mSwY6App .node polygon,#mermaid-svg-zLIHv6g0mSwY6App .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-zLIHv6g0mSwY6App .rough-node .label text,#mermaid-svg-zLIHv6g0mSwY6App .node .label text,#mermaid-svg-zLIHv6g0mSwY6App .image-shape .label,#mermaid-svg-zLIHv6g0mSwY6App .icon-shape .label{text-anchor:middle;}#mermaid-svg-zLIHv6g0mSwY6App .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-zLIHv6g0mSwY6App .rough-node .label,#mermaid-svg-zLIHv6g0mSwY6App .node .label,#mermaid-svg-zLIHv6g0mSwY6App .image-shape .label,#mermaid-svg-zLIHv6g0mSwY6App .icon-shape .label{text-align:center;}#mermaid-svg-zLIHv6g0mSwY6App .node.clickable{cursor:pointer;}#mermaid-svg-zLIHv6g0mSwY6App .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-zLIHv6g0mSwY6App .arrowheadPath{fill:#333333;}#mermaid-svg-zLIHv6g0mSwY6App .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-zLIHv6g0mSwY6App .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-zLIHv6g0mSwY6App .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-zLIHv6g0mSwY6App .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-zLIHv6g0mSwY6App .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-zLIHv6g0mSwY6App .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-zLIHv6g0mSwY6App .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-zLIHv6g0mSwY6App .cluster text{fill:#333;}#mermaid-svg-zLIHv6g0mSwY6App .cluster span{color:#333;}#mermaid-svg-zLIHv6g0mSwY6App div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-zLIHv6g0mSwY6App .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-zLIHv6g0mSwY6App rect.text{fill:none;stroke-width:0;}#mermaid-svg-zLIHv6g0mSwY6App .icon-shape,#mermaid-svg-zLIHv6g0mSwY6App .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-zLIHv6g0mSwY6App .icon-shape p,#mermaid-svg-zLIHv6g0mSwY6App .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-zLIHv6g0mSwY6App .icon-shape .label rect,#mermaid-svg-zLIHv6g0mSwY6App .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-zLIHv6g0mSwY6App .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-zLIHv6g0mSwY6App .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-zLIHv6g0mSwY6App :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 检查点触发
定时触发

checkpoint_timeout
Checkpointer
WAL 段累积

max_wal_size
扫描 shared_buffers
将所有脏页刷盘
更新 pg_control

标记检查点完成

sql 复制代码
-- 查看检查点相关参数
SHOW checkpoint_timeout;   -- 默认 5min
SHOW max_wal_size;         -- 默认 1GB
SHOW checkpoint_completion_target; -- 默认 0.9

-- 手动触发检查点
CHECKPOINT;

Background Writer(后台写进程)

Background WriterCheckpointer 的"好帮手"。它的职责是持续地、小批量地shared_buffers 中的脏页写入磁盘,而不是等到检查点时一次性全部刷盘。

这种设计可以有效分散 I/O 压力,避免检查点期间产生大量的 I/O 突发,从而降低对业务高峰期的性能冲击。

sql 复制代码
-- 查看 Background Writer 相关参数
SHOW bgwriter_delay;           -- 默认 200ms
SHOW bgwriter_lru_maxpages;    -- 默认 100
SHOW bgwriter_lru_multiplier;  -- 默认 2.0

-- 查看 Background Writer 统计信息
SELECT * FROM pg_stat_bgwriter;

WAL Writer(预写日志写进程)

WAL Writer 负责将 wal_buffers 中的 WAL 日志记录写入磁盘上的 WAL 文件。WAL(Write-Ahead Logging,预写日志)是 PostgreSQL 实现事务持久性和崩溃恢复的核心机制。

sql 复制代码
-- 查看 WAL 相关参数
SHOW wal_buffers;          -- 默认 -1(自动调整)
SHOW wal_writer_delay;     -- 默认 200ms
SHOW wal_writer_flush_after; -- 默认 1MB

Autovacuum Launcher / Worker(自动清理)

PostgreSQL 的 MVCC(多版本并发控制)机制中,UPDATEDELETE 操作会在表中留下"死元组"(Dead Tuples)------类比房间里的外卖垃圾。Autovacuum 机制负责自动清理这些死元组并回收存储空间。

Autovacuum Launcher 是调度进程,它会定期检查哪些表需要清理,并启动 Autovacuum Worker 进程执行实际的清理工作。

此外,Autovacuum 还会更新表的统计信息(如表的行数、不同值的分布等),为查询优化器提供决策依据。

sql 复制代码
-- 查看 Autovacuum 相关参数
SHOW autovacuum;                -- on/off
SHOW autovacuum_vacuum_threshold; -- 默认 50
SHOW autovacuum_analyze_threshold; -- 默认 50

-- 手动执行 VACUUM(不回收空间给操作系统)
VACUUM my_table;

-- 手动执行 VACUUM FULL(回收空间,但会锁表)
VACUUM FULL my_table;

-- 手动更新统计信息
ANALYZE my_table;

Stats Collector(统计信息收集器)------ PostgreSQL 15 起移除

在 PostgreSQL 14 及更早版本中,Stats Collector 是一个独立的后台进程,负责收集全局统计信息(如表的行数、索引使用频率、值分布等),并通过临时文件(pg_stat_tmp 目录)进行持久化。

PostgreSQL 15 的重大变更 :官方社区移除了 Stats Collector 进程,改用动态共享内存(DSM) 直接存储统计信息。

这一变更带来的改进包括:

  • 消除额外进程:不再需要独立的统计收集进程
  • 减少 I/O 开销:统计信息不再频繁写入临时文件
  • 更低延迟:统计信息实时写入共享内存,优化器可获得更及时的数据
sql 复制代码
-- PostgreSQL 15+ 查看统计信息的系统视图(用法不变)
SELECT * FROM pg_stat_user_tables WHERE schemaname = 'public';
SELECT * FROM pg_stat_indexes WHERE schemaname = 'public';
SELECT * FROM pg_stat_activity;

WAL Sender / WAL Receiver(复制相关)

  • WAL Sender:在主库(发布端)按需创建,负责将 WAL 日志流式发送给备库
  • WAL Receiver:在备库(订阅端)运行,负责接收来自主库的 WAL 日志流

这两个进程是 PostgreSQL 实现物理复制高可用的核心组件。

sql 复制代码
-- 查看复制状态
SELECT * FROM pg_stat_replication;
SELECT * FROM pg_replication_slots;

其他后台进程

进程 用途
Logger 将数据库运行日志写入 pg_log 目录
Archiver 将 WAL 日志归档到指定目录
Logical Replication Launcher 管理逻辑复制的 worker 进程

进程验证:使用 ps 命令查看

在 Linux/Unix 系统中,可以使用 ps 命令查看 PostgreSQL 的进程树:

bash 复制代码
# 树形显示 PostgreSQL 进程
ps -ef | grep postgres

# 或使用 axjf 查看进程树
ps axjf | grep -E 'postgres|grep'

# 查看所有 PostgreSQL 相关进程
ps -eo pid,ppid,cmd | grep postgres | grep -v grep

典型的输出示例(PostgreSQL 14):

dir 复制代码
postmaster (PID 3416)
├── checkpointer
├── background writer
├── walwriter
├── autovacuum launcher
├── stats collector
├── logical replication launcher
├── postgres: backend process (client connection 1)
├── postgres: backend process (client connection 2)
└── ...

在 PostgreSQL 15+ 中,stats collector 进程将不再出现。

API 速览

配置参数查询

sql 复制代码
-- 查询单个参数
SHOW shared_buffers;

-- 查询所有参数
SELECT name, setting, unit, context FROM pg_settings WHERE name LIKE '%memory%';

动态修改配置(无需重启)

sql 复制代码
-- 修改 work_mem(会话级)
SET work_mem = '128MB';

-- 修改 work_mem(全局,需重载配置)
ALTER SYSTEM SET work_mem = '128MB';
SELECT pg_reload_conf();

手动触发维护操作

sql 复制代码
-- 手动检查点
CHECKPOINT;

-- 手动清理(不回收空间)
VACUUM my_table;

-- 手动清理并更新统计信息
VACUUM ANALYZE my_table;

-- 手动更新统计信息
ANALYZE my_table;

统计信息查询

sql 复制代码
-- 查看表级统计信息
SELECT * FROM pg_stat_user_tables WHERE schemaname = 'public';

-- 查看索引使用统计
SELECT * FROM pg_stat_indexes WHERE schemaname = 'public';

-- 查看后台进程统计
SELECT * FROM pg_stat_bgwriter;

-- 查看当前活动会话
SELECT pid, usename, application_name, client_addr, state, query 
FROM pg_stat_activity 
WHERE state = 'active';

Demo 简单示例

以下是一个使用 Node.js + pg 库的完整示例,演示了 PostgreSQL 连接、查询执行以及统计信息查看。

运行说明

  1. 确保本地已安装 PostgreSQL 并启动服务
  2. 创建一个测试数据库和测试表
  3. 安装依赖:npm install pg
  4. 运行脚本:node demo.js

代码示例

js 复制代码
const { Client } = require('pg');

// PostgreSQL 连接配置
const config = {
  host: 'localhost',
  port: 5432,
  database: 'postgres',
  user: 'postgres',
  password: 'your_password',
};

async function runDemo() {
  const client = new Client(config);

  try {
    // 1. 建立连接 ------ 对应 postmaster 创建 Backend 进程
    await client.connect();
    console.log('✅ 连接成功,Backend 进程已创建');

    // 2. 创建测试表
    await client.query(`
      DROP TABLE IF EXISTS test_users;
      CREATE TABLE test_users (
        id SERIAL PRIMARY KEY,
        name TEXT,
        age INTEGER,
        created_at TIMESTAMP DEFAULT NOW()
      );
    `);
    console.log('✅ 测试表创建成功');

    // 3. 插入测试数据
    await client.query(`
      INSERT INTO test_users (name, age)
      SELECT 
        'User_' || generate_series,
        floor(random() * 50 + 20)
      FROM generate_series(1, 10000);
    `);
    console.log('✅ 插入 10000 条测试数据');

    // 4. 执行查询 ------ Backend 进程从 shared_buffers 读取数据
    const res = await client.query(`
      SELECT age, COUNT(*) 
      FROM test_users 
      GROUP BY age 
      ORDER BY age;
    `);
    console.log(`✅ 查询完成,返回 ${res.rowCount} 行`);

    // 5. 查看当前会话的 work_mem 设置
    const workMemRes = await client.query('SHOW work_mem;');
    console.log(`📊 当前 work_mem: ${workMemRes.rows[0].work_mem}`);

    // 6. 查看 shared_buffers 设置
    const sharedBuffersRes = await client.query('SHOW shared_buffers;');
    console.log(`📊 当前 shared_buffers: ${sharedBuffersRes.rows[0].shared_buffers}`);

    // 7. 查看表统计信息(由 Stats Collector / 共享内存提供)
    const statsRes = await client.query(`
      SELECT 
        schemaname,
        tablename,
        n_live_tup,
        n_dead_tup,
        last_vacuum,
        last_analyze
      FROM pg_stat_user_tables 
      WHERE tablename = 'test_users';
    `);
    console.log('📊 表统计信息:', JSON.stringify(statsRes.rows[0], null, 2));

    // 8. 查看后台进程统计
    const bgwriterRes = await client.query(`
      SELECT 
        checkpoints_timed,
        checkpoints_req,
        buffers_checkpoint,
        buffers_clean,
        buffers_backend
      FROM pg_stat_bgwriter;
    `);
    console.log('📊 后台写进程统计:', JSON.stringify(bgwriterRes.rows[0], null, 2));

    // 9. 查看当前活动会话(包含本会话的 Backend 进程)
    const activityRes = await client.query(`
      SELECT pid, usename, application_name, state, backend_type
      FROM pg_stat_activity 
      WHERE pid != pg_backend_pid();
    `);
    console.log(`📊 当前其他活动会话数: ${activityRes.rowCount}`);

    // 10. 清理测试数据
    await client.query('DROP TABLE test_users;');
    console.log('✅ 测试表已清理');

  } catch (err) {
    console.error('❌ 错误:', err.message);
  } finally {
    await client.end();
    console.log('✅ 连接已关闭,Backend 进程终止');
  }
}

runDemo();

代码说明

步骤 对应技术点
连接建立 postmaster 监听端口 → fork() 创建 Backend 进程
数据插入 数据写入 shared_buffers,WAL 记录写入 wal_buffers
查询执行 Backend 进程从 shared_buffers 读取缓存页
SHOW work_mem 查看本地内存参数(会话级)
pg_stat_user_tables 查看统计信息(PG 15 前来自 Stats Collector,PG 15+ 来自共享内存)
pg_stat_bgwriter 查看 Background WriterCheckpointer 的工作统计
pg_stat_activity 查看所有 Backend 进程和后台进程的状态

对应的 PostgreSQL 原生指令

sql 复制代码
-- 连接数据库
psql -h localhost -p 5432 -U postgres -d postgres

-- 创建测试表
CREATE TABLE test_users (id SERIAL PRIMARY KEY, name TEXT, age INTEGER);

-- 插入测试数据
INSERT INTO test_users (name, age) SELECT 'User_' || generate_series, floor(random() * 50 + 20) FROM generate_series(1, 10000);

-- 执行查询
SELECT age, COUNT(*) FROM test_users GROUP BY age ORDER BY age;

-- 查看参数
SHOW work_mem;
SHOW shared_buffers;

-- 查看统计信息
SELECT schemaname, tablename, n_live_tup, n_dead_tup FROM pg_stat_user_tables WHERE tablename = 'test_users';

-- 查看后台写进程统计
SELECT * FROM pg_stat_bgwriter;

-- 查看活动会话
SELECT pid, usename, state, backend_type FROM pg_stat_activity;

-- 清理
DROP TABLE test_users;

技术点总结

  • 进程模型 :每个客户端连接对应一个独立的 Backend 进程
  • 共享内存shared_buffers 缓存数据页,wal_buffers 缓存 WAL 日志
  • 本地内存work_mem 控制排序/哈希操作的内存上限
  • 后台进程CheckpointerBackground WriterWAL WriterAutovacuum 等协同工作
  • 统计信息 :PG 15 前由 Stats Collector 进程收集,PG 15+ 改用共享内存

项目难点与解决方案

核心难点

  • 多进程通信的复杂性 :PostgreSQL 的多进程架构需要依赖共享内存和信号量实现进程间通信,对操作系统的 IPC 配置(如 shmmaxshmall)有较高要求
  • 检查点 I/O 冲击Checkpointer 一次性刷盘大量脏页会导致 I/O 突发,影响在线业务的响应时间
  • MVCC 带来的垃圾膨胀 :频繁的 UPDATE/DELETE 会产生大量死元组,若不及时清理会导致表膨胀和性能退化

解决方案

  • Background Writer 分摊 I/O 压力 :通过 bgwriter_delaybgwriter_lru_maxpages 参数控制刷盘节奏,将集中式 I/O 转化为分散式 I/O
  • Checkpoint 调优 :通过 checkpoint_completion_target 参数(默认 0.9)延长检查点的完成窗口,平滑 I/O 负载
  • Autovacuum 精细化管理 :根据表的大小和更新频率,为不同表设置不同的 autovacuum_vacuum_scale_factorautovacuum_analyze_scale_factor,避免全表扫描带来的性能开销

广度

涵盖 PostgreSQL 从客户端连接、进程模型、内存架构到后台进程的完整体系,涉及 10+ 种核心进程和 6+ 个关键内存参数。

深度

深入剖析了共享内存的两种类型(固定 vs 动态)、CheckpointerBackground Writer 的协作机制、以及 PostgreSQL 15 移除 Stats Collector 的架构演进原因。

复杂度

涉及操作系统层面的进程管理(fork())、IPC 通信(共享内存/信号量)、数据库内核层面的缓冲区管理、WAL 机制和 MVCC 垃圾回收,属于 PostgreSQL 中级到高级的系统架构知识。

官方文档

参考链接

总结

本文系统梳理了 PostgreSQL 的进程与内存架构。PostgreSQL 采用多进程 C/S 模型 ,以 postmaster 为守护进程,通过 fork() 为每个客户端连接创建独立的 Backend 进程。

内存方面分为共享内存 (包含固定大小的 shared_buffers/wal_buffers 和动态共享内存 DSM)和本地内存work_memmaintenance_work_memtemp_buffers)。后台进程各司其职:Checkpointer 保障数据一致性、Background Writer 平滑 I/O 压力、WAL Writer 确保事务持久性、Autovacuum 清理 MVCC 垃圾并更新统计信息。

PostgreSQL 15 起移除了 Stats Collector 进程,改用共享内存存储统计信息,减少了 I/O 开销和进程间通信。理解这些底层机制,是进行性能调优、故障排查和架构设计的基础。

相关推荐
fasdgfgsdha56942 小时前
西乡塘代理记账哪家好
笔记
hz567893 小时前
好视通视频会议解决方案:需求分析、平台架构与价值实现(2026 完整版)
架构·音视频·实时音视频·需求分析·信息与通信
学计算机的计算基3 小时前
TCP 传输层硬核整理:三次握手、四次挥手、拥塞控制一次讲透
java·网络·笔记·网络协议·算法
heimeiyingwang3 小时前
【架构实战】消息队列选型与异步架构设计:从Kafka到RabbitMQ,一次聊透
架构·kafka·rabbitmq
Wang's Blog4 小时前
PostgreSQL笔记20: 实例、集群、模板数据库与表空间 —— 逻辑体系架构深度解析
数据库·笔记·postgresql
wp123_112 小时前
硬件元器件笔记|IPX8 防水 Type‑C 母座安费诺 124018802112A 与 TONEVEE TY48086‑24A 分析
c语言·开发语言·笔记
mifengxing13 小时前
操作系统 文件系统
笔记·操作系统·计算机408
会周易的程序员13 小时前
aiDgePLC iec61131 虚拟机 完整使用文档
c++·物联网·架构·st·iec61131
无聊的菜鸟14 小时前
TMS320F2806X学习笔记(零)—— C2000、CCS、新建工程
笔记·mcu·c2000·f2806x