正德厚生,臻于至善

PostgreSQL 逻辑结构

0. 参考图(用户提供)

database cluster
└── databases: database1 / database2 / … / databaseN
    └── database 内部
        ├── tables: table1 / table2 / … / tableN
        ├── indexes: index1 / index2 / … / indexM
        └── other objects: view1 / function1 / sequence1 / …

1. 五层结构总览

Database Cluster(一个 postgres 实例 + $PGDATA 数据目录)
└── Database(N 个,互相隔离的物理目录)
    └── Schema(命名空间:public / pg_catalog / information_schema / 自定义)
        └── Object(Table / Index / View / Sequence / Function / Type / Extension …)
            └── 物理落点:Tablespace → 文件 → 8KB Page → Tuple

2. 逐层详解

2.1 Database Cluster(数据库集群)

  • 一个 PG Cluster = 一个 postgres 实例 + 一个数据目录($PGDATA)。
  • 启动后由一个 postmaster 进程 fork 出若干 backend 进程与辅助进程(checkpointer、writer、walwriter、autovacuum launcher、logical replication launcher …)。
  • 内部可挂 N 个独立 database。
  • 注意:PG 的 “cluster” 不是 Oracle RAC 那种多实例共享存储;它只是”单实例管多个库”的逻辑集合。多机高可用走流复制 / 逻辑复制 / Citus 等方案。

Oracle 对照:≈ Instance + Database 的合体,但里面塞了 N 个互不相干的库。


2.2 Database(数据库)

  • 物理上是 $PGDATA/base/<dboid>/ 下的一个子目录。
  • 库与库之间完全隔离:
    • 不能跨库 JOIN;
    • 不能跨库引用 schema / 表 / 视图;
    • 跨库只能走 dblink、postgres_fdw,或逻辑复制把数据搬过来。
  • 自带两个模板库:
    • template0:干净的模板;
    • template1:建库时默认克隆,你装在 template1 里的扩展会被新库继承。

Oracle 对照:Oracle 一个 Database 里用 schema/user 隔对象;PG 把”隔对象”往上抬了一层——先隔库,库内再用 schema 隔。


2.3 Schema(模式)

  • database 内的命名空间,用于把同类对象分组,避免同名冲突。
  • 默认 schema:
    • public:建库后给用户用的默认 schema(PG15 起收回了 PUBLIC 的 CREATE 权限);
    • pg_catalog:系统表所在的 schema(自动在 search_path 最前面);
    • information_schema:SQL 标准视图层。
  • 对象解析顺序由 search_path 决定,典型值:"$user", public。

Oracle 对照:≈ User/Schema,但 PG 里 schema 和 role 是解耦的——一个 role 可以用任意 schema,不像 Oracle user=schema 绑定。


2.4 Schema 内的对象

对象说明Oracle 对应
Table默认是堆表(heap-organized),无索引组织表;可用 CLUSTER 或 BRIN 做物理有序Heap Table / IOT
Index类型丰富:B-tree(默认)、Hash、GIN、GiST、BRIN、SP-GiSTIndex(类型少)
View / Materialized View普通视图 + 物化视图View / Materialized View
Sequence序列对象Sequence
Function / ProcedurePG11+ 原生支持 PROCEDURE(带事务)Function / Procedure
Trigger / Rule行级 / 语句级触发器Trigger
Type / Domain / Composite Type自定义类型、域、复合类型Object Type / VARRAY
Extension一键打包安装的扩展(postgis、pg_stat_statements、uuid-ossp …)类似外部组件 / 包
TOAST 表大字段(>~2KB)自动拆到 pg_toast schema 下行外压缩存储LOB 段

2.5 系统目录(元数据本身也是对象)

元数据存在 pg_catalog schema 中,DBA 最常用的几张:

系统表作用
pg_class所有表 / 索引 / 视图 / 序列的元数据(relkind 区分类型)
pg_namespaceschema 字典
pg_attribute列字典
pg_database数据库字典
pg_tablespace表空间字典
pg_index索引元数据
pg_statistic统计信息(优化器用)

每个对象有一个 OID,是它在系统里的身份证。


3. 逻辑 → 物理的桥

  • Tablespace(表空间):集群级对象,一个表空间可以被多个 database 共用(和 Oracle 表空间属于单个 database 不一样)。
  • 一个表 = 堆表 +(可选)TOAST 表 + 索引表,各自在 pg_class 里是一行。
  • 行(Tuple)物理结构:4 字节元组头 + 空值位图 + 字段值 + 隐藏系统列 xmin / xmax
  • 默认页大小 8KB(编译时可改),对应 Oracle 的 Block。

