postgresql数据库内核总体架构

一、架构总览图

二、详细架构说明

1. 进程架构:多进程模型

PostgreSQL 采用经典的多进程架构,每个客户端连接对应一个独立的操作系统进程。

1.1 主守护进程(Postmaster)

  • 启动与初始化:第一个启动的进程,负责初始化共享内存、启动后台进程
  • 连接管理:监听网络端口(默认5432),接受客户端连接请求
  • 进程派生:为每个新连接 fork() 出独立的子进程(后端进程)
  • 信号处理:处理系统信号(SIGTERM、SIGHUP等),实现优雅关闭和配置重载
  • 子进程监控:监控子进程状态,处理异常退出的子进程

1.2 后端进程(Backend Process)

每个客户端连接对应一个独立的后端进程,包含完整的查询处理上下文:

arduino 复制代码
// 进程上下文结构示意
typedef struct {
    Port *port;                    // 连接端口信息
    MemoryContext top_memory;      // 顶级内存上下文
    TransactionState s_cur;        // 当前事务状态
    List *parse_trees;             // 解析树列表
    QueryDesc *query_desc;         // 查询描述符
    TupleTableSlot *slot;          // 元组槽
    ResourceOwner resowner;        // 资源所有者
} BackendProcess;

1.3 后台辅助进程组

进程名称 主要职责 触发条件 关键参数
Writer 将脏页刷回磁盘 后台定期或缓冲区满 bgwriter_delay, bgwriter_lru_maxpages
WAL Writer 刷写WAL缓冲区 事务提交或缓冲区满 wal_writer_delay, wal_writer_flush_after
Checkpointer 执行检查点 定期或手动CHECKPOINT checkpoint_timeout, checkpoint_completion_target
Autovacuum Launcher 启动自动清理工作进程 定期检查需要清理的表 autovacuum_naptime, autovacuum_max_workers
Autovacuum Worker 执行实际的VACUUM/ANALYZE 由Launcher启动 autovacuum_vacuum_threshold
Archiver 归档WAL日志 WAL段文件切换 archive_mode, archive_command
Stats Collector 收集统计信息 定期或DDL操作后 stats_temp_directory
Logical Replication Launcher 启动逻辑复制工作进程 配置了逻辑复制时 max_logical_replication_workers

2. 内存架构:分层管理

2.1 共享内存(Shared Memory)

在数据库启动时一次性分配,所有进程共享访问:

objectivec 复制代码
// 共享内存主要区域
typedef struct {
    shmem_startup_hdr *shim;      // 启动头信息
    PGShmemHeader *header;        // 共享内存头
    BufferDescriptors *buf_desc;  // 缓冲区描述符数组
    BufferBlocks *buf_blocks;     // 实际数据缓冲区
    LWLockArray *lock_array;      // 轻量级锁数组
    ProcArray *proc_array;        // 进程状态数组
    XLogCtlData *xlog_ctl;        // WAL控制结构
    CLogCtlData *clog_ctl;        // 提交日志控制
    MultiXactState *multi_state;  // 多事务状态
    // ... 其他共享数据结构
} SharedMemoryArea;

关键共享区域:

  • 共享缓冲区(Shared Buffers) :缓存数据页,减少磁盘I/O
  • WAL缓冲区(WAL Buffers) :事务日志的写前缓冲区
  • 锁表(Lock Tables) :存储表级、页级、行级锁信息
  • 进程表(Proc Array) :所有活动进程的状态信息
  • CLOG缓冲区:事务提交状态的缓存

2.2 进程私有内存(Local Memory)

每个后端进程独立分配,用于查询执行:

内存上下文 用途 生命周期
TopMemoryContext 进程生命周期内存 进程存活期间
CacheMemoryContext 系统缓存(如relcache) 长期存在
MessageContext 客户端消息处理 每个消息
TupleContext 元组处理临时内存 每个元组处理
ExprContext 表达式求值 每个表达式
WorkMem 排序、哈希操作 每个操作
MaintenanceWorkMem VACUUM、CREATE INDEX等 维护操作期间

