从pg_depend和pg_class开始了解MogDB/openGauss/postgresql的系统元数据设计

前言

学习任何一种数据库,必须要了解它的数据字典,这样有利于了解数据库的结构、解读部分日志、定位一些问题。PG/OG系数据库的系统元数据遵从一个统一的设计规则,可以让初学者快速入门。本文以MogDB为例,剖析一下PG/OG系数据库的系统元数据设计哲学。

一、pg_depend

先来看pg_depend的表结构

名称 类型 引用 描述
classid oid PG_CLASS.oid 有依赖对象所在系统表的OID。
objid oid 任意OID属性 指定的依赖对象的OID。
objsubid integer - 对于表字段,这个是该字段的字段号(objid和classid引用表本身)。对于所有其它对象类型,目前这个字段是零。
refclassid oid PG_CLASS.oid 被引用对象所在的系统表的OID。
refobjid oid 任意OID属性 指定的被引用对象的OID。
refobjsubid integer - 对于表字段,这个是该字段的字段号(refobjid和refclassid引用表本身)。对于所有其它对象类型,目前这个字段是零。
deptype "char" - 一个定义这个依赖关系特定语义的代码。
n-不加cascade不删除,加cascade删除
a-加不加cascade都自动删除
i-加不加cascade都报错
e-表示是从插件创建的,不能删
p-初始创建的,objid都是0,只有refobj,也不能删

该表描述对象与对象之间的依赖关系,删除objid时,判断是否存在refobjid,并根据deptype的类型,来看此时应该进行的操作,主要为依赖的强弱性来看删除时是否要加cascade。

从这个表上可以看出,在PG/OG/MogDB中,对于对象的唯一性,是用classid+objid两个值来决定的,我们可以理解为java上的类和对象的概念,即先有类,然后基于类来创建对象。

原生PG在设计的时候,期望保持oid唯一(在一个db内),即一个oid就对应一个明确的对象。但按照这个结构发展这么多年,到了GaussDB后,某次增加一个函数的时候,错误地使用了已经在操作符里被用过的oid,导致后续OG系只能按照classid+objid两个信息来确定对象唯一性了,当然这并没有从内核上打破classid+objid唯一这个规则。有兴趣的可以比较一下这条SQL在PG和OG的输出差异

select refobjid,count(1) from
(select distinct refclassid,refobjid from pg_depend) 
group by refobjid having count(1)>1

classid均来自于pg_class.oid,我们先观察一下classid中的这些oid分别对应什么

image-xlch.png

pg_shdependpg_depend类似,但区别在于,pg_shdepend记录的是全局共享的依赖关系,是跨库的,比如用户和角色(pg_authid);而pg_depend只记录本库的。注意这两者之间不是包含关系。

regclass是一个"​对象标识符类型",可以将oid转换成对应在pg_class的名称(MogDB 5.2版本中新增了regroleregnamespace)。

可以看到这里的classid,在pg_class中对应的都是系统元数据表,即classid表示的每一个表,都是一种对象所属的类。

第二个字段objid,即为前面这个类下的一个对象id

我们任意取一笔记录,来看怎么找到这个对象

image-wpbx.png

该行记录的系统类为1255,可以查到对应pg_proc;

image-crxi.png

对象id为12560,那我们就去pg_proc里查oid=12560的记录,即可找到该对象是什么

image-atdc.png

以pg_depend进行管中窥豹,可以大致了解这些系统表的组织关系。

二、pg_class

pg_depend里这些classid都是pg_class的某一行的oid,包括pg_class这个表本身的oid,也是relname=’pg_class’这一行的oid。

但我们知道,pg_class里不是只有系统表的名称,还有普通表、视图、索引的名称也都会记录在里面,那么pg_class里到底会存哪些对象?

我们看下pg_class的表结构,由于该表的字段太多,本次我们只关注它有哪些对象,关键在relkind这个字段

relkind 说明
r 表示普通表
i 表示索引
I 表示分区表GLOBAL索引
S 表示序列
L 表示Large序列
v 表示视图
c 表示复合类型
t 表示TOAST表
f 表示外表
m 表示物化视图

可以看到,这里的种类其实都拥有类似于表(或叫relation)的结构,即拥有多个字段,不过比较特殊的是索引和复合类型这两者,这两者不能通过sql语句直接查里面的数据,而复合类型本身更是没有数据内容

image-cxso.png

序列其实就是一张只有一行的表

image-eunx.png

在pg_attribute中,可以根据pg_class中的oid,查到该relation所包含的所有字段(包括隐藏字段和被删除字段),

也就是说,理论上,在MogDB中,下面这条SQL应该是查不出记录的(原生postgresql支持零列的表,但是仍然包含隐藏字段)

select * from pg_class c where not exists 
(select 1 from pg_attribute a where c.oid=a.attrelid);

pg_class中的每个oid(除了索引),都在pg_type中存在一条对应的记录,pg_class.oid=pg_type.typrelid

这意味着,每个relation自己,又都是各自的”类”,而relation里面的每行数据(tuple),是这个类的”对象”

至此,整个数据库中的所有数据,都是符合”类-对象”的特征

