70.1. 数据库文件布局

本节在文件和目录的层次上描述存储格式。

在传统上,数据库集簇所使用的配置和数据文件都被一起存储在集簇的数据目录里, 通常用PGDATA来引用(用的是可以定义它的环境变量的名字)。PGDATA的一个常见位置是/var/lib/pgsql/data。由不同数据库实例所管理的多个集簇可以在同一台机器上共存。

PGDATA目录包含几个子目录以及一些控制文件, 如表 70.1所示。除了这些必要的东西之外,集簇的配置文件postgresql.confpg_hba.confpg_ident.conf通常都存储在PGDATA中,不过可以把它们放在别的地方。

表 70.1. PGDATA的内容

描述
PG_VERSION一个包含PostgreSQL主版本号的文件
base包含每个数据库对应的子目录的子目录
current_logfiles记录当前被日志收集器写入的日志文件的文件
global包含集簇范围的表的子目录,比如pg_database
pg_commit_ts包含事务提交时间戳数据的子目录
pg_dynshmem包含被动态共享内存子系统所使用的文件的子目录
pg_logical包含用于逻辑复制的状态数据的子目录
pg_multixact包含多事务(multi-transaction)状态数据的子目录(用于共享的行锁)
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。这个子目录是该数据库文件的缺省位置;特别值得一提的是,该数据库的系统目录存储在此。

注意后续章节描述了内置的heap 表访问方法的行为,和内置 索引访问方法。 由于PostgreSQL的可扩展特性,其他访问方法的工作方式可能不同。

每个表和索引都存储在独立的文件里。对于普通关系,这些文件以表或索引的filenode号命名,它可以在pg_class.relfilenode中找到。但是对于临时关系,文件名的形式为tBBB_FFF,其中BBB是创建该文件的后台的后台ID,FFF是文件节点号。在每种情况下,在主文件(a/k/a 主分支)之外,每个表和索引有一个空闲空间映射(见第 70.3 节),它存储关系中可用空闲空间的信息。空闲空间映射存储在一个文件中,该文件以节点号加上后缀_fsm命名。表还有一个可见性映射,存储在一个后缀为_vm的分支中,它用于跟踪哪些页面已知含有非死亡元组。可见性映射将在第 70.4 节中进一步描述。不被日志记录的表和索引还有第三个分支,即初始化分支,它存储在后缀为_init的分支中(见第 70.5 节)。

小心

请注意,虽然一个表的文件节点通常和它的 OID 相匹配,但实际上并必须如此; 有些操作,比如 TRUNCATEREINDEXCLUSTER以及某些形式的ALTER TABLE,都可以改变文件节点而同时保留 OID。我们不应该假设文件节点和表 OID 相同。此外,对于包含pg_class本身在内的特定系统目录,其pg_class.relfilenode包含0。这些目录的实际文件节点号被存储在一个低层数据结构中,并且可以使用pg_relation_filenode()函数获取。

在表或者索引超过 1GB 之后,它就被划分成1G大小的。 第一个段的文件名和文件节点相同;随后的段被命名为 filenode.1、filenode.2等等。这样的安排避免了在某些有文件大小限制的平台上的问题(实际上,1GB只是默认的段尺寸。段尺寸可以在编译PostgreSQL时使用配置选项--with-segsize进行调整)。原则上,空闲空间映射和可见性映射分支也可以要求多个段,但实际上这很少发生。

如果一个表的列中可能存储相当大的项,那么该表就会有个与之相关联的TOAST表, 它用于存储无法保留在在表行中的域值的线外存储。如果表有TOAST表,该表的pg_class.reltoastrelid链接到它的TOAST表。 参阅第 70.2 节获取更多信息。

表和索引的内容在第 70.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中,如果临时文件被指定在一个非pg_default表空间中则它们会被创建在该表空间的pgsql_tmp子目录中。临时文件的名称的形式为pgsql_tmpPPP.NNN,其中PPP是其所属后端的PID,而NNN用于区别该后端的不同临时文件。