土法炼钢兴趣小组的算法知识备份

【PG 内核】PostgreSQL 内核机制深度拆解

文章导航

分类入口
databasekernel
标签入口
#postgresql#pg-kernel#mvcc#wal#vacuum#btree#query-planner#streaming-replication#logical-replication#buffer-manager#lock-manager#troubleshooting#pg-upgrade#pitr#postgresql-internals

目录

PostgreSQL 内核机制深度拆解

PostgreSQL 是开源关系型数据库的标杆——源码注释详尽,官方文档即是事实标准,pgsql-hackers 邮件列表记录了几十年间的设计决策。但中文互联网不缺”怎么用 pg_stat_statements 查慢查询”的教程,缺一份把 PG 内核实现讲透、同时告诉你生产环境会出什么事、出了事怎么排查的系统性资料

本站已有 存储工程(79 篇,B-Tree/Buffer Pool/WAL 通用原理)、数据库 MVCC(PG 源码级 MVCC 拆解)、分布式(70 篇,一致性/复制理论)——本系列是这些基础在 PG 内核中的具体落地,以及它们在生产环境中的故障形态和排查方法论

系列定位:上半场(第 1–20 章)是内核机制拆解,下半场(第 21–26 章)是运维实战。不写 runbook,写”为什么这样做、不这样做的后果”。

推荐入口

一、这个系列要回答的九个问题

  1. PG 的进程模型多进程共享内存到底是怎么工作的? Postmaster、backend、background worker、autovacuum launcher/worker 如何协作?共享内存中有哪些关键数据结构?fork() 的开销 PG 怎么消化? → 第 1、2 章。

  2. 一条 SQL 从客户端到磁盘经历了哪些环节? Parser → Analyzer → Rewriter → Planner → Executor → Buffer Manager → WAL → Disk。每个环节的输入输出、关键数据结构和性能边界是什么? → 第三部分(第 9–13 章)。

  3. PG 的 MVCC 为什么需要 VACUUM?膨胀到底怎么发生的? 已有 mvcc 文章 讲了可见性判断和隔离级别,本系列深入:CLOG 的 SLRU 结构、hint bit 的写入时机、VACUUM 的完整内部流程、freezing 的必要性和 wraparound 的触发链。 → 第 3、8 章。

  4. PG 的查询优化器如何做决策?统计信息不准的时候怎么办? pg_statistic 里存了什么?选择率估算公式从哪来?Join 顺序搜索空间多大?统计信息漂移怎么诊断? → 第 10、11 章。

  5. PG 的高可用方案各自的机制边界在哪? 流复制的 WAL Sender/Receiver 协议、同步复制的语义差异、逻辑解码的 reorder buffer、逻辑复制的冲突处理。 → 第 18、19 章。

  6. PG 最危险的 5 种故障模式——根因、排查路径和修复边界是什么? 连接风暴为什么能打垮 PG?事务 ID wraparound 最早期的信号是什么?replication slot 溢出的多米诺效应一个 slot 如何拖垮整个集群? → 第 22 章集中回答。

  7. 排查慢查询的完整工具链怎么用——如何从现象定位到内核根因? pg_stat_statementsEXPLAIN (ANALYZE, BUFFERS)wait_eventpg_locksperf/bpftrace——五层递进的调查链,每一层解释”这个数字为什么有问题”。 → 第 23 章集中回答,第 6、10、12 章提供所需的内核知识。

  8. PG 最容易被误解和配错的 GUC——配错之后会发生什么? work_mem 不是”每个操作的内存上限”——200 个连接同时做 hash join 会发生什么?fsync=off 为什么 COMMIT 之后数据真可能丢?random_page_cost=4 在 NVMe 上有多离谱? → 第 26 章回答,第 5、10 章提供内核基础。

  9. pg_upgrade / PITR / pg_resetwal 的内部机制和操作边界是什么? pg_upgrade --link 为什么零拷贝但同时零回滚?PITR 恢复的三个关键窗口各缺了什么就无法完成?pg_resetwal 为什么是最后手段——以及用了之后哪些数据不可访问? → 第 24、25 章。

二、篇目依赖关系与推荐阅读路径

全系列六部分、26 篇。每篇按「问题→机制→源码→实验→边界」组织,源码以 PG 17 为主线,涉及版本差异时标注 PG 16。运维章(第 21–26 章)按「现象→根因(内核机制解释)→排查路径→修复边界」组织。

强依赖图