3. 查询处理流水线

3.1 解析阶段(Parser)

ini 复制代码
-- 示例:SELECT * FROM users WHERE id = 1;

解析器生成原始解析树

arduino 复制代码
typedef struct SelectStmt {
    NodeTag type;
    List *targetList;      // 目标列列表
    List *fromClause;      // FROM子句
    Node *whereClause;     // WHERE条件
    List *groupClause;     // GROUP BY
    Node *havingClause;    // HAVING
    List *windowClause;    // WINDOW
    // ... 其他字段
} SelectStmt;

3.2 分析/重写阶段(Analyzer/Rewriter)

  • 语义分析:检查对象存在性、权限、类型兼容性
  • 查询重写:应用规则系统(如视图展开)
  • 输出查询树(Query Tree)
arduino 复制代码
typedef struct Query {
    NodeTag type;
    CmdType commandType;    // SELECT/INSERT/UPDATE/DELETE
    List *rtable;           // 范围表
    List *jointree;         // 连接树
    List *targetList;       // 目标列表
    List *returningList;    // RETURNING列表
    // ... 其他字段
} Query;

3.3 优化阶段(Optimizer)

基于成本的优化器(CBO)工作流程:

  1. 子查询处理:扁平化子查询,转换为连接

  2. 预处理表达式:常量折叠、函数内联

  3. 生成路径

    • 顺序扫描路径(SeqScan)
    • 索引扫描路径(IndexScan、BitmapHeapScan)
    • 连接路径(NestLoop、HashJoin、MergeJoin)
  4. 成本估算:基于统计信息计算I/O、CPU成本

  5. 路径选择:选择成本最低的执行路径

arduino 复制代码
// 路径成本估算
typedef struct Path {
    NodeTag type;
    NodeTag pathtype;       // 路径类型
    RelOptInfo *parent;     // 父关系
    PathTarget *pathtarget; // 路径输出
    ParamPathInfo *param_info;
    List *pathkeys;         // 路径排序
    double rows;            // 预估行数
    Cost startup_cost;      // 启动成本
    Cost total_cost;        // 总成本
} Path;

3.4 执行阶段(Executor)

采用火山模型(Volcano Model) ​ 的迭代器模式:

arduino 复制代码
// 执行器状态机
typedef struct PlanState {
    NodeTag type;
    Plan *plan;                    // 对应的计划节点
    EState *state;                 // 执行状态
    TupleTableSlot *ps_ResultTupleSlot;  // 结果元组槽
    
    // 迭代器接口
    TupleTableSlot *(*ExecProcNode)(struct PlanState *pstate);
    
    // 子节点
    List *child_ps;
} PlanState;

主要执行节点类型:

  • 扫描节点:SeqScan、IndexScan、BitmapHeapScan
  • 连接节点:NestLoop、HashJoin、MergeJoin
  • 聚合节点:Agg、Group、WindowAgg
  • 排序节点:Sort、Materialize
  • 修改节点:ModifyTable(INSERT/UPDATE/DELETE)

4. 存储引擎架构

4.1 表空间与文件布局

bash 复制代码
$PGDATA/
├── base/                    # 默认表空间
│   ├── 1/                  # 数据库OID
│   │   ├── 12345           # 关系文件(表/索引)
│   │   ├── 12345_fsm       # 空闲空间映射
│   │   ├── 12345_vm        # 可见性映射
│   │   └── 12345_init      # 初始化分支
├── pg_tblspc/              # 表空间链接
├── pg_wal/                 # WAL日志
├── pg_xact/                # 提交日志
└── pg_stat/                # 统计信息

4.2 堆表存储结构

页面结构(8KB默认):