4. 深入一:MVCC 与 VACUUM

4.1 为什么需要 MVCC

  • 读写不互相阻塞:读的人看到一致性快照,写的人只锁自己要改的行。
  • 实现方式:不在原地改数据,而是保留多版本。

4.2 两个隐藏系统列

  • xmin:插入这一行(版本)的事务 ID。
  • xmax:删除 / 更新这一行(版本)的事务 ID;未删除时为无效值。

4.3 UPDATE 的真实动作

PG 的 UPDATE 不是原地更新:

  1. 把旧 tuple 标记为 dead:xmax = 当前事务 ID;
  2. 插入一条新 tuple:xmin = 当前事务 ID;
  3. 旧版本仍留在堆表里,只是对不兼容快照的事务不可见。

→ 这就是 PG 表膨胀(bloat)的根因。

4.4 快照(Snapshot)如何决定可见性

一个事务启动时拿到一个快照,里面记录了:

  • 当时活跃的事务 ID 列表;
  • 上一个完成的事务 ID(xmax)。

可见性规则(简化):

  • xmin 已提交,且 xmin 在快照启动前已结束 → 这行版本是”插入”状态;
  • xmax 为空,或 xmax 还没提交,或 xmax 在快照启动时还在运行 → 这行版本对当前事务仍可见;
  • 否则不可见。

4.5 VACUUM 在干什么

命令作用锁是否收缩文件
VACUUM扫描表,把 dead tuple 标记为可复用不阻塞 DML否(只标记)
VACUUM FULL整表重写,物理收缩AccessExclusiveLock是(生产慎用)
VACUUM ANALYZE清理 + 重新收集统计信息不阻塞 DML否
autovacuum后台自动调度,基于阈值自动跑不阻塞 DML否

4.6 事务 ID 回卷(Wraparound)

  • 老 PG 用 32 位 xid,循环使用;
  • 当一个 tuple 太老、xmin 被”绕回去”时,会发生事务 ID 回卷,数据库强制停机;
  • Freezing 机制:VACUUM 会把足够老的 tuple 标记为 frozen(xmin = 2),不再受 xid 影响;
  • PG13+ 引入 64 号 epoch 缓解问题,但 freezing 仍是日常运维主线。

4.7 与 Oracle 的关键差异

维度PostgreSQLOracle
旧版本存哪留在堆表里(dead tuple)Undo Tablespace
写是否阻塞读不阻塞(MVCC)不阻塞(一致性读)
读是否阻塞写不阻塞不阻塞
回收方式VACUUM / autovacuumUndo 自动回收
膨胀来源dead tuple 堆积行迁移 / 链

5. 深入二:TOAST(大字段存储)

5.1 触发条件

  • PG 默认页大小 8KB,一个 tuple 不能跨页(必须放得进一个页);
  • 当一行所有字段加起来超过 TOAST_TUPLE_THRESHOLD(默认 2KB),触发 TOAST。

5.2 四种存储策略

策略压缩行外存储说明
PLAIN否否不压缩不外联(适合定长、短字段)
EXTERNAL否是外联不压缩(适合读多的大字段)
EXTENDED是是先压缩再外联(默认)
MAIN是尽量不先压缩,尽量留在主表

5.3 物理结构

  • 主表行里大字段变成一个 TOAST pointer(指针);
  • 真实数据被切成约 2KB 一块的 chunks,存到 pg_toast.pg_toast_<tableoid> 表;
  • 每个 chunk 是一行,通过 chunk_id + chunk_seq 排序还原。

5.4 与 Oracle LOB 对比

  • Oracle:LOB 需要显式建 LOB 段、指定 SEGMENT / CACHE / NOCACHE;
  • PG:透明处理,建表时不用操心,字段自动按策略 TOAST。

6. 深入三:声明式分区(PG10+)

6.1 分区方式

CREATE TABLE orders (
    id bigint,
    created_at date,
    region text,
    amount numeric
) PARTITION BY RANGE (created_at);   -- RANGE / LIST / HASH
  • RANGE:按区间(按月、按年);
  • LIST:按枚举值(按 region);
  • HASH:按哈希取模(打散热点)。