类层级 对象层级 对象
0-主类 pg_class 1-元数据表 一个元数据表(比如pg_proc、pg_type)
1-元数据表 pg_class 2-对象(relation) 一个relation(比如一个自建表、一个序列)
1-元数据表 pg_proc 2-对象(proc) 一个函数(比如length、sum)
1-元数据表 pg_type 2-对象(type) 一个数据类型(比如varchar、int)
2-对象(relation) 一个自建表 3-行(tuple) 表的一行

三、系统元数据表清单

以上为MogDB/openGauss/postgresql的系统表元数据设计及其之间的逻辑关系,接下来我们看具体的元数据表,以下只列举一些常见的

表名 ORACLE视图名 扩展视图或扩展表 备注
pg_database dba_pdbs/sys.container$ 数据库列表
pg_collation 字符排序规则
pg_conversion 字符编码转换规则
pg_authid dba_users/sys.user$ pg_user 用户,pg_user隐藏了密码,加了用户级guc参数展示
pg_roles 角色,按权限过滤展示
pg_auth_history 历史密码变更记录
pg_auth_members 继承角色关系
pg_group 关联查询pg_auth_members
gs_db_privilege any权限 (PG没有)
pg_default_acl 新对象的默认权限(比如给schema下的新表默认查询权限)
pg_user_status 用户状态(登录失败次数、密码过期)
pg_namespace 模式(schema)
pg_class dba_tables pg_tables 所有的表和类似表的对象(relation),都在pg_class中
dba_views pg_views 里面查询了pg_rewrite获取定义
pg_index dba_indexex/dba_ind_columns pg_indexes 索引
pg_constraint dba_constraint 约束
pg_partition dba_tab_partitionsdba_tab_subpartitions 分区表和子分区表
pg_rewrite pg_rules 视图的定义在重写规则里,pg_rules只展示非select的规则
gs_matview dba_mviews gs_matview_dependency (物化视图依赖基表) 物化视图 (PG没有)
pg_type dba_types pg_enum (枚举类型) 注意create table或view时,会同时create一个同名的type
pg_range (范围类型)
pg_set (set类型, B 模式专用) (PG没有)
pg_attribute dba_tab_cols pg_attrdef (属性的默认值) 针对pg_class中每个oid的属性(比如表的字段)
pg_description
pg_shdescription sh指在各个db中共享,比如pg_database表就是一个共享表
pg_proc dba_procedures pg_aggregate (聚合函数) 所有的function和procedure,包括package内的
pg_language 支持的pg_proc语言
gs_package gs_object (object-type,MogDB 5.2新增 ) 所有的package(PG没有)
pg_trigger dba_triggers DML触发器(含truncate)
pg_event_trigger 事件触发器(比如ddl)
pg_extension 扩展插件
pg_synonym dba_synonyms 同义词(PG没有)
pg_foreign_server dba_db_links 外部服务器
pg_foreign_data_wrapper 外部数据封装器
pg_foreign_table 外部表
pg_depend dba_dependencies 依赖关系
pg_shdepend 共享依赖关系
pg_job dbms_job 功能的相关表 定时任务(PG没有)
pg_job_proc
gs_job_argument dbms_schedule 功能的相关表 定时任务(openGauss没放出sql函数接口)
gs_job_attribute
pg_operator v$sqlfn_metadata 操作符
pg_opclass 操作符类
pg_opfamily 操作符族
pg_cast 类型转换
pg_am 访问方法(hash、btree...)
pg_amop 访问方法的操作符
pg_amproc 访问方法调用的函数
pg_object dba_objects 所有对象(目前只包含以下)s 序列、l 大序列、v 视图、r 表、i 索引、P 存储过程、函数、S 包说明/object-type说明、B 包体/object-type体
pg_directory dba_directory 数据库目录
pg_db_role_setting 设置到数据库、用户级别的参数
pg_statistic 统计信息
gs_dependencies_obj gs_dependencies plsql编译依赖关系(openGauss 6.0/MogDB 5.2新增)
pg_rlspolicy 行级访问策略
pg_inherits 继承表
gs_masking gs_masking_policy 动态脱敏(PG没有)
gs_masking_policy_actions
gs_masking_policy_filters

四、元数据表字段名设计

几乎所有的元数据表的字段名,都有一个特征,即有一个该表表名的三到四个字母缩写作为前缀。
比如pg_type表中的字段,是以typ开头,比如typnamespace、typname;
比如pg_proc表中的字段,是以pro开头,比如pronamespace、proname;
比如pg_attribute表中的字段,是以att开头,比如attrelid、attname;
有个特殊点的,pg_class,是以rel(即relation)开头,比如relnamespace、relname。
这种设计的好处是让所有的元数据字段能平面化,和内核一一对应,不存在一个属性名同时存在于多个地方。

五、总结

得益于postgresql这种严谨的设计,让所有内核开发者能遵循统一的规则来进行新功能的扩展;并且由于其极其简单明了的对象组织关系,让初学者也可以很快对数据库有个框架性的理解,只是千万不要死记硬背, 学习postgresql的优势在于,它几乎全部的功能都可以从表中查到,并且最终回到pg_class这个表上来。openGauss/MogDB继续延续这种设计,让国产数据库站在前辈们的肩膀上继续壮大。

posted on 2024-11-06 12:53  DarkAthena  阅读(15)  评论(0编辑  收藏  举报

导航