1. 程式人生 > 其它 >PostgreSQL中Oid和Relfilenode的對映

PostgreSQL中Oid和Relfilenode的對映

技術標籤:資料庫postgresql

作者李傳成
中國PG分會認證專家,瀚高軟體資深核心研發工程師
https://zhuanlan.zhihu.com/p/342466054

PostgreSQL中的表會有一個RelFileNode值指定這個表在磁碟上的檔名(外部表、分割槽表除外)。一般情況下在pg_class表的relfilenode欄位可以查出這個值,但是有一些特定表在relfilenode欄位的查詢結果是0,這個部落格中將會探究這些特殊表relfilenode的核心處理。

正常表的Relfilenode

當我們建立一張普通表時,在pg_class系統表裡可以查詢出其relfilenode,可以看出在表剛剛建立時其oid和relfilenode都是16808,在磁碟上也可以查詢到16808這個檔案。事實上,這個檔案儲存了我們向表t2插入的資料。

postgres=# create table t2(i int);
CREATE TABLE
postgres=# select oid,relname,relfilenode from pg_class where relname = 't2';
  oid  | relname | relfilenode 
-------+---------+-------------
 16808 | t2      |       16808
(1 row)

postgres=# \q
[email protected]:/h2/pgpgpg/bin$ ll ../data/base/12835/16808 
-rw-------+ 1 movead movead 0 12月 31 17:11 ..
/data/base/12835/16808 [email protected]:/h2/pgpgpg/bin$

在我們對一張表執行truncate,vacuum full等操作後,會重寫這個表的資料,會引發這個表relfilenode值的變更。如下測試可以看出truncate之後,t2表的relfilenode從16808變為了16811.

postgres=# truncate t2;
TRUNCATE TABLE
postgres=# select oid,relname,relfilenode from pg_class where relname = 't2';
  oid  | relname |
relfilenode -------+---------+------------- 16808 | t2 | 16811 (1 row) postgres=# checkpoint; CHECKPOINT postgres=# \q [email protected]:/h2/pgpgpg/bin$ ll ../data/base/12835/16808 ls: 無法訪問'../data/base/12835/16808': 沒有那個檔案或目錄 [email protected]:/h2/pgpgpg/bin$ ll ../data/base/12835/16811 -rw-------+ 1 movead movead 0 12月 31 17:16 ../data/base/12835/16811 [email protected]:/h2/pgpgpg/bin$

Nail表的Relfilenode

postgres=# select oid, relname, relfilenode,reltablespace
from pg_class
where relfilenode = 0 and relkind = 'r'
order by reltablespace;
 oid  |        relname        | relfilenode | reltablespace 
------+-----------------------+-------------+---------------
 1247 | pg_type               |           0 |             0
 1255 | pg_proc               |           0 |             0
 1249 | pg_attribute          |           0 |             0
 1259 | pg_class              |           0 |             0
 3592 | pg_shseclabel         |           0 |          1664
 1262 | pg_database           |           0 |          1664
 2964 | pg_db_role_setting    |           0 |          1664
 1213 | pg_tablespace         |           0 |          1664
 1261 | pg_auth_members       |           0 |          1664
 1214 | pg_shdepend           |           0 |          1664
 2396 | pg_shdescription      |           0 |          1664
 1260 | pg_authid             |           0 |          1664
 6000 | pg_replication_origin |           0 |          1664
 6100 | pg_subscription       |           0 |          1664
(14 rows)

postgres=#

上述查詢可以看出,從pg_class系統表中查詢出的這些表的relfilenode為0。其中pg_type、pg_proc、pg_attribute、pg_class是非共享表,在核心中稱他們為Nail表。剩餘的表是在pg_global表空間裡的共享表。

pg_class表中relfilenode欄位的意義是為了告訴程式,某一張表在磁碟上儲存的檔名。比如我們查詢t2表時,一定會先到pg_class系統表中獲取其relfilenode,然後到磁碟找到這個檔案,然後開啟並掃描。可是如果我們想查詢pg_class系統表在磁碟上的檔名時,應該去哪找到它的relfilenode?在PostgreSQL中提供了一組函式介面進行oid和relfilenode的轉化。

postgres=# select pg_relation_filenode(1259);
 pg_relation_filenode 
----------------------
                16475
(1 row)

postgres=# select pg_filenode_relation(0,16475);
 pg_filenode_relation 
----------------------
 pg_class
(1 row)

postgres=# select pg_filenode_relation(0,16475)::oid;
 pg_filenode_relation 
----------------------
                 1259
(1 row)

postgres=#

通過pg_relation_filenode()可以將oid轉化為relfilenode,
通過pg_filenode_relation可以將relfilenode轉化為oid.
既然pg_class表中不儲存oid和relfilenode的對應關係,那麼PostgreSQL是怎麼樣儲存這個對映關係的呢?
在這裡插入圖片描述

Nail表Relfilenode的儲存機制

經過研究發現,在資料目錄裡存在著pg_filenode.map檔案,如下所示。

[email protected]:/h2/pgpgpg/data/base/12835$ ll pg_filenode.map 
-rw-------+ 1 movead movead 512 12月 31 15:10 pg_filenode.map
[email protected]:/h2/pgpgpg/data/base/12835$
[email protected]:/h2/pgpgpg/data/global$ ll pg_filenode.map 
-rw-------+ 1 movead movead 512 12月 31 15:10 pg_filenode.map
[email protected]:/h2/pgpgpg/data/global$

在global目錄下的pg_filenode.map檔案裡儲存了shared表的oid和relfilenode的對映關係,12835目錄下儲存了OID為12835的資料庫裡nail表的oid和relfilenode的對映關係。
pg_filenode.map檔案的結構為:

typedef struct RelMapping
{
    Oid         mapoid;         /* OID of a catalog */
    Oid         mapfilenode;    /* its filenode number */
} RelMapping;

typedef struct RelMapFile
{
    int32       magic;          /* always RELMAPPER_FILEMAGIC */
    int32       num_mappings;   /* number of valid RelMapping entries */
    RelMapping  mappings[MAX_MAPPINGS];
    pg_crc32c   crc;            /* CRC of all above */
    int32       pad;            /* to make the struct size be 512 exactly */
} RelMapFile;

結語

這個部落格主要闡述了在PostgreSQL中表的oid和relfilenode對映的兩種不同表現形式,你只要記住使用pg_relation_filenode()永遠會得到正確的結果,從pg_class系統表中查詢則可能會得到錯誤的結果。

瞭解更多PostgreSQL技術乾貨、熱點文集、行業動態、新聞資訊、精彩活動,請訪問中國PostgreSQL社群網站:www.postgresqlchina.com