PostgreSQL的元組、頁面結構及索引查找原理

我們知道postgresql數據庫通過數據多版本實現mvcc,pg又沒有undo段,老版本的數據元組直接存放在數據頁面中,這樣帶來的問題就是舊元組需要不斷地進行清理以釋放空間,這也是數據庫膨脹的根本原因。本文簡單介紹一下postgresql數據庫的元組、頁面的結構以及索引查找流程。

元組結構

元組,也叫tuple,這個叫法是很學術的叫法,但是現在數據庫中一般叫行或者記錄。下面是元組的結構:

typedef struct HeapTupleFields
{
    TransactionId t_xmin;       /* inserting xact ID */
    TransactionId t_xmax;       /* deleting or locking xact ID */

    union
    {
        CommandId   t_cid;      /* inserting or deleting command ID, or both */
        TransactionId t_xvac;   /* old-style VACUUM FULL xact ID */
    }           t_field3;
} HeapTupleFields;
struct HeapTupleHeaderData
{
    union
    {
        HeapTupleFields t_heap;
        DatumTupleFields t_datum;
    }           t_choice;

    ItemPointerData t_ctid;     /* current TID of this or newer tuple (or a
                                 * speculative insertion token) */

    /* Fields below here must match MinimalTupleData! */
#define FIELDNO_HEAPTUPLEHEADERDATA_INFOMASK2 2
    uint16      t_infomask2;    /* number of attributes + various flags */
#define FIELDNO_HEAPTUPLEHEADERDATA_INFOMASK 3
    uint16      t_infomask;     /* various flag bits, see below */
#define FIELDNO_HEAPTUPLEHEADERDATA_HOFF 4
    uint8       t_hoff;         /* sizeof header incl. bitmap, padding */
    /* ^ - 23 bytes - ^ */
#define FIELDNO_HEAPTUPLEHEADERDATA_BITS 5
    bits8       t_bits[FLEXIBLE_ARRAY_MEMBER];  /* bitmap of NULLs */
    /* MORE DATA FOLLOWS AT END OF STRUCT */
};

在這裏插入圖片描述

t_xmin:代表插入此元組的事務xid;
t_xmax:代表更新或者刪除此元組的事務xid,如果該元組插入後未進行更新或者刪除,t_xmax=0;
t_cid:command id,代表在當前事務中,已經執行過多少條sql,例如執行第一條sql時cid=0,執行第二條sql時cid=1;
t_ctid:保存着指向自身或者新元組的元組標識(tid),由兩個數字組成,第一個數字代表物理塊號,或者叫頁面號,第二個數字代表元組號。在元組更新後tid指向新版本的元組,否則指向自己,這樣其實就形成了新舊元組之間的“元組鏈”,這個鏈在元組查找和定位上起着重要作用。

瞭解了元組結構,再簡單瞭解下元組更新和刪除過程。

更新過程

在這裏插入圖片描述
上圖中左邊是一條新插入的元組,可以看到元組是xid=100的事務插入的,沒有進行更新,所以t_xmax=0,同時t_ctid指向自己,0號頁面的第一號元組。右圖是發生xid=101的事務更新該元組後的狀態,更新在pg裏相當於插入一條新元組,原來的元組的t_xmax變爲了更新這條事務的xid=101,同時t_ctid指針指向了新插入的元組(0,2),0號頁面第二號元組,第二號元組的t_xmin=101(插入該元組的xid),t_ctid=(0,2),沒有發生更新,指向自己。

刪除過程

在這裏插入圖片描述
上圖代表該元組被xid=102的事務刪除,將t_xmax設置爲刪除事務的xid,t_ctid指向自己。

頁面結構

下面再來看看頁面的結構

在這裏插入圖片描述

從上圖可以看到,頁面包括三種類型的數據
1.header data:數據頭是page生成的時候隨之產生的,由pageHeaderData定義結構,24個字節長,包含了page的相關信息,下面是數據結構:

typedef struct PageHeaderData
{
    /* XXX LSN is member of *any* block, not only page-organized ones */
    PageXLogRecPtr pd_lsn;      /* LSN: next byte after last byte of xlog
                                 * record for last change to this page */
    uint16      pd_checksum;    /* checksum */
    uint16      pd_flags;       /* flag bits, see below */
    LocationIndex pd_lower;     /* offset to start of free space */
    LocationIndex pd_upper;     /* offset to end of free space */
    LocationIndex pd_special;   /* offset to start of special space */
    uint16      pd_pagesize_version;
    TransactionId pd_prune_xid; /* oldest prunable XID, or zero if none */
    ItemIdData  pd_linp[FLEXIBLE_ARRAY_MEMBER]; /* line pointer array */
} PageHeaderData;

pd_lsn: 存儲最近改變該頁面的xlog位置。
pd_checksum:存儲頁面校驗和。
pd_lower,pd_upper:pd_lower指向行指針(line pointer)的尾部,pd_upper指向最後那個元組。
pd_special: 索引頁面中使用,它指向特殊空間的開頭。
2.line pointer:行指針,四字節,每一條元組會有一個行指針指向真實元組位置。
3.heap tuple:存放真實的元組數據,注意元組是從頁面的尾部向前堆積的,元組和行指針之間的是數據頁的空閒空間。

索引查找

看了頁面和元組結構,再看看索引的結構。

在這裏插入圖片描述

以上圖爲例,索引的數據包含兩部分(key=xxx,TID=(block=xxx,offset=xxx)),key表示真實數據,tid代表指向數據行的指針,具體block代表頁面號,offset代表行偏移量,指向數據頁面的line pointer,比如執行下面的查詢語句
select * from tbl where id=1000;
key=1000,根據key值在索引中找到tid爲5號頁面的1號元組,再通過一號元組行指針找到元組1,檢查元組1的t_ctid字段,發現指向了新的元組2,於是定位到真實元組數據2。

歡迎關注我的公衆號:數據庫架構之美

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章