PostgreSQL 对象管理




  • 概述

    PostgreSQL 中的所有数据都存储在对应的文件中,即我们常见到的文件。这些用来存储数据的文件共同构成了 PostgreSQL 整个数据库集簇,而数据库集簇是对 PostgreSQL 中多个数据库组成的集合的称呼。而在逻辑上,PostgreSQL 所有的数据库都是隶属于某个表空间,并且单个数据库不能跨表空间,而一个表空间中可以存放多个数据库。表空间和数据库的关系属于多对多的关系。那么数据库中的数据是如何存放在数据文件中的呢?接下来一起探究。

    理解 oid 和 relfilenode 的关系

    在 PostgreSQL 中,oid 全称为 Object identifier,称为对象标识符,在 PostgreSQL 中,用于为每个对象分配的一个内部主键数据类型,其别名为 regclass,并且 oid 可以转换为整数。而 relfilenode 则为 PostgreSQL 数据库中对对象的物理访问信息。relfilenode 关联三个对应的oid,即表空间的oid,数据库的oid和对象的oid。默认,在创建一个对象时,会为该对象使用 oid 映射到 relfilenode 编号。内部采用 RelMapping 的方式进行映射,因此,在 PostgreSQL 中,所有对象的管理通过 oid 来管理内部对象,外部文件却通过 relfilenode 来管理。

     
    <code><span class="code-snippet_outer">
    postgres=# CREATE TABLE tab_test(id int,name varchar);
    CREATE TABLE
    postgres=# INSERT INTO tab_test VALUES(1,'PostgreSQL');
    INSERT 0 1
    postgres=# SELECT relname,oid,relfilenode FROM pg_class WHERE relname = 'tab_test';
     relname | oid | relfilenode 
    ----------+-------+-------------
     tab_test | 16384 | 16384
    (1 row)
    通过上面的示例可以观察到创建的新的对象 oid 和 relfilenode 是一样的。其中默认的第一个 oid 为 16384
    
    #define FirstNormalObjectId 16384
    那么问题来了,如果在一个并发的多事务环境中,创建的对象会不会有冲突。
    
    
     

    --事务1
    postgres=# BEGIN;
    BEGIN
    postgres=*# CREATE TABLE tab_test(id int,name varchar);
    CREATE TABLE
    postgres=*# SELECT relname,oid,relfilenode FROM pg_class WHERE relname = 'tab_test'; relname | oid | relfilenode
    ----------+-------+-------------
    tab_test | 16384 | 16384
    (1 row)

    <span class="code-snippet_outer"><code></code></span></pre>
    --事务2
    postgres=# BEGIN;
    BEGIN
    postgres=*# CREATE TABLE tab_t(id int,name varchar);
    CREATE TABLE
    postgres=*# SELECT relname,oid,relfilenode FROM pg_class WHERE relname = 'tab_t';
    relname | oid | relfilenode
    ---------+-------+-------------
    tab_t | 16390 | 16390
    <span class="code-snippet_outer"><code></code></span></pre>
    实际上并不会,新的对象会为其分配一个新的的 oid。事务如果发生回滚,oid 不会被复用,oid 值递增。
    
    relfilenode 值在以下情况中将会被重置,此刻 relfilenode 和 oid 将会不一致
    <span class="code-snippet_outer"><strong>1、VACUUM FULL</strong>
    
    
     

    postgres=# SELECT relname,oid,relfilenode
    FROM pg_class
    WHERE relname ~ 'tab_test';
    relname | oid | relfilenode
    ----------+-------+-------------
    tab_test | 16384 | 16384
    (1 row)

    <span class="code-snippet_outer"><code></code></span></pre>
    postgres=# VACUUM tab_test;
    VACUUM
    postgres=# SELECT relname,oid,relfilenode
    FROM pg_class
    WHERE relname ~ 'tab_test';
    relname | oid | relfilenode
    ----------+-------+-------------
    tab_test | 16384 | 16384
    (1 row)
    postgres=# VACUUM FULL tab_test;
    VACUUM
    postgres=# SELECT relname,oid,relfilenode
    FROM pg_class
    WHERE relname ~ 'tab_test';
    relname | oid | relfilenode
    ----------+-------+-------------
    tab_test | 16384 | 16390
    
    VACUUM FULL 操作对表和索引都会重置 relfilenode
    
    2、REINDEX INDEX
     <code></code></pre>
    postgres=# CREATE INDEX idx_tab_test_id ON tab_test USING btree(id);
    CREATE INDEX
    postgres=# SELECT relname,oid,relfilenode
    FROM pg_class
    WHERE relname ~ 'idx_tab_test_id';
    relname | oid | relfilenode
    -----------------+-------+-------------
    idx_tab_test_id | 16401 | 16401
    (1 row)
    <code></code></pre>
    postgres=# REINDEX INDEX idx_tab_test_id ;
    REINDEX
    postgres=# SELECT relname,oid,relfilenode
    FROM pg_class
    WHERE relname ~ 'idx_tab_test_id';
    relname | oid | relfilenode
    -----------------+-------+-------------
    idx_tab_test_id | 16401 | 16402
    (1 row)
    
    仅仅针对索引重置 relfilenode
    
    3、CLUSTER table_name USING index_name
     <code>
    postgres=# SELECT relname,oid,relfilenode
    FROM pg_class
    WHERE relname ~ 'tab_test|idx_tab_test_id';
     relname | oid | relfilenode 
    -----------------+-------+-------------
     tab_test | 16384 | 16390
     idx_tab_test_id | 16401 | 16402
    (2 rows)
    postgres=# CLUSTER tab_test USING idx_tab_test_id;
    CLUSTER
    postgres=# SELECT relname,oid,relfilenode
    FROM pg_class
    WHERE relname ~ 'tab_test|idx_tab_test_id';
     relname | oid | relfilenode 
    -----------------+-------+-------------
     tab_test | 16384 | 16403
     idx_tab_test_id | 16401 | 16409
    (2 rows)
    CLUSTER 操作会对表和索引都重置 relfilenode
    
    

    relfilenode 涉及的函数

    pg_relation_filonode()

    postgres=# SELECT pg_relation_filenode('tab_test');
    pg_relation_filenode
    ----------------------
    16410
    (1 row)

    pg_relation_filepath()

    postgres=# SELECT pg_relation_filepath('tab_test');
    pg_relation_filepath
    ----------------------
    base/13580/16410
    (1 row)

    结语

    在 PostgreSQL 中,除了通过使用 oid 来作为对象标识符以外,另外一种标识类型为 xid,或者叫做事务(简称为 xact)标识符,在relation 对象中,对应为隐藏列 xmin 和 xmax。当然 cid 用来作命令标识符,在 relation 对象中,以隐藏列cmin 和cmax 存在。元组标识符 tid用于标识元组(即行)在表中的物理位置,在 relation 对象中使用 ctid ,也属于隐藏属性,使用k&v 形式查找对象,ctid 是一对(blkno,blkindex)来标识。

     

    Speak Your Mind

    *