Radim Marek:Postgres 关系文件,归档于 base/ 目录下

Radim Marek:Postgres 关系文件,归档于 base/ 目录下

💡 原文英文,约2800词,阅读约需11分钟。
📝

内容提要

本文讲解PostgreSQL表在磁盘上的文件组织。通过pg_relation_filepath可由表名获取文件路径,路径包含数据库OID与relfilenode。每个表、索引、序列各占一个文件,带主键的表还会生成TOAST表及其索引。每个关系有main、fsm、vm、init四种fork。OID是关系的永久标识,relfilenode是文件名,VACUUM FULL、TRUNCATE等重写操作会更换relfilenode。文中还说明了表空间、1GB分段文件以及缓冲区与磁盘字节的一致性。

🔎

延伸解读

OID 与 relfilenode:别混淆身份与文件名

OID 是关系的永久标识,所有目录引用都指向它,永不改变;relfilenode 只是磁盘上的文件名。CREATE TABLE 时两者通常相同,但 VACUUM FULL、TRUNCATE、CLUSTER、REINDEX 等重写操作会生成新文件并更换 relfilenode,而 OID 保持不变。因此,手动用 pg_class.oid 拼接路径的脚本在重写后会失效,应使用 pg_relation_filepath() 获取路径,用 pg_filenode_relation() 反查关系名。

TOAST 表与索引:主键和变长列带来的额外文件

创建带主键和 text 列的表时,PostgreSQL 会自动生成 TOAST 表及其索引,用于存储超长值。这些是普通关系,拥有自己的 relfilenode 和文件,与主表并列。例如,一个 CREATE TABLE 可能产生五个关系文件:主表、主键索引、序列、TOAST 堆和 TOAST 索引。pg_table_size 涵盖堆和 TOAST 关系,pg_indexes_size 涵盖索引,而序列不计入其中。

四种 fork:主文件之外的辅助文件

每个关系有 main、fsm、vm、init 四种 fork,共享 relfilenode 但后缀不同。main 是实际数据页;fsm 是空闲空间映射,帮助 INSERT 快速找到有空间的页;vm 是可见性映射,标记全可见和全冻结页以加速扫描和 VACUUM;init 仅用于无日志表,是崩溃后重置的初始状态。索引可以有 fsm 但不会有 vm,因为可见性是堆的概念。

1GB 分段:大表的文件拆分规则

单个关系文件默认上限为 1GB(131072 个 8KB 页)。超过后,PostgreSQL 会创建同名但带 .1、.2 等后缀的分段文件。pg_relation_filepath() 仍返回基础名,存储管理器根据块号计算实际文件。块号连续,不感知分段:第 131072 块就是 .1 文件的第一页。pg_relation_size 会汇总所有分段。

❓

Q&A

PostgreSQL 中如何根据表名找到它在磁盘上的文件路径?

使用 pg_relation_filepath('表名') 函数,它返回相对于数据目录的路径,例如 base/5/16431。路径包含三部分:base 是默认表空间目录,5 是数据库 OID,16431 是 relfilenode(文件名)。

PostgreSQL 的 relfilenode 和 OID 有什么区别?

OID 是关系的永久标识,不会改变;relfilenode 是磁盘上的文件名。大多数情况下两者相等,但执行 VACUUM FULL、TRUNCATE 等重写操作后,relfilenode 会改变,而 OID 保持不变。

PostgreSQL 中一个表会生成哪些额外的文件?

除了主文件,带主键的表还会生成 TOAST 表及其索引文件。每个关系有 main、fsm、vm、init 四种 fork:main 是数据页,fsm 是空闲空间映射,vm 是可见性映射,init 用于未日志表崩溃恢复。

PostgreSQL 的 fsm 和 vm 文件分别有什么作用?

fsm 是空闲空间映射,记录每个堆页的剩余空间,帮助 INSERT 快速找到可用页;vm 是可见性映射,每页两位,标记全可见和全冻结,用于跳过可见性检查和 VACUUM 回卷保护。

PostgreSQL 中 VACUUM FULL 后为什么 relfilenode 会变?

VACUUM FULL 会重写表,将紧凑副本写入新文件,然后更新目录指向新文件。OID 不变,但 relfilenode 变为新文件的编号。旧文件在下一个检查点前仍存在,之后被删除。

PostgreSQL 的表空间在磁盘上如何组织?

表空间是数据目录外的另一个目录,内部布局与 base 相同。pg_tblspc 下每个表空间有一个以 OID 命名的符号链接,指向实际位置。链接下是 PG_版本号/数据库OID/relfilenode 的路径。

PostgreSQL 中为什么表文件会分成多个段?

单个文件默认上限为 1GB,当关系超过此大小时,PostgreSQL 会创建带 .1、.2 等后缀的段文件,每个段包含 131072 个 8KB 页。这是为了兼容有文件大小限制的平台。

🏷️

标签

➡️

继续阅读