flowchart TD
  A["01 进程模型与共享内存"] --> B["02 页面布局与元组格式"]
  B --> C["03 MVCC 实现:CLOG/hint bit"]
  A --> E["05 Buffer Manager"]
  B --> E
  C --> D["04 WAL 内部机制"]
  E --> D
  A --> F["06 锁管理器"]
  E --> G["07 事务与子事务"]
  F --> G
  C --> H["08 VACUUM 与 Freezing"]
  G --> H
  B --> I["09 查询解析与重写"]
  I --> J["10 统计信息与代价模型"]
  J --> K["11 Join 顺序与路径生成"]
  K --> L["12 执行器与表达式求值"]
  L --> M["13 JIT 编译"]
  B --> N["14 B-Tree 索引"]
  B --> O["15 GiST 索引"]
  B --> P["16 GIN 索引"]
  B --> Q["17 BRIN 与其他索引"]
  D --> R["18 流复制"]
  R --> S["19 逻辑复制与逻辑解码"]
  R --> T["20 扩展系统与 FDW"]
  
  A --> U["21 监控体系与告警设计"]
  C --> U
  H --> U
  C --> V["22 经典故障模式与排查手册"]
  H --> V
  D --> V
  R --> V
  F --> W["23 性能异常调查方法论"]
  J --> W
  L --> W
  U --> W
  V --> W
  W --> X["24 数据恢复与损坏应对"]
  W --> Y["25 大版本升级与迁移实战"]
  W --> Z["26 配置陷阱与生产最佳实践"]

推荐阅读路径

三、目录与每篇一句话价值

第一部分:体系结构地基

  1. 进程模型与共享内存 Postmaster 启动、backend fork()、共享内存初始化 (CreateSharedMemoryAndSemaphores)、PGPROC/LWLock 数组——理解 PG 多进程架构的”骨架”,是读懂所有后续机制的入口。

  2. 页面布局与元组格式 PageHeaderDataHeapTupleHeaderData(xmin/xmax/ctid/t_infomask)、TOAST 外存机制——理解 PG 如何把数据和元数据塞进 8KB 页面,以及大字段的处理策略。

第二部分:并发控制与崩溃恢复

  1. MVCC 实现:CLOG、hint bit 与快照可扩展性 在已有 mvcc 文章 基础上深入:CLOG 的 SLRU 页面与事务状态位、hint bit 的写入时机和竞争、PG 14 snapshot scalability 优化——这是理解”为什么 VACUUM 必须存在”的基础。

  2. WAL 内部机制 XLogInsert() 的完整路径、WAL record 物理布局、Checkpoint 两阶段流程、wal_sync_method 的语义差异——以及 checkpoint IO 风暴的根因和调优。

  3. Buffer Manager shared_buffers 的组织、Clock sweep 替换算法、buffer 状态机、bgwriter 的触发条件、BAS_BULKREAD/BAS_VACUUM 策略——理解”为什么 shared_buffers 不是越大越好”。

  4. 锁管理器 SpinLock → LWLock → Heavyweight Lock 三层体系、LWLock 的演进(PG 9.4 到 PG 16)、DeadLockCheck() 的等待图算法、行级锁的 lock bits——以及死锁检测与 log_lock_waits 的联合使用。

  5. 事务与子事务 事务状态机 (TransState)、子事务 (savepoint) 的 TransactionState 栈、2PC 的 WAL 记录与状态文件、事务 ID 分配 (AssignTransactionId) 与 xidStopLimit/xidWrapLimit

  6. VACUUM 与 Freezing VACUUM 的 scan→vacuum→index cleanup→FSM/VM update 流程、可见性映射 (VM) 与 Index-Only Scan 的依赖、anti-wraparound VACUUM 的触发条件与预警信号链——从 age(relfrozenxid) 到数据库只读 shutdown 的完整危机时间线。

