postgreSQL系统表分析
1 | test=# select pg_relation_filenode('pg_attribute'); |
这个命令可以找到表文件在哪里存着
转载自
前言
系统表是整个 PostgreSQL 数据库存储体系中最重要的一部分数据,它们用来组织管理PostgreSQL 的数据空间,将用户自己定义的数据集合更好得以一个或者多个表组织起来。它们本质也是一个个表对象,相比于普通表是存储的元数据。
这里的元数据可以理解为描述数据的数据。比如,用户创建的表有
(c1 int, c2 text)
两种列类型,这一些类型
int, text
会被单独存放在
pg_type
的系统表中,同时 c1, c2
列名字则会被存放在 pg_attribute
的系统表中,并和 pg_type
形成关联。这一些 pg_type
, pg_attribute
类型的表可以建立对用户表的关系描述,所以它们可以被称为元数据。
OID
Oid 在 PostgreSQL 中被用来描述一个个数据表的逻辑对象,比如
Relation
, type
, attr
,
namespace
等等,每创建一个对象都会为其分配一个属于自己的标识(Oid)。PG
也会通过 Oid 来在不同的数据表之间建立关联,也就是说有一些
对象是全局唯一的(pg_class 表的oid)。但是因为 Oid 是
unsigned int
,所以当对象的数量超过42亿之后可能会有回卷,所以PG
对Oid的划分有一些自己的定义,比如预留16383 个Oid
作为全局唯一的对象标识,其他的都是给用户表使用,允许发生回卷。
1 | postgres=# select oid,relname,relnamespace from pg_class where relname='pg_class'; |
可以看到student表的OID和存储tuple的文件名是一样的
接下来看看 PostgreSQL 内部非常重要的一些系统表,以及它们之间的关系模型,从而更好的帮助我们理解创建的一个用户表是如何被组织管理的。
pg_database
1 | postgres=# select oid,datname,datdba,encoding from pg_database; |
pg_database系统表用来管理一个database的对象属性
pg_class
pg_class
系统表用来管理一个表的对象属性,就是存储在当前数据库的所有表在此时的固有属性信息都会被统一放在pg_class
系统表中,直接看其列属性的定义 pg_class.h
:
因为过多,简单挑几个关键信息如下
1 | // pg_class 的唯一标识 |
可以看到通过 create type map as (string varchar, int_1
int); create table map_test (id int, value
map); 创建的表在 pg_class 中存储的属性信息 有两个,一个是 类型
map
的属性信息, 一个是表
map_test
的属性信息。
1 | -- 复合类型 map 的属性信息 |
当然,pg_class 本身也是一个对象,所以在 pg_class 中也会存储自己的对象属性信息。
1 | postgres=# select oid,relname,relnamespace,reltype,relam,relfilenode,relpersistence,relkind,relnatts from pg_class where relname='pg_class'; |
pg_type
该系统表用于记录管理所有的类型定义,比如上面的
create table map_test (id int, value map);
建表过程中用到的类型 int
以及 复合类型 map
都会被存储到 pg_type
中,而列名字 id
以及
value
则会被存储到的 pg_attribute
系统表中,这个后面会说。
PG 通过 pg_class
的对象属性描述的系统表 以及
pg_type
和 pg_attribute
两种对列属性描述的系统表 共同构造一个基本表的列信息。
接下来看看pg_type 的定义
pg_type.h
,挑选几个简略定义如下:
1 | // pg_type的 固有对象 标识是1247 |
比如对于我们前面通过
create type map as (string varchar, int_1 int);
创建的类型,可以从 pg_type中看到其信息如下:
1 | postgres=# select oid,typname,typlen,typtype from pg_type where typname='map'; |
因为 map
是我们自己创建的类型,其在 PG 内部的Oid 会从
16384 之后开始生成,标识其属于用户对象。 对于普通的
int
类型,其在数据库初始化的时候 以及在 pg_type
中预先创建好了,且 Oid也是提前分配好的,保证全局唯一:
1 | postgres=# select oid,typname,typlen,typtype from pg_type where typname='int4'; |
pg_attribute
这个系统表描述的是一个表(对象)的每一个列属性的定义。在
pg_class中我们看到的是这个表对象的列的个数,但是具体每一个列
都是什么类型,名字是什么,长度是多少,是第几列等这样的列的描述信息则是会存储在
pg_attribute 系统表中。
其基本类型定义如下pg_attribute.h
:
1 | CATALOG(pg_attribute,1249 |
比如我们查看 前面创建的 test_map
表的列描述信息如下:
1 | postgres=# select oid from pg_class where relname='map_test'; |
可以看到 pg_attribute 还为 map_test
默认分配了一些默认不可见的属性列,用作 extension
时查看更细粒度的tuple信息。 用户自己创建的两列 id
和
value
则是有自己的typeid信息,可以从 pg_type
中看到其定义。
系统表关系
接下来还是通过上面两个简单语句:
1 | create type map as (string varchar, int_1 int); |
看看最后创建的 map_test
表信息
是如何由不同的系统表中的数据共同描述的。

如上图,描述了整个创建过程中涉及到的
系统表信息(并不全面),主要的几个系统表如上。 当我们执行第一条语句
create type map as (string varchar, int_1 int);
按照上图给出的系统表,发生的事情如下: - 在pg_attribute
增加map的两个列属性,一个是 string,一个是int_1,并标识各自的 pg_type
relid;创建好的 string和int_1 行各自的 attrelid
都会保存下来,用于指向pg_class 中的 map 对象对应的 oid。 - 在 pg_type
中创建 map类型,因为其是由两个基本类型 int4
和
varchar
组合而成,所以其类型是组合类型。其
typerelid
也是指向 pg_class 中 map 对象的 oid。 -
在pg_class中创建一行信息, 记录其指向的 pg_type 中的行oid 以及所属的
namespace信息。因为 map对象是
type类型,并不是relation,所以其内部不需要存储数据,也就不需要relam
的智齿了。
当我们执行第二条语句
create table map_test (id int, value map);
就是继续在
pg_type 以及 pg_attribute 系统表中添加对应的行。 - 因为复合类型
map已经存在,则 map_test 表中的行只需要增加对应的
pg_attribute信息了,不需要额外创建pg_type行。增加的 id
和
value
行只需要让 atttypid
指定
pg_type中的类型即可,id 是基本类型,value 则是已经创建好的复合类型 map。
- pg_class 中增加属于
map_test
的行。因为其拥有符合类型的列,且是是一个普通表;所以拥有
toast 以及 am。
可以看到创建表的过程中需要有较多的系统表的读写,上图仅仅展示了写入的系统表 以及很小部分需要读区的系统表,实际执行 SQL 语句的过程中会有更多的对系统表的访问。
接下来我们看看 系统表的初始化以及访问链路。
pg_filenode.map
1 |
|