闲聊PostgreSQL的oid

栏目: 数据库 · 发布时间: 5年前

闲聊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

-------+----

沈阳男科医院哪里好 http://mobile.99syjk.com/

沈阳男科医院那个好 http://mobile.shenda120.com/


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

原则

原则

[美] 瑞·达利欧 / 刘波、綦相 / 中信出版社 / 2018-1 / CNY 98.00

※ 华尔街投资大神、对冲基金公司桥水创始人,人生经验之作 作者瑞·达利欧出身美国普通中产家庭,26岁时被炒鱿鱼后在自己的两居室内创办了桥水,现在桥水管理资金超过1 500亿美元,截至2015年年底,盈利超过450亿美元。达利欧曾成功预测2008年金融危机,现在将其白手起 家以来40多年的生活和工作原则公开。 ※ 多角度、立体阐述生活、工作、管理原则 包含21条高原则、139条中原......一起来看看 《原则》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具