sql 复制代码
+-----------------------+
| PageHeaderData (24B)  |
+-----------------------+
| LinePointer1 (4B)     |
| LinePointer2 (4B)     |
| ...                   |
+-----------------------+
| Free Space            |
+-----------------------+
| Tuple1                |
| Tuple2                |
| ...                   |
+-----------------------+
| Special Space         |
+-----------------------+

元组头结构:

ini 复制代码
typedef struct HeapTupleHeaderData {
    union {
        HeapTupleFields t_heap;
        DatumTupleFields t_datum;
    } t_choice;
    
    ItemPointerData t_ctid;      // 当前元组ID(页号+行指针)
    
    uint16 t_infomask2;          // 属性数量+标志位
    uint16 t_infomask;           // 标志位(可见性、锁等)
    uint8 t_hoff;                // 头长度
    
    bits8 t_bits[FLEXIBLE_ARRAY_MEMBER];  // NULL位图
} HeapTupleHeaderData;

4.3 索引架构

PostgreSQL支持多种索引访问方法:

索引类型 适用场景 内部结构 特性
B-Tree 默认索引,范围查询 B+树变体 支持排序、唯一约束
Hash 等值查询 哈希表 仅支持等值比较
GiST 地理数据、全文搜索 平衡树 可扩展,支持自定义操作符
SP-GiST 非平衡数据结构 空间分区树 适合不规则数据分布
GIN 多值类型(数组、JSON) 倒排索引 支持包含查询
BRIN 大数据表,有序数据 块范围索引 存储空间小

5. 事务与并发控制

5.1 MVCC实现机制

元组可见性判断算法:

kotlin 复制代码
bool HeapTupleSatisfiesMVCC(HeapTuple htup, Snapshot snapshot,
                            Buffer buffer) {
    // 获取元组的事务信息
    xmin = HeapTupleHeaderGetXmin(htup->t_data);
    xmax = HeapTupleHeaderGetXmax(htup->t_data);
    
    // 根据快照判断可见性
    if (TransactionIdIsCurrentTransactionId(xmin)) {
        // 当前事务插入
        if (HeapTupleHeaderGetCmin(htup->t_data) >= snapshot->curcid)
            return false;  // 命令ID在快照之后
        else
            return true;
    } else if (TransactionIdIsInProgress(xmin)) {
        // 其他活跃事务插入
        return false;
    } else if (TransactionIdDidCommit(xmin)) {
        // 已提交事务插入
        // 检查删除事务
        if (!HeapTupleHeaderXmaxCommitted(htup->t_data)) {
            if (TransactionIdIsCurrentTransactionId(xmax)) {
                // 当前事务删除
                return false;
            }
            if (TransactionIdIsInProgress(xmax)) {
                // 其他活跃事务删除
                return true;
            }
            if (TransactionIdDidCommit(xmax)) {
                // 已提交事务删除
                return false;
            }
            return true;  // 删除事务已中止
        }
        return false;  // 已删除
    } else {
        // 插入事务已中止
        return false;
    }
}

5.2 快照隔离级别

sql 复制代码
-- PostgreSQL支持的隔离级别
SET TRANSACTION ISOLATION LEVEL 
    READ UNCOMMITTED |  -- 实际实现为READ COMMITTED
    READ COMMITTED |    -- 默认级别
    REPEATABLE READ |   -- 快照隔离
    SERIALIZABLE;       -- 可串行化

快照数据结构:

go 复制代码
typedef struct SnapshotData {
    SnapshotType snapshot_type;  // 快照类型
    TransactionId xmin;          // 最早仍活跃的事务ID
    TransactionId xmax;          // 第一个未分配的事务ID
    TransactionId *xip;          // 活跃事务ID数组
    uint32 xcnt;                 // 活跃事务数量
    CommandId curcid;            // 当前命令ID
    uint32 speculativeToken;
    struct GlobalVisState *vistest;
    TimestampTz taken_at;        // 快照获取时间
    // ... 其他字段
} SnapshotData;

