目 录CONTENT

文章目录

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

DarkAthena
2024-11-06 / 0 评论 / 0 点赞 / 39 阅读 / 0 字

前言

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

一、pg_depend

先来看pg_depend的表结构

名称类型引用描述
classidoidPG_CLASS.oid有依赖对象所在系统表的OID。
objidoid任意OID属性指定的依赖对象的OID。
objsubidinteger-对于表字段,这个是该字段的字段号(objid和classid引用表本身)。对于所有其它对象类型,目前这个字段是零。
refclassidoidPG_CLASS.oid被引用对象所在的系统表的OID。
refobjidoid任意OID属性指定的被引用对象的OID。
refobjsubidinteger-对于表字段,这个是该字段的字段号(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_class1-元数据表一个元数据表(比如pg_proc、pg_type)
1-元数据表pg_class2-对象(relation)一个relation(比如一个自建表、一个序列)
1-元数据表pg_proc2-对象(proc)一个函数(比如length、sum)
1-元数据表pg_type2-对象(type)一个数据类型(比如varchar、int)
2-对象(relation)一个自建表3-行(tuple)表的一行

三、系统元数据表清单

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

表名ORACLE视图名扩展视图或扩展表备注
pg_databasedba_pdbs/sys.container$数据库列表
pg_collation字符排序规则
pg_conversion字符编码转换规则
pg_authiddba_users/sys.user$pg_user用户,pg_user隐藏了密码,加了用户级guc参数展示
pg_roles角色,按权限过滤展示
pg_auth_history历史密码变更记录
pg_auth_members继承角色关系
pg_group关联查询pg_auth_members
gs_db_privilegeany权限 (PG没有)
pg_default_acl新对象的默认权限(比如给schema下的新表默认查询权限)
pg_user_status用户状态(登录失败次数、密码过期)
pg_namespace模式(schema)
pg_classdba_tablespg_tables所有的表和类似表的对象(relation),都在pg_class中
dba_viewspg_views里面查询了pg_rewrite获取定义
pg_indexdba_indexex/dba_ind_columnspg_indexes索引
pg_constraintdba_constraint约束
pg_partitiondba_tab_partitionsdba_tab_subpartitions分区表和子分区表
pg_rewritepg_rules视图的定义在重写规则里,pg_rules只展示非select的规则
gs_matviewdba_mviewsgs_matview_dependency (物化视图依赖基表)物化视图 (PG没有)
pg_typedba_typespg_enum (枚举类型)注意create table或view时,会同时create一个同名的type
pg_range (范围类型)
pg_set (set类型, B 模式专用) (PG没有)
pg_attributedba_tab_colspg_attrdef (属性的默认值)针对pg_class中每个oid的属性(比如表的字段)
pg_description
pg_shdescriptionsh指在各个db中共享,比如pg_database表就是一个共享表
pg_procdba_procedurespg_aggregate (聚合函数)所有的function和procedure,包括package内的
pg_language支持的pg_proc语言
gs_packagegs_object (object-type,MogDB 5.2新增 )所有的package(PG没有)
pg_triggerdba_triggersDML触发器(含truncate)
pg_event_trigger事件触发器(比如ddl)
pg_extension扩展插件
pg_synonymdba_synonyms同义词(PG没有)
pg_foreign_serverdba_db_links外部服务器
pg_foreign_data_wrapper外部数据封装器
pg_foreign_table外部表
pg_dependdba_dependencies依赖关系
pg_shdepend共享依赖关系
pg_jobdbms_job 功能的相关表定时任务(PG没有)
pg_job_proc
gs_job_argumentdbms_schedule 功能的相关表定时任务(openGauss没放出sql函数接口)
gs_job_attribute
pg_operatorv$sqlfn_metadata操作符
pg_opclass操作符类
pg_opfamily操作符族
pg_cast类型转换
pg_am访问方法(hash、btree...)
pg_amop访问方法的操作符
pg_amproc访问方法调用的函数
pg_objectdba_objects所有对象(目前只包含以下)s 序列、l 大序列、v 视图、r 表、i 索引、P 存储过程、函数、S 包说明/object-type说明、B 包体/object-type体
pg_directorydba_directory数据库目录
pg_db_role_setting设置到数据库、用户级别的参数
pg_statistic统计信息
gs_dependencies_objgs_dependenciesplsql编译依赖关系(openGauss 6.0/MogDB 5.2新增)
pg_rlspolicy行级访问策略
pg_inherits继承表
gs_maskinggs_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继续延续这种设计,让国产数据库站在前辈们的肩膀上继续壮大。

0
  1. 支付宝打赏

    qrcode alipay
  2. 微信打赏

    qrcode weixin
博主关闭了所有页面的评论