深入 Postgres 内部:数据库集群、数据库与表的存储机制

查看原文 HN 讨论

文章摘要

这篇文章带读者深入 Postgres 的底层存储,讲清楚「数据库集群(database cluster)、数据库、表」在磁盘上究竟是怎么组织的。首先厘清概念:Postgres 里的「数据库集群」指的是由单个服务器实例管理的多个数据库,并非分布式的多台服务器。每个数据库是表、索引、视图等对象的集合,对象由 OID(object identifier,无符号整数) 唯一标识;系统目录 pg_database 记录集群内所有数据库,pg_class 存放表和索引、pg_index 存放索引元数据——这些系统目录本身就是普通表。

在物理布局上,一切都存放于 $PGDATA 目录下:base/ 为每个数据库建一个以 OID 命名的子目录,global/ 存放集群级目录,pg_wal/ 存放预写日志(WAL),pg_tblspc/ 存放表空间符号链接。每个数据库目录内的文件以 relfilenode(物理文件标识符)命名。文章特别辨析了 OID 与 relfilenode 的区别:二者初始相同,但 OID 是永久标识,relfilenode 在 VACUUM FULL 重写数据到新文件时会改变。表空间(tablespace)则是命名的存储位置,允许管理员把数据分散到多块磁盘以优化性能。

文章还讲解了堆表(heap table)的页结构:表由 8KB 的页组成,每页含页头(记录空闲空间与校验和)、行指针(line pointer,从页头向后增长)、以及元组(tuple,从页尾向前存储),中间留出「空闲空间空洞」。对于超过约 2KB 的超大字段,Postgres 通过 TOAST 机制自动建立独立的 TOAST 表,把压缩后的数据分块存储、主表只留指针,从而避免元组跨页溢出,且对查询透明。读取时借助 B-tree 索引高效定位,必要时执行顺序扫描。

HN 评论精华

该帖讨论较少,但整体评价正面,读者普遍欣赏这类深入底层的科普。