第三部分:查询生命周期

  1. 查询解析与重写 gram.yRawStmt parse tree → parse_analyze() 语义分析 → 规则系统 (pg_rewrite) 视图展开——理解 Query 结构体的 rtable/jointree/targetList 各字段含义。

  2. 查询规划器 — 统计信息与代价模型 pg_statistic 的内容(MCV/histogram/correlation)、ANALYZE 的采样流程、代价常量的物理意义 (random_page_cost 在 NVMe 上为什么该改)、统计信息漂移的诊断和扩展统计 (CREATE STATISTICS)。

  3. 查询规划器 — Join 顺序与路径生成 make_one_rel() 的 path 生成流程、四种访问路径 (SeqScan/IndexScan/IndexOnlyScan/BitmapScan)、三种 Join 方式 (NestLoop/HashJoin/MergeJoin) 的代价比较、动态规划 vs GEQO 遗传算法的切换条件。

  4. 执行器与表达式求值 火山模型的 ExecInitNode()ExecProcNode()ExecEndNode()、EEO (Expression Evaluation) 的 opcode 编译、TupleTableSlot 机制——以及查询 hang 住时用 wait_event + pg_blocking_pids() 诊断。

  5. JIT 编译 PG 的 LLVM 集成 (llvmjit.c)、JIT 编译范围(表达式求值与 tuple 变形)、jit_above_cost 的决策流程——以及 JIT 的适用场景和不适用场景。

第四部分:索引内部机制

  1. B-Tree 索引 PG B-Tree 的页面布局 (BTPageOpaque/high key/rightlink)、插入与页面分裂 (_bt_split()) 的完整流程、PG 12+ 去重 (deduplicate_items)——为什么 PG B-Tree 不直接套用教科书实现。

  2. GiST 索引 GiST 的抽象算子接口 (Consistent/Union/Penalty/PickSplit)、搜索的深度优先 + Consistent 过滤、几何类型 (point_ops) 和全文搜索 (tsvector_ops) 的 GiST 实现。

  3. GIN 索引 倒排结构 (entry tree + posting tree/list)、Fast Update 的 pending list 机制、gingetbitmap() 的 bitmap AND/OR——以及 GIN vs GiST 的选择三角(写/读/存储)。

  4. BRIN 与其他索引 BRIN 的范围摘要哲学、hash 索引的 WAL 现状、Bloom 索引 (bloom 扩展) 的适用场景——什么时候不建 B-Tree 反而更好。

第五部分:扩展性与高可用

  1. 流复制 WAL Sender (WalSndLoop()) 和 WAL Receiver (WalRcvLoop()) 的协议、同步复制的三级语义、slot 机制——以及 replication slot 溢出导致 primary PANIC 的完整因果链。

  2. 逻辑复制与逻辑解码 Logical Decoding Context 的内部流程、Reorder Buffer 的事务重排、pgoutput 协议、Publication/Subscription 模型——以及逻辑复制的四种冲突类型和修复边界。

  3. 扩展系统与 FDW PG 的 hook 机制全景、FDW 的 FdwRoutine 回调接口、postgres_fdw 的 pushdown 机制——理解扩展如何影响 planner/executor 的内部决策。

第六部分:运维实战与故障排查

  1. 监控体系与告警设计 从内核机制出发定义”应该监控什么”——六个监控维度的指标选型、告警阈值的内核依据、pg_stat_statements/pgBadger/pg_exporter 的集成策略。不讲”装哪个 Grafana 面板”,讲”这个指标不正常时内核在做什么”。

  2. 经典故障模式与排查手册 连接风暴(work_mem 的连锁效应)、事务 ID wraparound 危机(从预警到数据库只读的完整时间线)、replication slot 溢出(一个 slot 如何拖垮整个集群)、OOM 连锁 kill(为什么 Postmaster 杀一个连接会断开所有连接)、长事务的隐性破坏(膨胀加速与 DDL 阻塞)。每例配 Mermaid 时序图和排查断点。

  3. 性能异常调查方法论 五层调查链:pg_stat_statements(定位问题 query)→ EXPLAIN (ANALYZE, BUFFERS)(看计划与实际偏差)→ wait_event(诊断等什么)→ pg_locks + pg_blocking_pids()(追踪锁等待链)→ perf/bpftrace(内核调用栈)。三个特殊场景的深度诊断:“有时快有时慢”、“CPU 100% 但没慢查询”、“IO 打满但命中率 99%”。

  4. 数据恢复与损坏应对 PITR 恢复的三个关键窗口与恢复精度、pg_checksums 的校验粒度与盲区、pg_resetwal 的内部操作和不可逆后果、pg_dump/pg_restore 的并行策略与恢复加速——以及哪些恢复操作做了之后数据已经不可逆地丢了。

  5. 大版本升级与迁移实战 pg_upgrade --link 为什么快(hard link = 0 拷贝)以及为什么没有回滚机制、pg_upgrade --check 的预检清单、逻辑复制跨版本迁移的低停机方案、pg_dump 迁移后统计信息消失的陷阱——附带迁移方案决策树(小/中/大库选哪种方案)。

  6. 配置陷阱与生产最佳实践 11 个最容易被误解和配错的 GUC,每条配:为什么默认值在你的场景下可能是错的、配错之后的症状和根因解释、通过什么视图和日志确认当前设置有问题。不是”设成 X 就好了”,是”知道为什么以及怎么验证”。

