数据库教程FGMT50‑PostgreSQL体系结构深入与源码解析
前言
理解 PostgreSQL 内部体系架构是 DBA 进行故障诊断、性能调优、问题深度排查的底层根基。只有理清逻辑存储、物理文件布局、进程模型、内存工作机制,遇到慢查询、锁等待、WAL 异常、实例启动失败等问题时,才不会停留在表面现象。风哥教程本文以两台实验主机fgedu‑net‑cn1、fgedu‑net‑cn2开展实操,硬件规格统一 8CPU、64GB 内存,全部数据路径统一为/fgedudb;实例名、业务数据库名、业务账号统一使用fgedudb、fgedudb、fgedu。
风哥教程本文包含 PostgreSQL 逻辑结构、物理存储目录、进程架构、内存模型、源码编译调试环境搭建、initdb 初始化流程跟踪、数据库启动流程 gdb 调试、源代码目录解读。面向数据库 DBA、运维工程师、数据库架构师、大数据工程师;学习完成后,能够看懂 PG 底层运行机制,具备源码级问题定位能力。
本文内容大纲
- PostgreSQL 整体体系架构总览
- PostgreSQL 逻辑存储结构理论与实操
- PostgreSQL 物理存储目录、页结构、表空间理论与实操
- PostgreSQL 进程架构:主进程、后台辅助进程、backend 会话进程
- PostgreSQL 内存结构理论,64G/8C 硬件参数配置实操
- PostgreSQL 源码文件目录结构与阅读工具
- 源码编译搭建调试环境完整实战
- gdb 调试工具跟踪 initdb 初始化全过程
- gdb 跟踪 PostgreSQL 实例启动流程
- 底层问题分析思路、运维落地建议
- 风哥针对本文总结
一、PostgreSQL 整体体系架构总览
PostgreSQL 采用多进程模型,不使用多线程。每一个客户端连接对应独立 backend 后端进程,进程之间通过共享内存、信号量、轻量锁完成通信;整个实例分为实例集群(cluster),一个集群下包含多个 database 数据库,数据库之下包含 schema,schema 存放表、索引、视图、函数等对象PostgreSQL。
整体可以划分为四大模块:逻辑存储模块、物理文件存储模块、进程后台模块、内存共享模块。
- 逻辑层:集群 cluster → database → schema → table/index/view/function
- 物理层:PGDATA 数据目录,base、global、pg_wal、pg_xact、表空间目录,8KB 默认数据页 page 作为最小 IO 单元。
- 进程层:postmaster 主守护进程、backend 会话进程、checkpoint、bgwriter、walwriter、autovacuum、archiver 等后台辅助进程。
- 内存层:共享内存(shared_buffers、wal_buffers 等)、每个 backend 私有本地内存(work_mem、maintenance_work_mem)。
风哥 itpux‑com
集群 cluster 概念非常关键:一套 PGDATA 目录代表一个数据库集群实例,一个集群内部可以创建多个 database 数据库,database 之间物理隔离,不能跨库直接访问表,只能通过 dblink/fdw 访问。很多初学者混淆集群、实例、数据库三者概念,会造成部署、备份、迁移理解偏差PostgreSQL。
实验环境准备,两台主机fgedu‑net‑cn1、fgedu‑net‑cn2,root 账号执行,创建基础目录,后续源码编译、数据库数据全部放在/fgedudb路径下。
#操作系统基础目录创建
mkdir -p /fgedudb/pg_soft
mkdir -p /fgedudb/pg_data/fgedudb
mkdir -p /fgedudb/pg_tablespace
mkdir -p /fgedudb/pg_log
#创建postgres操作系统用户
groupadd postgres
useradd -r -g postgres -s /bin/bash postgres
chown -R postgres:postgres /fgedudb
chmod 700 /fgedudb/pg_data/fgedudb
ls -ld /fgedudb/*
安装基础依赖包,用于后续编译源码、gdb 调试:
yum install -y gcc gcc‑c++ readline‑devel zlib‑devel bison flex libxml2‑devel gdb make cmake
二、PostgreSQL 逻辑存储结构理论与实操
逻辑层级自上而下:数据库集群 cluster → Database 数据库 → Schema 模式 → 对象(表、索引、序列、视图、函数、类型)。
- Cluster 集群:由 initdb 初始化生成,一套 PGDATA,一套 postmaster 进程;集群全局对象:角色用户、表空间、pg_database,保存在 global 子目录,不属于任何单一 database。
- Database:集群内部独立数据库,不同 database 之间 SQL 不能直接跨库 select;template0、template1 为系统模板库,template1 作为新建数据库模板,template0 为干净只读模板,禁止修改PostgreSQL。
- Schema:数据库之下的命名空间,一个数据库允许多个 schema;public 是默认 schema;可以做权限隔离,同一个库下不同业务放不同 schema。
- 对象:表 table、索引 index、序列 sequence、视图 view、存储过程 function、自定义 type。
风哥教程 113257174
实操:逻辑对象创建,主机 fgedu‑net‑cn1
切换 postgres 操作系统用户,后续 initdb 初始化集群:
su - postgres
#设置环境变量
export PGDATA=/fgedudb/pg_data/fgedudb
export PATH=/fgedudb/pg_soft/pg_bin/bin:$PATH
执行 initdb 初始化数据库集群:
initdb -D $PGDATA --encoding=UTF8 --locale=en_US.UTF‑8 --data‑checksums
--data‑checksums开启数据页校验和,生产强烈建议开启,初始化之后无法关闭。
启动数据库集群:
pg_ctl start -D $PGDATA -l /fgedudb/pg_log/fgedudb.log
登录 psql,实操逻辑对象:
psql -p 5432 -U postgres postgres
--查看集群下全部数据库
\l
--创建业务数据库 fgedudb
CREATE DATABASE fgedudb ENCODING 'UTF8' LC_COLLATE 'en_US.UTF‑8' LC_CTYPE 'en_US.UTF‑8' TEMPLATE template0;
\c fgedudb
--创建业务schema fgedu
CREATE SCHEMA fgedu;
SET search_path TO fgedu,public;
--创建业务测试表
CREATE TABLE t_biz_data (
id bigserial primary key,
biz_code text,
create_time timestamp without time zone default now()
);
INSERT INTO t_biz_data(biz_code) VALUES('biz‑001'),('biz‑002');
--查看schema下对象
\dt
\dn
\q
关键知识点:
- 跨 database 不能直接 select,必须使用 fdw 或者 dblink 组件;
- template0 只读,新建数据库优先 template0,避免 template1 有残留自定义对象带入新库;
- search_path 参数控制 schema 搜索顺序,业务开发经常踩坑。
网上搜索风哥教程可以学习全套数据库教程
三、PostgreSQL 物理存储目录、页结构、表空间理论与实操
PGDATA 目录就是数据库集群物理根目录,核心一级子目录含义PostgreSQL:
表格
| 目录 / 文件 | 说明 |
|---|---|
| PG_VERSION | 数据库大版本标记文件 |
| base | 各个 database 的数据目录,子目录为数据库 oid,存放表、索引数据文件 |
| global | 集群全局系统表,pg_database、pg_roles 等集群全局元数据 |
| pg_wal | WAL 预写日志,事务重做日志,PITR 恢复、流复制依赖,禁止随意删除 |
| pg_xact | 事务提交状态 clog,记录每个事务 commit/abort 状态 |
| pg_multixact | 多事务行锁状态 |
| pg_tblspc | 表空间软链接,指向外部表空间真实目录 |
每一张表、索引,对应 base 下以 relfilenode 命名文件;单个文件超过 1GB 自动分片,生成xxx.1、xxx.2分片;每个文件内部最小 IO 单元是page 数据页,默认 8KB ;每个 page 内部包含页头、元组 tuple、空闲空间、行指针数组;同时配套.fsm空闲空间映射文件、.vm可见性映射文件,用于 vacuum 清理死元组。
表空间 tablespace:可以把表、索引存放在 PGDATA 之外的磁盘目录,用于冷热数据分离,高速 SSD 放热点索引,普通磁盘放归档冷数据;表空间目录必须操作系统 postgres 用户拥有,空目录;表空间属于集群元数据,不能脱离集群单独拷贝挂载到另外一套集群PostgreSQL。
实操 1:查看物理文件对应 oid、relfilenode
登录 psql,fgedudb 库:
\c fgedudb
SELECT oid,relname,relfilenode FROM pg_class WHERE relname='t_biz_data';
SELECT oid,datname FROM pg_database WHERE datname='fgedudb';
拿到 database 的 oid,进入 base 下对应 oid 子目录,就可以看到 t_biz_data 对应的 relfilenode 数据文件、fsm、vm 文件。
#示例,替换为实际查询出来的oid
cd /fgedudb/pg_data/fgedudb/base/数据库oid
ls -lh
实操 2:创建业务表空间
--表空间物理目录操作系统提前创建,权限postgres
CREATE TABLESPACE fgedu_ts LOCATION '/fgedudb/pg_tablespace/fgedu_ts';
--将表创建到此表空间
CREATE TABLE t_biz_hot (id bigserial primary key,info text) TABLESPACE fgedu_ts;
--索引也可以指定表空间
CREATE INDEX idx_biz_hot_code ON t_biz_hot(info) TABLESPACE fgedu_ts;
\db
操作系统查看 pg_tblspc 目录,是软链接指向/fgedudb/pg_tablespace/fgedu_ts真实目录。
风哥数据库教程 itpux‑com
物理存储运维要点
- pg_wal 目录文件绝对不能手动 rm 删除,会直接导致实例崩溃无法恢复;
- 表空间目录不能直接拷贝给另外一套 PG 集群,元数据记录在 global 系统表;
- relfilenode 会发生变化:truncate 表、vacuum full 会改变 relfilenode,物理文件名改变,DBA 排查故障要注意这点;
- page 默认 8KB,编译阶段 configure 可以修改 BLCKSZ,编译后不可变更。
四、PostgreSQL 进程架构:主进程、后台辅助进程、backend 会话进程
PostgreSQL 完全多进程架构,没有多线程,每个客户端连接生成独立 backend 进程,进程隔离,一个 backend 崩溃不会直接搞垮整个集群,主进程 postmaster 会负责重启清理资源。
进程角色分解:
- postmaster(守护主进程):集群根进程,负责监听端口,接收客户端 TCP 连接,fork 生成 backend 会话进程,管理所有后台辅助进程,信号处理,实例启停管理;postmaster PID 是整个集群标识。
- backend 后端会话进程:每一个客户端连接对应一个 backend,执行 SQL,执行读写,拥有私有内存,崩溃由 postmaster 回收资源。
- bgwriter 后台写进程:把 shared_buffers 脏页刷盘,减轻 checkpoint 压力,避免 checkpoint 瞬间大量 IO 风暴。
- checkpoint 检查点进程:执行检查点,把内存脏数据持久化磁盘,更新 WAL LSN,崩溃恢复从上一个检查点开始重放 WAL。
- walwriter WAL 写进程:专门负责把 WAL 缓冲区内容写入 pg_wal 磁盘文件,PG 重要的预写日志 WAL 机制。
- autovacuum 自动清理进程:多子进程,执行 vacuum、vacuum analyze,清理 dead tuple 死元组,更新统计信息,防止表膨胀,MVCC 核心配套进程。
- archiver 归档进程:开启 archive_mode 后,将 pg_wal 段文件归档,用于时间点 PITR 恢复。
上 51CTO 搜索风哥可以学习全套数据库教程
实操:查看全部 PostgreSQL 进程,主机 fgedu‑net‑cn1
ps -ef | grep postgres
可以看到 postmaster 主进程,backend 会话,bgwriter、checkpoint、walwriter、autovacuum、archiver 等后台进程。
psql 内部查看会话 backend pid:
SELECT pid,usename,datname,state,query FROM pg_stat_activity;
--查看当前自己backend进程pid
SELECT pg_backend_pid();
生产运维常见进程相关故障点:
- autovacuum 未正常工作,表疯狂膨胀,元组堆积;
- checkpoint 过于频繁,磁盘 IO 打满;
- 大量空闲 backend 会话占用连接数;
- archiver 归档卡住,pg_wal 目录持续暴涨磁盘占满。
五、PostgreSQL 内存结构理论,64G 内存 8C 硬件参数配置实操
PG 内存分为两大块:共享内存(所有进程共同访问)、每个 backend 进程私有内存(每个连接独立分配)。
共享内存核心参数(64G 物理内存 8CPU)
shared_buffers:PG 自己的内存缓冲池,建议设置物理内存 1/4,64G 机器设置 16G;不能超过操作系统总内存;PG 会在这里缓存数据 page;wal_buffers:WAL 日志缓冲区,一般设置 16MB‑64MB;effective_cache_size:查询规划器估算可用总缓存,规划索引扫描成本,64G 主机建议 48G;
backend 进程私有内存(每个会话独立)
work_mem:排序、hash join、hash 聚合操作单操作内存;大查询会多次分配,不能设置过大;64G 机器设置 64MB;maintenance_work_mem:vacuum、create index、alter table 维护操作内存,全局维护操作,64G 机器设置 1GB;temp_file_limit:会话临时文件上限,防止 SQL 疯狂写临时文件打满磁盘。
实操:postgresql.conf 配置,适配 64G 内存 8CPU 实例
配置文件路径/fgedudb/pg_data/fgedudb/postgresql.conf
#共享内存
shared_buffers = 16GB
wal_buffers = 64MB
effective_cache_size = 48GB
#私有会话内存
work_mem = 64MB
maintenance_work_mem = 1GB
temp_file_limit = 10GB
#连接参数
max_connections = 300
superuser_reserved_connections = 10
#WAL参数
max_wal_size = 4GB
min_wal_size = 1GB
wal_level = replica
#autovacuum
autovacuum = on
autovacuum_max_workers = 4
autovacuum_naptime = 1min
修改配置后重载生效,不需要重启(部分参数需要重启):
pg_ctl reload -D /fgedudb/pg_data/fgedudb
psql 查看内存相关参数:
show shared_buffers;
show work_mem;
show effective_cache_size;
内存重要理论:操作系统还有 page cache 文件系统缓存;PG 的 shared_buffers + OS page cache 共同缓存数据;effective_cache_size 不实际分配内存,仅仅给优化器做成本估算。
六、PostgreSQL 源码文件目录结构与阅读工具
PostgreSQL 主体由 C 语言开发,contrib 存放社区附加模块;源码目录结构说明:
src/backend:核心服务端代码,进程、执行器、优化器、存储、WAL、vacuum;src/bin:客户端工具二进制源码,psql、pg_ctl、initdb、pg_dump;src/include:全部头文件,数据结构定义;src/interfaces:libpq 客户端库源码;contrib:扩展模块,pg_stat_statements、dblink、pg_buffercache 等附加工具;
常用源码阅读工具:
- gdb:调试器,跟踪函数调用、断点、堆栈,本教程重点实操工具;
- cscope /ctags:生成索引,跳转函数、变量定义;
- vscode+clangd:现代编辑器源码浏览;
关键入口源码文件:
- initdb 主逻辑:
src/bin/initdb/initdb.c - postmaster 主进程入口:
src/backend/postmaster/postmaster.c - backend 后端会话入口:
src/backend/tcop/postgres.c - WAL 写逻辑:
src/backend/access/transam/xlog.c - 缓冲池管理:
src/backend/storage/buffer/bufmgr.c
提示:阅读源码优先从 main 入口函数跟踪调用链,不要漫无目的浏览全部文件。
七、源码编译搭建调试环境完整实战
编译开启‑‑enable‑debug加入调试符号,方便 gdb 断点跟踪;开启‑‑enable‑cassert内部断言,用于开发调试,生产环境不要开启断言,会严重降低性能Postg...。主机fgedu‑net‑cn1操作。
7.1 下载解压源码包
cd /fgedudb/pg_soft
#下载源码包,放置/fgedudb/pg_soft目录
tar -zxvf postgresql‑*.tar.gz
cd postgresql‑*
7.2 configure 配置编译选项,开启调试符号
./configure \
--prefix=/fgedudb/pg_soft/pg_bin \
--enable‑debug \
--enable‑cassert \
--with‑readline \
--with‑zlib
--enable‑debug:加入 gdb 调试符号;--enable‑cassert:开启内部断言,调试环境使用;- prefix 指定编译安装输出目录
/fgedudb/pg_soft/pg_bin。
7.3 编译安装,使用 8 核 CPU 并行编译
make -j8
make install
#编译contrib扩展模块
cd contrib
make -j8
make install
配置环境变量:
echo "export PATH=/fgedudb/pg_soft/pg_bin/bin:\$PATH" >> /etc/profile
source /etc/profile
which psql
which initdb
注意:此套编译带 debug 符号,不能直接上生产环境,性能低,体积大,专门用于源码学习调试。
八、gdb 调试工具跟踪 initdb 初始化全过程
initdb 是独立可执行程序,不属于 postmaster 后台进程,程序入口src/bin/initdb/initdb.c main()函数,gdb 可以直接运行 initdb,打断点跟踪每一步集群初始化流程。
8.1 清理旧数据目录,准备全新调试环境
#停止旧实例
pg_ctl stop -D /fgedudb/pg_data/fgedudb
#清空数据目录,initdb调试需要全新目录
rm -rf /fgedudb/pg_data/fgedudb
mkdir -p /fgedudb/pg_data/fgedudb
chown postgres:postgres /fgedudb/pg_data/fgedudb
su - postgres
export PGDATA=/fgedudb/pg_data/fgedudb
8.2 gdb 直接运行 initdb,设置断点
gdb --args initdb -D $PGDATA --encoding=UTF8 --locale=en_US.UTF‑8 --data‑checksums
进入 gdb 交互控制台:
#设置main入口断点
b main
run
#单步 next,进入函数step,查看堆栈bt
next
step
bt
#设置关键函数断点,例如initdb的创建template数据库函数
b setup_template_databases
#continue继续执行
c
quit
initdb 内部执行主要流程:
- 创建 PGDATA 目录,生成 PG_VERSION 版本标记;
- 初始化共享内存、基础系统 catalog 元数据表;
- 初始化 pg_xact、pg_wal、global 系统目录;
- 构建 template1、template0 模板数据库;
- 生成 postgres 默认数据库;
- 设置超级用户 postgres 密码,配置默认 pg_hba.conf、postgresql.conf。
通过 gdb 单步调试,能够看清每一步物理文件生成逻辑;initdb 执行完成之后,PGDATA 目录完整生成,此时就可以启动 postmaster 实例。
实操提示:gdb 调试 initdb,必须使用 postgres 操作系统用户,否则目录权限报错。
九、gdb 跟踪 PostgreSQL 实例启动流程
postmaster 是数据库守护主进程,二进制程序为postgres;启动流程:执行pg_ctl start,调用 postgres 可执行程序,进入 postmaster.c main 函数,初始化共享内存、信号量、WAL 恢复、加载系统 catalog,开始监听 TCP 端口,准备接收客户端连接。
9.1 方式一:gdb attach 附加到已经运行的 postmaster 进程
启动实例,拿到 postmaster 主 PID 号:
su - postgres
pg_ctl start -D $PGDATA
ps -ef | grep postmaster
attach 附加调试(调试环境,不要生产操作):
gdb -p 主进程PID
常用 gdb 命令:
bt #打印调用堆栈
info threads #查看全部线程(PG多进程,这里看到fork子进程)
b PostmasterMain #主循环断点
c
next
quit
9.2 跟踪 backend 会话进程
获取当前 backend pid,psql 内部执行select pg_backend_pid();,使用 gdb attach 绑定 backend pid,可以跟踪 SQL 执行内部调用链。
gdb -p backend‑pid
9.3 启动阶段断点,直接 gdb 运行 postgres 程序
#先停止实例
pg_ctl stop -D $PGDATA
gdb --args postgres -D /fgedudb/pg_data/fgedudb
(gdb) b PostmasterMain
(gdb) run
启动流程关键步骤:
- PostmasterMain 入口,解析启动参数;
- 信号、操作系统资源初始化;
- 初始化共享内存、信号量;
- WAL 崩溃恢复:重放 pg_wal 日志,恢复到一致性状态;
- 加载系统全局 catalog;
- 初始化各个后台辅助进程 bgwriter/checkpoint/walwriter/autovacuum;
- 开启 socket 监听 5432 端口,等待客户端连接;
- 收到连接请求 fork 生成 backend 子进程。
重要提醒:gdb attach 附加进程会暂停数据库运行,生产环境绝对禁止操作,只允许测试学习环境使用。
十、底层问题分析思路、运维落地建议
风哥结合体系结构与源码学习,整理 DBA 运维分析思路:
- 故障优先区分现象层级 :
- 实例无法启动:优先看 postmaster 日志,排查共享内存、WAL 损坏、目录权限、pg_hba.conf、postgresql.conf 参数;
- SQL 性能慢:看 backend 进程、explain 执行计划、buffer 访问、IO;
- 表膨胀:重点排查 autovacuum 进程运行状态,MVCC 死元组;
- WAL 暴涨:检查 archiver 归档进程是否卡住。
- 不要只看表面现象,回溯底层模型:遇到锁、膨胀、crash,回到进程模型、内存模型、page 元组 MVCC 机制去理解根因。
- 源码学习建议:不要通读全部源码,针对具体问题跟踪调用链;例如想搞懂 checkpoint,就在 gdb 针对 checkpoint 相关函数打断点,跟踪执行流程。
- 调试编译版本只用于学习,生产使用官方 release 版本,关闭 debug 与 cassert 断言。
- 物理备份迁移要理解 PGDATA 内部各个目录作用,不要随意手动删除 pg_wal、pg_xact 目录文件。
风哥针对本文总结
风哥教程本文完整讲解 PostgreSQL 体系结构深入与源码解析,包含整体架构总览、逻辑存储结构、物理存储 PGDATA 目录、page 页、表空间原理与实操;进程模型 postmaster 守护进程、backend 会话进程、bgwriter、checkpoint、walwriter、autovacuum 后台进程;内存模型共享内存与 backend 私有内存,基于 64G 内存 8CPU 硬件规格 postgresql.conf 参数配置;源码目录结构、源码阅读工具;完整实战搭建带调试符号的源码编译环境;使用 gdb 调试 initdb 集群初始化全过程;gdb 跟踪 postmaster 实例启动流程、backend 会话进程。
PostgreSQL 是多进程架构,每个客户端连接独立 backend 进程;集群 cluster 是 PG 非常核心概念,一套 PGDATA 对应一个集群,集群内部多个 database 数据库,database 之间物理隔离;物理存储最小 IO 单元为 8KB page;WAL 预写日志保障崩溃恢复能力,pg_wal 文件严禁手动删除。
源码调试环境仅用于学习研究,禁止直接上线生产;gdb attach 会暂停进程,仅测试环境使用。掌握体系底层原理,DBA 面对实例启动失败、表膨胀、WAL 异常、慢 SQL、锁冲突等故障,能够穿透现象定位根因,而不是只停留在简单配置复制粘贴。掌握本套风哥教程全部实操,能够建立 PostgreSQL 底层认知,为后续性能调优、故障排查、高可用架构打下坚实底层基础。