6. WAL与恢复机制

6.1 WAL记录结构

go 复制代码
typedef struct XLogRecord {
    uint32 xl_tot_len;      // 记录总长度
    TransactionId xl_xid;   // 事务ID
    XLogRecPtr xl_prev;     // 前一条记录指针
    uint8 xl_info;          // 标志位
    RmgrId xl_rmid;         // 资源管理器ID
    pg_crc32c xl_crc;       // CRC校验
    // 之后是实际的数据
} XLogRecord;

6.2 检查点机制

检查点触发条件:

  1. 时间间隔(checkpoint_timeout,默认5分钟)
  2. WAL段文件数量(max_wal_size,默认1GB)
  3. 手动执行CHECKPOINT命令

检查点执行流程:

scss 复制代码
void CreateCheckPoint(int flags) {
    // 1. 开始检查点
    START_CRIT_SECTION();
    
    // 2. 刷新所有脏页到磁盘
    CheckPointBuffers();
    
    // 3. 写入检查点记录到WAL
    XLogBeginInsert();
    XLogRegisterData(&checkPoint, sizeof(checkPoint));
    XLogInsert(RM_XLOG_ID, XLOG_CHECKPOINT_SHUTDOWN);
    
    // 4. 刷新WAL到磁盘
    XLogFlush(recptr);
    
    // 5. 更新控制文件
    UpdateControlFile();
    
    // 6. 删除旧的WAL文件
    RemoveOldWalFiles();
    
    END_CRIT_SECTION();
}

7. 扩展性架构

7.1 扩展接口

c 复制代码
// 扩展加载接口
typedef struct {
    const char *name;           // 扩展名
    void (*init)(void);         // 初始化函数
    void (*shutdown)(void);     // 关闭函数
    // ... 其他钩子函数
} ExtensionCallbacks;

// 注册示例
PG_MODULE_MAGIC;
void _PG_init(void) {
    // 注册扩展
    RegisterExtension("my_extension", my_init, my_shutdown);
}

7.2 可扩展组件类型

组件类型 注册函数 用途示例
数据类型 CREATE TYPE 自定义几何类型、货币类型
函数 CREATE FUNCTION 用户定义函数、聚合函数
操作符 CREATE OPERATOR 自定义比较操作符
索引方法 CREATE ACCESS METHOD 支持新的索引结构
外部数据包装器 CREATE FOREIGN DATA WRAPPER 访问外部数据源
表访问方法 CREATE TABLE ACCESS METHOD 自定义表存储格式
过程语言 CREATE LANGUAGE PL/Python、PL/Java

8. 关键性能优化点

8.1 缓冲区管理策略

  • 时钟扫描算法(Clock-sweep) :用于缓冲区替换
  • 预取(Prefetch) :顺序扫描时的异步I/O
  • 批量写入:检查点期间的顺序写优化

8.2 并行查询执行

sql 复制代码
-- 并行查询配置
SET max_parallel_workers_per_gather = 4;
SET parallel_setup_cost = 1000;
SET parallel_tuple_cost = 0.1;

-- 并行执行计划示例
EXPLAIN (ANALYZE, VERBOSE)
SELECT COUNT(*) FROM large_table WHERE condition;
-- 输出将显示 Workers Planned/Launched

8.3 分区表支持

sql 复制代码
-- 声明式分区
CREATE TABLE measurement (
    city_id int not null,
    logdate date not null,
    peaktemp int,
    unitsales int
) PARTITION BY RANGE (logdate);

-- 创建分区
CREATE TABLE measurement_y2023m01 
    PARTITION OF measurement
    FOR VALUES FROM ('2023-01-01') TO ('2023-02-01');

-- 分区裁剪优化
EXPLAIN SELECT * FROM measurement 
WHERE logdate >= '2023-01-15' AND logdate < '2023-01-20';

三、架构优势与特点

