第 67 章 数据库物理存储
目录
本章概述 PostgreSQL 数据库所使用的物理存储格式。
67.1. 数据库文件布局 #
本节从文件和目录的层次描述存储格式。
传统上,数据库集簇所使用的配置文件和数据文件都存放在集簇的数据目录中,这个目录通常称为 PGDATA(名字来自可用于定义它的环境变量)。PGDATA 的一个常见位置是
/var/lib/pgsql/data。同一台机器上可以存在多个由不同服务器实例管理的集簇。
PGDATA 目录包含若干子目录和控制文件,如表 67.1 所示。除这些必需项之外,集簇配置文件
postgresql.conf、pg_hba.conf 和
pg_ident.conf 传统上也存放在 PGDATA
中,不过也可以把它们放在其他地方。
表 67.1. PGDATA 的内容
| 项 | 描述 |
|---|---|
PG_VERSION | 包含 PostgreSQL 主版本号的文件 |
base | 包含各数据库子目录的子目录 |
current_logfiles | 记录日志收集器当前正在写入的日志文件的文件 |
global | 包含集簇范围内表的子目录,例如
pg_database |
pg_commit_ts | 包含事务提交时间戳数据的子目录 |
pg_dynshmem | 包含动态共享内存子系统所用文件的子目录 |
pg_logical | 包含逻辑解码状态数据的子目录 |
pg_multixact | 包含多事务状态数据的子目录(用于共享行锁) |
pg_notify | 包含 LISTEN/NOTIFY 状态数据的子目录 |
pg_replslot | 包含复制槽数据的子目录 |
pg_serial | 包含已提交可串行化事务信息的子目录 |
pg_snapshots | 包含导出快照的子目录 |
pg_stat | 包含统计子系统永久文件的子目录 |
pg_stat_tmp | 包含统计子系统临时文件的子目录 |
pg_subtrans | 包含子事务状态数据的子目录 |
pg_tblspc | 包含指向表空间的符号链接的子目录 |
pg_twophase | 包含预备事务状态文件的子目录 |
pg_wal | 包含 WAL(预写式日志)文件的子目录 |
pg_xact | 包含事务提交状态数据的子目录 |
postgresql.auto.conf | 用于存储通过
ALTER SYSTEM 设置的配置参数的文件 |
postmaster.opts | 记录服务器上次启动时所用命令行选项的文件 |
postmaster.pid | 记录当前 postmaster 进程 ID(PID)、集簇数据目录路径、postmaster 启动时间戳、端口号、Unix 域套接字目录路径(在 Windows 上为空)、第一个有效的 listen_address(IP 地址或 *,若未监听 TCP 则为空),以及共享内存段 ID 的锁文件(服务器关闭后该文件不存在) |
对于集簇中的每个数据库,PGDATA/base
中都有一个子目录,其名称是该数据库在 pg_database
中的 OID。这个子目录是该数据库文件的默认位置;特别是,它的系统目录就存放在这里。
每个表和索引都存储在单独的文件中。对于普通关系,这些文件以表或索引的文件节点编号命名,该编号可在
pg_class.relfilenode
中找到。但对于临时关系,文件名的形式为
t,其中 BBB_FFFBBB 是创建该文件的后端的 ID,FFF 是文件节点编号。无论哪种情况,除了主文件(也称主分支)之外,每个表和索引还有一个空闲空间映射(见第 67.3 节),用于存储该关系可用空闲空间的信息。空闲空间映射存放在以文件节点编号加上
_fsm 后缀命名的文件中。表还拥有可见性映射,存放在带有 _vm
后缀的分支中,用于跟踪哪些页面已知不含死元组。可见性映射在第 67.4 节中有进一步说明。不记录 WAL 的表和索引还有第三个分支,称为初始化分支,存放在带有 _init 后缀的分支中(见第 67.5 节)。
小心
请注意,虽然表的文件节点往往与其 OID 相同,但这并非必然如此;有些操作,例如 TRUNCATE、REINDEX、CLUSTER 以及某些形式的
ALTER TABLE,会在保留 OID 的同时改变文件节点。不要假定文件节点和表 OID 一定相同。此外,对于某些系统目录(包括
pg_class 本身),pg_class.relfilenode
中存放的是零。这些目录的实际文件节点编号保存在更底层的数据结构中,可以使用 pg_relation_filenode() 函数取得。
当表或索引超过 1 GB 时,会被划分成若干个 1 GB 大小的段。第一段的文件名与文件节点相同,后续各段则命名为
filenode.1、filenode.2,依此类推。这样的安排避免了在文件大小受限的平台上出现问题。(实际上,1 GB 只是默认段大小。段大小可以通过配置选项
--with-segsize 在构建 PostgreSQL
时调整。)原则上,空闲空间映射和可见性映射分支也可能需要多个段,不过在实践中这不太可能发生。
如果一个表包含可能具有很大条目的列,它就会有关联的
TOAST 表,用于对那些大到无法直接保存在表行中的字段值进行行外存储。pg_class.reltoastrelid
会把该表关联到其 TOAST 表(如果存在的话)。更多信息见第 67.2 节。
表和索引的内容将在第 67.6 节中进一步讨论。
表空间使情况变得更复杂。每个用户定义的表空间都在
PGDATA/pg_tblspc 目录中有一个符号链接,指向其物理表空间目录(即该表空间的 CREATE TABLESPACE
命令中指定的位置)。该符号链接以表空间的 OID 命名。在物理表空间目录中,有一个名称依赖于 PostgreSQL 服务器版本的子目录,例如 PG_9.0_201008051。(使用这个子目录的原因是,为了让数据库的后续版本可以在不发生冲突的情况下复用同一个
CREATE TABLESPACE 位置值。)在这个版本相关的子目录中,对于每个在该表空间中拥有对象的数据库,都有一个以该数据库 OID 命名的子目录。表和索引就使用文件节点命名方案存储在该目录中。pg_default 表空间不通过 pg_tblspc
访问,而是对应于 PGDATA/base。同样,pg_global 表空间也不通过
pg_tblspc 访问,而是对应于
PGDATA/global。
pg_relation_filepath() 函数会显示任意关系的完整路径(相对于 PGDATA)。它常可用作记住上述众多规则的替代方法。但请记住,该函数只给出该关系主分支第一段的名字,若要找出与该关系关联的所有文件,你可能还需要追加段号以及/或者 _fsm、_vm
或 _init。
临时文件(用于诸如排序的数据多于内存可容纳量之类的操作)会创建在
PGDATA/base/pgsql_tmp 中;或者创建在表空间目录下的
pgsql_tmp 子目录中,如果为其指定的是不同于
pg_default 的表空间。临时文件的名称形式为
pgsql_tmp,其中 PPP.NNNPPP 是所属后端的 PID,NNN 用来区分该后端的不同临时文件。