四、读者定位与先修要求

维度 说明
主要读者 数据库内核开发者、后端工程师(日常使用 PG 且想深入理解)、SRE/DBRE(需要内核视角排查问题)
次要读者 系统软件方向学生、从 MySQL 转 PG 的工程师、分布式数据库开发者(参考 PG 的单机设计)
先修知识 SQL、基本数据库概念(ACID、索引、WAL)、C 语言阅读能力、操作系统基础(进程、内存、I/O)
不假设 PG 内核贡献经验、数据库理论论文深度

五、与其他系列的关联

本站已有系列 与本系列的关系
数据库 MVCC 第 3 章的直接前置——已有文章讲了可见性判断和隔离级别,本系列深入 CLOG/hint bit/snapshot scalability
存储工程(79 篇) WAL/ARIES、Buffer Pool、B-Tree 的通用原理在 PG 中的具体实现
LSM-Tree 系列(9 篇) B-Tree vs LSM 的工程取舍在 PG 索引章节中对照
分布式系列(70 篇) 一致性与复制的理论背景,在 PG 流复制/逻辑复制章节引用
操作系统百科(111 篇) fork()、shmem、fsync/fdatasync、信号量——PG 大量使用这些 OS 原语
Linux 内核工程 内核层面的 I/O、内存、进程调度对 PG 性能的影响

如果你没有通读存储工程系列,建议至少阅读以下三篇作为前置背景: - B-Tree 工程实践 - Buffer Pool 设计 - WAL/ARIES 恢复算法

六、承诺与不承诺

承诺

不承诺

同主题继续阅读

把当前热点继续串成多页阅读,而不是停在单篇消费。

2026-06-16 · database / kernel

【PG 内核】逻辑复制与逻辑解码:冲突处理与延迟放大

拆解 PostgreSQL 逻辑复制的完整内核路径:LogicalDecodingContext 从 WAL 解码出逻辑变更的内部流程、Reorder Buffer 按 COMMIT 顺序重排事务与 snapshot 重建机制、pgoutput 输出插件的二进制协议与行过滤变换、Publication/Subscription 模型的内核实现。重点剖析四种冲突类型的根因与修复边界——update_missing/delete_missing 为什么静默跳过而 duplicate_key 直接停摆、subscription 被 disable 后的数据追平策略、序列不在逻辑复制范围内的自增主键冲突陷阱、大事务在 reorder buffer 中的延迟放大效应。

2026-06-16 · database / kernel

【PG 内核】数据恢复与损坏应对:PITR、pg_resetwal 和页面损坏的边界

拆解 PostgreSQL 数据恢复路径的内部机制与操作边界:PITR 的三个关键窗口与 timeline fork 原理、pg_checksums 的校验粒度与盲区、pg_resetwal 的 hint bit 代价与 VACUUM FULL 陷进、pg_dump 并行调度的内部策略。重点在于每种操作做什么、不做什么、哪些后果不可逆。

2026-06-16 · database / kernel

【PG 内核】大版本升级与迁移实战:pg_upgrade --link 为什么快以及为什么没有回滚

拆解 pg_upgrade 的三种模式(--link 硬链接零拷贝、--clone CoW 快照、--copy 物理复制)的执行流程、内部机制和不可回滚的根本原因;逻辑复制跨版本迁移的低停机方案及序列/large object/DDL 三大盲区;四种常见坑的根因与应对;附带迁移方案决策树,从小库到大库选哪种方案一次说清。

2026-06-16 · database / kernel

【PG 内核】MVCC 实现:CLOG、hint bit 与快照可扩展性

在已有 MVCC 文章基础上深入 PG 并发控制的三个基础设施:CLOG 的 SLRU 结构(事务状态位、页面格式、SLRU 淘汰)、hint bit 的写入时机和竞争问题(何时写、谁写、写坏了怎么办)、PG 14 snapshot scalability 优化的具体机制(ProcArrayLock 为什么是瓶颈、xid/xmin 的原子更新如何减少持锁路径),以及事务 ID 回卷(wraparound)的威胁模型。最后与 InnoDB undo log 方案做系统性对比。


By .