1. 进程隔离优势

  • 稳定性:单个进程崩溃不影响其他连接
  • 安全性:进程间内存隔离,防止内存泄漏扩散
  • 简化编程:无需处理多线程同步的复杂性

2. 扩展性设计

  • 可插拔存储引擎:通过表访问方法接口
  • 自定义数据类型:完整的类型系统支持
  • 丰富的索引类型:多种索引访问方法

3. 企业级特性

  • 完整的ACID支持:通过WAL和MVCC实现
  • 在线备份:支持热备份和PITR
  • 逻辑复制:数据选择性复制
  • 并行查询:多核CPU利用

4. 社区生态

  • 丰富的扩展:PostGIS(地理信息)、pgvector(向量搜索)、TimescaleDB(时序数据)
  • 多种连接池:pgBouncer、pgpool-II
  • 监控工具:pg_stat_statements、pgBadger、pgAdmin

四、配置调优建议

1. 内存相关配置

ini 复制代码
# postgresql.conf 关键参数
shared_buffers = 4GB           # 共享缓冲区,建议系统内存的25%
work_mem = 64MB                # 每个操作的内存,复杂查询需要更多
maintenance_work_mem = 1GB     # 维护操作内存
effective_cache_size = 12GB    # 优化器假设的OS缓存大小

2. WAL相关配置

ini 复制代码
wal_level = replica            # 或 logical 用于逻辑复制
max_wal_size = 4GB             # 最大WAL大小
min_wal_size = 1GB             # 最小WAL大小
checkpoint_timeout = 15min     # 检查点时间间隔
checkpoint_completion_target = 0.9  # 检查点完成目标

3. 并行查询配置

ini 复制代码
max_worker_processes = 8       # 最大工作进程数
max_parallel_workers_per_gather = 4  # 每个Gather节点的并行数
max_parallel_workers = 8       # 系统并行工作进程数
parallel_setup_cost = 1000     # 并行启动成本
parallel_tuple_cost = 0.1      # 并行元组传输成本

总结

PostgreSQL 的架构设计体现了"稳健优先、功能丰富、高度可扩展"的哲学思想。其多进程模型虽然在某些高并发场景下可能不如多线程模型高效,但提供了无与伦比的稳定性和隔离性。清晰的模块化设计使得每个组件都可以独立优化和扩展,而严谨的 MVCC 实现和完整的 WAL 机制确保了数据的一致性和持久性。

随着 PostgreSQL 的持续发展,其架构也在不断演进,如引入了并行查询、JIT编译、增量排序等现代特性,同时保持了向后兼容性。这种平衡传统与创新的设计理念,使得 PostgreSQL 能够在保持稳定可靠的同时,不断适应新的应用场景和技术挑战。

相关推荐
良逍Ai出海1 天前
OpenClaw 新手最该先搞懂的 2 套命令
android·java·数据库
Keanu-1 天前
Redis 主从复制及哨兵模式配置
服务器·数据库·redis
blues92571 天前
【JOIN】关键字在MySql中的详细使用
数据库·mysql
x-cmd1 天前
[260307] x-cmd v0.8.6:新增 gpt-5.4 模型支持,sudo/os/hostname/cpu 等模块文档更新
java·数据库·gpt·sudo·x-cmd·googel
JuneXcy1 天前
第10章 数据库的安全与保护
数据库·mysql
liqianpin11 天前
完美解决phpstudy安装后mysql无法启动
数据库·mysql
小Tomkk1 天前
化繁为简:Access 与 SQL 创新指南(第四篇)
数据库·sql
cyber_两只龙宝1 天前
【MySQL】MySQL主从复制架构
linux·运维·数据库·mysql·云原生·架构
D.不吃西红柿1 天前
【无标题】
数据库·database·ai编程·数据库设计
Dylan~~~1 天前
PostgreSQL 数据库性能问题定位完全指南
数据库·postgresql