?
本文檔使用 php中文網(wǎng)手冊 發(fā)布
本節(jié)提供一個在PostgreSQL表和索引使用的頁格式的概述。 [1] 序列和TOAST表的格式就像一個普通表的。
下面說明一下,一個字節(jié)假定為包含8位。另外,術(shù)語項為存儲在頁上的一個獨立數(shù)據(jù)值。 在表中,一項是一行;在索引中,一項為一個索引條目。
每個表和索引存儲為固定大小的頁數(shù)組。(通常 8 kB,不過當(dāng)編譯服務(wù)器的時候,可以選擇不同的頁大?。?在表中,所有的頁是邏輯等價的,所以一個特殊項(行)可以存儲在任意頁。在索引,第一頁通常保留為持有控制信息的元頁, 這里可以有不同類型的索引頁,依賴于索引訪問方法。
Table 54-2顯示一個頁的整體布局, 這里每頁有 5 部分。
Table 54-2. 頁整體布局
項 | 描述 |
---|---|
頁頭數(shù)據(jù) | 24 字節(jié) 長整型。 包含關(guān)于頁的一般信息,包含自由空間指針。 |
項ID數(shù)據(jù) | 指向?qū)嶋H項的(偏移量,長度)對的數(shù)組。 每項 4 字節(jié)。 |
自由空間 | 未分配空間。從這個區(qū)域開始分配新項指針,或從結(jié)尾分配新項指針。 |
項 | 實際項本身 |
專用空間 | 索引訪問方法專用數(shù)據(jù)。不同方法存儲不同的數(shù)據(jù)。普通表里為空。 |
每頁的前24個字節(jié)構(gòu)成一個頁頭(PageHeaderData)。在Table 54-3有它的詳細(xì)格式。前兩個字段跟蹤相關(guān)頁的最近的WAL條目。 下邊的一個2字節(jié)的字段是包含標(biāo)志位。隨后由3個2字節(jié)整數(shù)字段(pd_lower,pd_upper, 和 pd_special)。這些包含分別為從頁開始到未分配空間的開始,到未分配空間的結(jié)束,專用空間的開始的偏移字節(jié)數(shù)。 下邊頁頭的2字節(jié),pd_pagesize_version,存儲頁大小和版本指示符。 從PostgreSQL 8.3開始 版本編號是 4; PostgreSQL 8.1 和 8.2 使用版本編號 3; PostgreSQL 8.0 使用版本編號 2; PostgreSQL 7.3 和 7.4 使用版本編號 1; 先前發(fā)布版本使用版本編號 0。 (在大多數(shù)這些版本中,基本的頁布局和頭格式?jīng)]有變化,但是堆布局有行頭.) 頁面大小是基本上只存在一個交叉檢查;在安裝的版本中,這里不支持多于一頁大小的。最后一個字段是個提示,顯示是否整理頁,可能是有利的。 它跟蹤在頁上最舊的未修整的XMAX。
Table 54-3. 頁頭數(shù)據(jù)布局
字段 | 類型 | 長度 | 描述 |
---|---|---|---|
pd_lsn | XLogRecPtr | 8 字節(jié) | LSN: 該頁上xlog日志記錄變化的最后字節(jié)的下一字節(jié) |
pd_tli | uint16 | 2 字節(jié) | 最后變化的時間線ID(僅其最低16 位) |
pd_flags | uint16 | 2 bytes | Flag bits |
pd_lower | LocationIndex | 2 bytes | Offset to start of free space |
pd_upper | LocationIndex | 2 字節(jié) | 到自由空間結(jié)尾的偏移量 |
pd_special | LocationIndex | 2 字節(jié) | 到專用空間開始的偏移量 |
pd_pagesize_version | uint16 | 2 字節(jié) | 頁大小和版本編號布局信息 |
pd_prune_xid | TransactionId | 4 字節(jié) | 頁上最舊的未修整的XMAX,如果沒有則為零。 |
在src/include/storage/bufpage.h可以找到所有的詳細(xì)信息。
下面的頁頭是項標(biāo)識符(ItemIdData),每個需要4字節(jié)。一個項標(biāo)識符包含一個到項開始的字節(jié)偏移, 以字節(jié)計的長度,和一些影響它解釋的屬性位。新項標(biāo)識符需要從未分配空間的開始分配。 可以通過查看pd_lower來確定項標(biāo)識符的數(shù)量,分配新的標(biāo)示符,其會增加。因為一個項標(biāo)示符從來不移動直到釋放了它, 實際上,每個指針為PostgreSQL所創(chuàng)建的一項由頁號和項標(biāo)識符的索引構(gòu)成。 (ItemPointer,還可以稱為CTID)
項本身存儲在從未分配的空間的結(jié)尾向后分配的空間。確切的結(jié)構(gòu)取決于包含什么表。 表和序列兩都使用一個名為HeapTupleHeaderData的結(jié)構(gòu),下面描述。
最后這段是"特殊段"其包含想存放的任何訪問方法。例如, b-tree 索引存儲連接頁左右的兄弟,以及相應(yīng)的索引結(jié)構(gòu)的一些其它數(shù)據(jù)。 普通的表根本沒有使用特殊段。(通過設(shè)置pd_special等于頁大小來表示)
所有表行結(jié)構(gòu)方式相同。有個固定大小的頭(在大多數(shù)機(jī)器占用23 字節(jié)),隨后一個NULL位圖的可選項,對象ID字段,和用戶數(shù)據(jù)。 該頭的詳細(xì)信息在Table 54-4。 實際的用戶數(shù)據(jù)(行中列)由 t_hoff 表示的偏移量開始,它必須始終是為平臺的MAXALIGN間距的倍數(shù)。 NULL位圖僅存在,如果在t_infomask設(shè)置了HEAP_HASNULL位。 如果它存在,它就開始于固定頭的后面,占用足夠的字節(jié),每數(shù)據(jù)列一位。 (那是,t_natts 位一塊) 在這個位列表中, 一個1位 標(biāo)識非空,一個 0 位是空。 對象ID 僅存在,如果在t_infomask設(shè)置了HEAP_HASOID位。 如果存在,它將出現(xiàn)在t_hoff邊界前。任何需要做 t_hoff 的MAXALIGN倍數(shù)的填充,出現(xiàn)在NULL位圖和對象ID之間。 (反過來又保證對象ID得到恰當(dāng)?shù)膶R)
Table 54-4. HeapTupleHeaderData 布局
字段 | 類型 | 長度 | 描述 |
---|---|---|---|
t_xmin | TransactionId | 4 字節(jié) | 插入 XID 戳 |
t_xmax | TransactionId | 4 字節(jié) | 刪除 XID 戳 |
t_cid | CommandId | 4 字節(jié) | 插入 和/或 刪除 CID 戳 (用 t_xvac 覆蓋) |
t_xvac | TransactionId | 4 字節(jié) | VACUUM 操作移動一行版本的XID |
t_ctid | ItemPointerData | 6 字節(jié) | 這個當(dāng)前的或新行版本的 TID |
t_infomask2 | int16 | 2 字節(jié) | 字段個數(shù),加上各種標(biāo)志位 |
t_infomask | uint16 | 2 字節(jié) | 各種標(biāo)志位數(shù) |
t_hoff | uint8 | 1 字節(jié) | 用戶數(shù)據(jù)偏移量 |
在src/include/access/htup.h可以找到所有的詳細(xì)信息。
解釋實際數(shù)據(jù)只能從其它表獲取信息來做,大多pg_attribute。 需要來表示字段位置的鍵值是attlen和attalign。 沒有直接獲取特定字段的方法,除僅當(dāng)有固定寬度字段并且沒有空值的情況外。所有這些策略封裝在函數(shù) heap_getattr, fastgetattr 和 heap_getsysattr。
要讀取數(shù)據(jù)你需要逐次檢查每個屬性。首先檢查字段是否為NULL依據(jù) NULL位圖。如果是,跳到下一個。 然后確定你已經(jīng)右對齊。如果字段是固定寬度的字段,那么所有的字節(jié)簡單的放置。如果它是變長的字段(attlen = -1) 那么它是一個更復(fù)雜的位。所有變長數(shù)據(jù)類型共享通用的頭結(jié)構(gòu)struct varlena,其包括存儲值的總長度和一些標(biāo)志位。 依賴這些標(biāo)志,數(shù)據(jù)可能是行內(nèi)或在一個TOAST表;它也可能是壓縮的。 (參閱Section 54.2)
[1] | 實際上,索引訪問方法不需要使用這個頁格式。所有存在的索引方法用這個基本格式,但是 索引元頁上保持的數(shù)據(jù)通常不遵循該項設(shè)計規(guī)則。 |