6.2 物理结构

  • 分区表本身是逻辑容器,不存数据;
  • 每个分区是一个独立的子表(继承关系),数据真实落在分区里;
  • 多级分区:子分区上还可以再 PARTITION BY。

6.3 关键优化:分区裁剪(Partition Pruning)

  • 优化器在生成执行计划时,根据 WHERE 条件排除不相关分区;
  • 只扫描命中的分区,而不是全表;
  • 例:WHERE created_at = '2026-10-01' 只扫 10 月分区。

6.4 版本演进要点

  • PG10:声明式分区初版;
  • PG11:支持 DEFAULT 分区、在线加分区、分区表上的 PRIMARY KEY(含分区键);
  • PG12:分区表上的外键、分区索引;
  • PG13+:增量分区、更多裁剪优化。

6.5 与 Oracle 分区对比

维度PostgreSQLOracle
分区方式RANGE / LIST / HASHRANGE / LIST / HASH / RANGE-LIST …
分区表是否存数据否(纯逻辑容器)否(类似)
全局索引不支持原生全局索引支持全局索引
分区裁剪优化器自动优化器自动

7. 与 Oracle 的关键差异 Checklist(DBA 视角)

  • 实例 : 库 = 1 : N(PG)vs 1 : 1(Oracle)——多业务隔离优先用 schema 还是独立 database,要想清楚。
  • schema ≠ role(PG)vs user = schema(Oracle)——权限模型要重新适应。
  • 跨库查询必须走 FDW / DBLINK,不像 Oracle 一个库内就能跨 schema 随便查。
  • MVCC 不用 undo 表空间,旧版本留在堆里,VACUUM / autovacuum 是日常运维主线。
  • 没有 extent / 段级管理,表膨胀靠统计信息 + vacuum 治理,不是靠 allocate / deallocate 区间。
  • TOAST 透明处理大字段,不用像 Oracle 那样显式建 LOB 段。
  • 表空间是集群级,可跨库共用;Oracle 表空间属于单个 database。
  • 事务 ID 回卷风险,必须监控 freezing 进度,不能放任 autovacuum 躺平。
  • 无 IOT,物理有序只能靠 CLUSTER(一次性重写)或 BRIN 索引。
  • 跨库 JOIN 性能差(走 FDW),大表关联尽量在同一 database 内完成。

8. 常用系统目录速查 SQL

-- 查看当前 database 下所有 schema
SELECT schema_name FROM information_schema.schemata;

-- 查看某张表的 OID、类型、表空间
SELECT relname, relkind, reltablespace, reltuples
FROM pg_class
WHERE relname = 'orders';

-- 查看表的列
SELECT attname, format_type(atttypid, atttypmod) AS type
FROM pg_attribute
WHERE attrelid = 'orders'::regclass AND attnum > 0;

-- 查看当前 search_path
SHOW search_path;

-- 查看表是否膨胀(粗略)
SELECT relname, n_live_tup, n_dead_tup,
       round(n_dead_tup::numeric / NULLIF(n_live_tup, 0) * 100, 2) AS dead_pct
FROM pg_stat_user_tables
ORDER BY n_dead_tup DESC;

-- 查看 autovacuum 配置
SHOW autovacuum;
SHOW autovacuum_vacuum_threshold;
SHOW autovacuum_analyze_threshold;

9. 一页纸总结

Cluster 管 N 个 Database,Database 内用 Schema 隔对象,Schema 内放 Table/Index/View/Function/Extension 等;数据最终落在 8KB Page 里,每行带 xmin/xmax 两个隐藏系统列支撑 MVCC,旧版本靠 VACUUM 回收;大字段自动 TOAST 行外压缩;大表用声明式分区做裁剪。和 Oracle 最大的认知差:PG 是”一实例多库、库间隔离、schema 与 role 解耦、MVCC 留在堆里”。

赞(0) 打赏
未经允许不得转载:徐万新之路 » PostgreSQL 逻辑结构

支持快讯、专题、百度收录推送、人机验证、多级分类筛选器,适用于垂直站点、科技博客、个人站,扁平化设计、简洁白色、超多功能配置、会员中心、直达链接、文章图片弹窗、自动缩略图等...

联系我们

觉得文章有用就打赏一下文章作者

非常感谢你的打赏,我们将继续提供更多优质内容,让我们一起创建更加美好的网络世界!

支付宝扫一扫

微信扫一扫

登录

找回密码

注册