闲聊PostgreSQL的oid

本文涉及的产品
云原生数据库 PolarDB PostgreSQL 版,标准版 2核4GB 50GB
云原生数据库 PolarDB MySQL 版,通用型 2核4GB 50GB
简介: 闲聊PostgreSQL的oid oid为何物? PostgreSQL的系统表中大多包含一个叫做OID的隐藏字段,这个OID也是这些系统表的主键。
# 闲聊PostgreSQL的oid ## oid为何物? PostgreSQL的系统表中大多包含一个叫做OID的隐藏字段,这个OID也是这些系统表的主键。 所谓OID,中文全称就是"对象标识符"。what?还有“对象”? 如果对PostgreSQL有一定了解,应该知道PostgreSQL最初的设计理念就是"对象关系数据库"。也就是说,系统表中储存的那些元数据,比如表,视图,类型,操作符,函数,索引,FDW,甚至存储过程语言等等这些统统都是对象。具体表现就在于这些东西都可以扩展,可以定制。不仅如此,PostgreSQL还支持函数重载,表继承等这些很OO的特性。 利用PostgreSQL的这些特性,用户可以根据业务场景从应用层到数据库层做一体化的优化设计,获得极致的性能与用户体验。一些用惯了MySQL的互联网架构师推崇"把数据库当存储",这一设计准则用在MySQL上也许合适,但如果硬要套在PostgreSQL上,就有点暴殄天物了! 扯得有点远了^_^,下面举几个栗子看下oid长啥样。 ## 使用示例 先随便创建一张表 postgres=# create table tb1(id int); CREATE TABLE 再看下这张表对应的oid postgres=# select oid from pg_class where relname='tb1'; oid ------- 32894 (1 row) 这个oid是隐藏字段,因此必须在select列表里明确指定oid列名,光使用`select *`是不输出oid的。 postgres=# select *from pg_class where relname='tb1'; -[ RECORD 1 ]-------+------ relname | tb1 relnamespace | 2200 reltype | 32896 reloftype | 0 relowner | 10 relam | 0 relfilenode | 32894 reltablespace | 0 relpages | 0 reltuples | 0 relallvisible | 0 reltoastrelid | 32897 relhasindex | f relisshared | f relpersistence | p relkind | r relnatts | 2 relchecks | 0 relhasoids | f relhaspkey | f relhasrules | f relhastriggers | f relhassubclass | f relrowsecurity | f relforcerowsecurity | f relispopulated | t relreplident | d relispartition | f relfrozenxid | 596 relminmxid | 2 relacl | reloptions | relpartbound | 不同对象对应于不同的对象标识符类型,比如表对象对应的对象标识符类型就是`regclass`, 通过对象标识符类型可以实现,对象标识符的数字值和对象名称之间的自由转换。 比如,上面那条SQL可以改写成以下的形式。 postgres=# select 'tb1'::regclass::int; int4 ------- 32894 (1 row) 反过来当然也是可以的,在PostgreSQL里就是一个普通的类型转换。 postgres=# select 32894::regclass; regclass ---------- tb1 (1 row) ## 表的数据类型 作为OO的体现之一,PostgreSQL中每个表都是一个新的数据类型,即有一个相应的数据类型对象。 通过`pg_class`可以查出刚才创建的表对应的数据类型对象的oid postgres=# select reltype from pg_class where relname='tb1'; reltype --------- 32896 (1 row) 在定义数据类型的系统表`pg_type`中保存了这个类型相关的信息。 postgres=# select * from pg_type where oid=32896; -[ RECORD 1 ]--+------------ typname | tb1 typnamespace | 2200 typowner | 10 typlen | -1 typbyval | f typtype | c typcategory | C typispreferred | f typisdefined | t typdelim | , typrelid | 32894 typelem | 0 typarray | 32895 typinput | record_in typoutput | record_out typreceive | record_recv typsend | record_send typmodin | - typmodout | - typanalyze | - typalign | d typstorage | x typnotnull | f typbasetype | 0 typtypmod | -1 typndims | 0 typcollation | 0 typdefaultbin | typdefault | typacl | 数据类型的对象标识符类型是regtype,通过regtype转换可以看到新创建的数据类型对象的名字也叫`tb1`。 postgres=# select 32896::regtype; regtype --------- tb1 (1 row) `tb1`类型在使用上和内置的int,text这些常见的数据类型几乎没有区别。 所以,你可以把一个字符串的值转换成`tb1`类型。 postgres=# select $$(999,'abcd')$$::text::tb1; tb1 -------------- (999,'abcd') (1 row) 可以使用`.`取出表类型里面的1个或所有字段 postgres=# select ($$(999,'abcd')$$::text::tb1).id; id ----- 999 (1 row) postgres=# select ($$(999,'abcd')$$::text::tb1).*; id | c1 -----+-------- 999 | 'abcd' (1 row) 当然,还可以用这个类型去创建新的表 postgres=# create table tb2(id int, c1 tb1); CREATE TABLE 如果你其实是想要创建一个像表一样的数据类型(即多个字段的组合),也可以单独创建这个数据类型。 'g, postgres=# create type ty1 as (id int,c1 text); CREATE TYPE ## 表文件 每个表的数据存储在文件系统中单独的文件中(实际不止一个文件),文件路径可以通过系统函数查询 postgres=# select pg_relation_filepath('tb1'); pg_relation_filepath ---------------------- base/13211/32894 (1 row) 上面的`base`对应的是缺省表空间,除此以外还有global表空间。 postgres=# select oid,* from pg_tablespace ; oid | spcname | spcowner | spcacl | spcoptions ------+------------+----------+--------+------------ 1663 | pg_default | 10 | | 1664 | pg_global | 10 | | (2 rows) 用户等全局对象存储在global表空间 postgres=# select relname,reltablespace from pg_class where relkind='r' and reltablespace<>0; relname | reltablespace -----------------------+--------------- pg_authid | 1664 pg_subscription | 1664 pg_database | 1664 pg_db_role_setting | 1664 pg_tablespace | 1664 pg_pltemplate | 1664 pg_auth_members | 1664 pg_shdepend | 1664 pg_shdescription | 1664 pg_replication_origin | 1664 pg_shseclabel | 1664 (11 rows) 表文件路径的第2部分13211是表所在数据库的oid postgres=# select oid,datname from pg_database; oid | datname -------+----------- 13211 | postgres 1 | template1 13210 | template0 (3 rows) 第3部分就是表对象的oid。 ## oid如何分配? oid的分配来自一个实例的全局变量,每分配一个新的对象,对这个全局变量加一。 当分配的oid超过4字节整形最大值的时候会重新从0开始分配,但这并不会导致类似于事务ID回卷那样严重的影响。 系统表一般会以oid作为主键,分配oid时,PostgreSQL会通过主键索引检查新的oid是否在相应的系统表中已经存在, 如果存在则尝试下一个oid。 相关代码如下: Oid GetNewOidWithIndex(Relation relation, Oid indexId, AttrNumber oidcolumn) { Oid newOid; SnapshotData SnapshotDirty; SysScanDesc scan; ScanKeyData key; bool collides; InitDirtySnapshot(SnapshotDirty); /* Generate new OIDs until we find one not in the table */ do { CHECK_FOR_INTERRUPTS(); newOid = GetNewObjectId(); ScanKeyInit(&key, oidcolumn, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(newOid)); /* see notes above about using SnapshotDirty */ scan = systable_beginscan(relation, indexId, true, &SnapshotDirty, 1, &key); collides = HeapTupleIsValid(systable_getnext(scan)); systable_endscan(scan); } while (collides); return newOid; } 因此,oid溢出不会导致系统表中出现oid冲突(2个不同的系统表可能存在oid相同的对象)。 但重试毕竟会使分配有效的oid花费较多的时间,因此不建议用户为普通的用户表使用oid(使用`with oids`)从而导致oid过早的耗尽。 而且,使用oid的用户表如果未给oid创建唯一索引,oid溢出时,可能这个用户表中可能出现重复oid。以下是一个简单的演示: 创建一个`with oids`的表,并插入2条记录 postgres=# create table tb3(id int) with oids; CREATE TABLE postgres=# insert into tb3 values(1); INSERT 32912 1 postgres=# insert into tb3 values(2); INSERT 32913 1 此时,下一个全局oid是32914 [postgres@node1 ~]$ pg_ctl -D data stop waiting for server to shut down.... done server stopped [postgres@node1 ~]$ pg_controldata data pg_control version number: 1002 Catalog version number: 201707211 Database system identifier: 6500386650559491472 Database cluster state: shut down pg_control last modified: Sun 07 Jan 2018 11:14:58 PM CST Latest checkpoint location: 0/9088930 Prior checkpoint location: 0/9073988 Latest checkpoint's REDO location: 0/9088930 Latest checkpoint's REDO WAL file: 000000010000000000000009 Latest checkpoint's TimeLineID: 1 Latest checkpoint's PrevTimeLineID: 1 Latest checkpoint's full_page_writes: on Latest checkpoint's NextXID: 0:602 Latest checkpoint's NextOID: 32914 Latest checkpoint's NextMultiXactId: 2 Latest checkpoint's NextMultiOffset: 3 Latest checkpoint's oldestXID: 548 Latest checkpoint's oldestXID's DB: 1 Latest checkpoint's oldestActiveXID: 0 Latest checkpoint's oldestMultiXid: 1 Latest checkpoint's oldestMulti's DB: 1 Latest checkpoint's oldestCommitTsXid:0 Latest checkpoint's newestCommitTsXid:0 Time of latest checkpoint: Sun 07 Jan 2018 11:14:58 PM CST Fake LSN counter for unlogged rels: 0/1 Minimum recovery ending location: 0/0 Min recovery ending loc's timeline: 0 Backup start location: 0/0 Backup end location: 0/0 End-of-backup record required: no wal_level setting: replica wal_log_hints setting: off max_connections setting: 100 max_worker_processes setting: 8 max_prepared_xacts setting: 0 max_locks_per_xact setting: 64 track_commit_timestamp setting: off Maximum data alignment: 8 Database block size: 8192 Blocks per segment of large relation: 131072 WAL block size: 8192 Bytes per WAL segment: 16777216 Maximum length of identifiers: 64 Maximum columns in an index: 32 Maximum size of a TOAST chunk: 1996 Size of a large-object chunk: 2048 Date/time type storage: 64-bit integers Float4 argument passing: by value Float8 argument passing: by value Data page checksum version: 0 Mock authentication nonce: 5b060aed93e061d3d1ad2dccdfe3336b1ac844f94872e068d86587c48c7d394a 篡改下一个全局oid为32912 [postgres@node1 ~]$ pg_resetwal -D data -o 32912 Write-ahead log reset [postgres@node1 ~]$ pg_ctl -D data start 再插入3条记录,oid存在重复分配。 postgres=# insert into tb3 values(3); INSERT 32912 1 postgres=# insert into tb3 values(4); INSERT 32913 1 postgres=# insert into tb3 values(5); INSERT 32914 1 postgres=# select oid,* from tb3; oid | id -------+---- 32912 | 1 32913 | 2 32912 | 3 32913 | 4 32914 | 5 (5 rows)
相关实践学习
使用PolarDB和ECS搭建门户网站
本场景主要介绍基于PolarDB和ECS实现搭建门户网站。
阿里云数据库产品家族及特性
阿里云智能数据库产品团队一直致力于不断健全产品体系,提升产品性能,打磨产品功能,从而帮助客户实现更加极致的弹性能力、具备更强的扩展能力、并利用云设施进一步降低企业成本。以云原生+分布式为核心技术抓手,打造以自研的在线事务型(OLTP)数据库Polar DB和在线分析型(OLAP)数据库Analytic DB为代表的新一代企业级云原生数据库产品体系, 结合NoSQL数据库、数据库生态工具、云原生智能化数据库管控平台,为阿里巴巴经济体以及各个行业的企业客户和开发者提供从公共云到混合云再到私有云的完整解决方案,提供基于云基础设施进行数据从处理、到存储、再到计算与分析的一体化解决方案。本节课带你了解阿里云数据库产品家族及特性。
相关文章
|
关系型数据库 分布式数据库 PolarDB
|
关系型数据库 分布式数据库 定位技术
PolarDB for PostgreSQL 开源必读手册-VACUUM处理(中)
PolarDB for PostgreSQL 开源必读手册-VACUUM处理
170 0
|
关系型数据库 分布式数据库 PolarDB
《阿里云产品手册2022-2023 版》——PolarDB for PostgreSQL
《阿里云产品手册2022-2023 版》——PolarDB for PostgreSQL
368 0
|
存储 缓存 关系型数据库
|
存储 SQL 并行计算
PolarDB for PostgreSQL 开源必读手册-开源PolarDB for PostgreSQL架构介绍(中)
PolarDB for PostgreSQL 开源必读手册-开源PolarDB for PostgreSQL架构介绍
428 0
|
存储 算法 安全
PolarDB for PostgreSQL 开源必读手册-开源PolarDB for PostgreSQL架构介绍(下)
PolarDB for PostgreSQL 开源必读手册-开源PolarDB for PostgreSQL架构介绍
386 0
|
关系型数据库 分布式数据库 开发工具
|
存储 关系型数据库 Linux
PolarDB for PostgreSQL 开源必读手册-PolarDB安装与配置(下)
PolarDB for PostgreSQL 开源必读手册-PolarDB安装与配置
712 0
|
存储 SQL 关系型数据库