\dtS, \dS

关系型数据库(RDBMS)有两个基本的问题: 1.对象如何被表示? 2.系统如何找到它们?

每一种对象由系统目录中的一行或多行表示

  • pg_databse: 一行代表一个数据库 oid, datname, datdba, datacl[], encoding, ...
  • pg_namespace: 一行代表一个schema oid, nspname, nspowner, nspacl[]
  • pg_tablespace: 一行代表一个表空间 oid, spcname, spcowner, spcacl[]

对于其他更具体的对象也是如此:

  • pg_class: 其中的每一行代表一个关系 (Relation),这包括了表、视图、索引、序列等。这是最核心的目录之一。
  • pg_attribute: 其中的每一行代表一个列 (Attribute),并指向它所属的表。
  • pg_proc: 其中的每一行代表一个函数或存储过程
  • pg_constraint: 其中的每一行代表一个约束

Role=Privileges/Grantor jas=arwdRxt/jas,fred=r/jas,joe=rwad/jas

  • Role (角色/被授权者): 指的是权限被授予了。如果这个部分是空的,则代表授予了 PUBLIC(即所有角色)。
  • Privileges (权限): 指授予了哪些具体的操作权限。它由一系列单字符代码组成。
  • Grantor (授权者): 指这个权限是由授予的。 a(增), r(读), w(改), d(删), R(规则), x(引用), t(触发器)

关键点:当你执行 CREATE TABLE my_table ... 时,PostgreSQL 实际上就是在 pg_class 表里插入了一行来记录 my_table 的存在,并在 pg_attribute 表里插入了多行来记录它的所有列。每个对象都有一个独一无二的 OID (Object Identifier),作为它在整个数据库系统中的内部“身份证号”。

Global db: catalog tables defining: databases, users

Local db: schemas, tables, attributes, functions, types

一个表的所有信息不都只在一个表里,pg_class是主入口,记录主要信息,pg_attribute记录详细信息,pg_constraint记录数据的约束规则。

pg_class除了普通表,还有

  • Views (视图): 本质是一个存储起来的查询,但可以像表一样被查询。
  • Composite (tuple) types (复合类型): 用户自定义的一种数据类型,其结构就像一个表的行结构。实际上,每创建一个表,PostgreSQL 都会自动为其创建一个同名的复合类型。
  • Sequences (序列): 用于生成唯一序列号的对象,常用于自增主键。
  • Indexes (索引): 用于加速查询的数据结构,它本身也有自己的物理存储,可以被看作一种特殊的关系。
  • 其他特殊对象: 如物化视图(Materialized Views)、外部表(Foreign Tables)等。

pg_class 表中一些非常重要的列:

  • oid: 对象标识符 (Object Identifier)。一个在整个数据库中唯一的数字,是这个关系对象的内部主键。
  • relname: 关系名称 (Relation Name)。就是我们平时使用的表名、视图名等,例如 'employees'
  • relnamespace: 关系所属的命名空间 (Relation Namespace)。它存储的是该关系所属的 Schema (模式) 的 oid
  • reltype: 关系类型 (Relation Type)。它存储的是定义此关系行结构的复合类型的 oid
  • relowner: 关系所有者 (Relation Owner)。存储拥有此关系的角色(用户)的 oid
  • relkind: 关系种类 (Relation Kind)。这是一个极其重要的字段,用一个单字符来区分这一行记录到底是什么类型的对象。
    • r = 普通表 (regular relation/table)
    • i = 索引 (index)
    • S = 序列 (Sequence)
    • v = 视图 (view)
    • c = 复合类型 (composite type)
    • f = 外部表 (foreign table)
    • m = 物化视图 (materialized view)
  • reltuples: 关系元组数 (Relation Tuples)。这是一个估算值,表示表中的行数。查询优化器会用它来制定执行计划。
  • relnatts: 关系属性数 (Relation Number of Attributes)。表示这个关系(主要是表)有多少个列。
  • relhaspkey: 关系有主键 (Relation Has Primary Key)。一个布尔值,表示这个表是否有主键。
  • relacl: 关系访问控制列表 (Relation Access Control List)。就是我们之前讨论过的,存储该对象权限信息的数组。

2. 从名称到磁盘字节的路径是怎样的? (How to go from a name to bytes?)

这是数据库工作的核心路径。我们可以用一个去图书馆找书的比喻来理解这个过程。

你的目标: SELECT * FROM public.employees; (找到 “public部门” 的 “employees” 这本书的所有内容)

  1. 解析请求 (Parsing)
    • 你向图书管理员(PostgreSQL解析器)提出请求。管理员首先确认你的请求符合语法规则。
  2. 查找目录卡片 (Name Resolution / Catalog Lookup)
    • 管理员(系统)会去查询“目录卡片”(系统目录)。
    • 首先,它查阅 pg_namespace 找到名为 public 的部门(Schema)的 OID。
    • 然后,它查阅 pg_class 这本更核心的目录,寻找 relname (关系名) 等于 employees 并且 relnamespace (所属schema的OID) 与 public 的OID相匹配的那一行。
  3. 定位物理位置 (Find Physical Location)
    • pg_class 中找到 employees 表对应的那一行后,管理员就获得了这本书的关键物理信息:
      • reltablespace: 书所在的馆区(表空间),比如“主馆区”或“南区SSD馆”。这决定了文件存储的根目录。
      • relfilenode: 书在馆区里的唯一书架编号。这个编号直接对应磁盘上的一个文件名。
    • 至此,PostgreSQL已经知道了要找的数据存储在哪个目录下的哪个文件里。
  4. 取回数据页 (Buffer Manager)
    • 现在,图书管理员派一个助手(缓冲区管理器 Buffer Manager)去取书。
    • 助手会先看看“常用书手推车”(共享内存缓冲区 Shared Buffers)上有没有这本书的某一页。如果有,就直接从内存里拿,非常快。
    • 如果内存里没有,助手才会去那个物理书架(磁盘 Disk)上把对应的数据页取出来,并放到手推车上以备后续使用。
  5. 解读内容 (Tuple Access)
    • 助手把数据页(书的一页)拿回来后,图书管理员(访问方法 Heap Access Method)知道这本书的排版格式,它懂得如何从这一页中精确地读出每一行字(元组 Tuple),并检查这行字你是否有权限看(MVCC可见性检查),最后把内容呈现给你。

总结:这个过程是一个从逻辑名称到物理地址的层层解析。数据库通过查询它自身的元数据(系统目录),将一个我们能理解的表名(如 employees),一步步翻译成磁盘上某个文件的某个偏移量,最终将字节流读取到内存并解析成我们需要的行数据。

总结

Pg的核心设计哲学是一切皆为表。不仅用户数据存储在表中,所有关于数据库自身的元数据(关于表、数据库、函数、权限等所有信息)都在特殊的系统表中。