Chapter 7. 分頁文件

一份描述資料庫文件預設頁面格式的文件.

本章提供一個PostgreSQL 的表和索引所使用的頁面格式的概述.(索引存取樣式不 需要使用這些頁面格式。 目前,所有索引方法都使用這個基本格式,但保留在索引元資料頁裡 的資料通常並不準確地遵循項布局規則。)TOAST表和序列的格式和 普通表一樣。

在下面解釋中,假定一個 字節 包含 8 個位.另外, 項(item) 指的是儲存在一個頁面裡的獨立資料值。在一個表裡, 一個項是一個元組(行)﹔在一個索引裡,一個項是一條索引記錄。

Table 7-1 顯示一個頁面的基本布局。每個頁面有五個部分。

Table 7-1. 樣例頁面布局

描述
PageHeaderData20字節長。包含關於頁面的一般資訊, 包括自由空間指針。
ItemPointerData(偏移量,長度)對的陣列,指向實際項。
Free space(自由空間)未分配的空間。所有新元組都從這裡分配,通常是從結尾開始。
Items(項)實際的項本身。
Special Space(特殊空間)索引存取樣式相關的資料。不同的方式存放不同的資料。 在普通表中為空。

每個頁面的頭20個字節組成頁頭(PageHeaderData)。它的格式在 Table 7-2 裡詳細介紹。頭兩個字節 處理與 WAL 相關的東西。然後跟著三個2字節的整數位段 (pd_lowerpd_upper, 和 pd_special)。這些欄位分別表示與未分配 空間開頭的偏移,與未分配空間結尾的偏移,以及到特殊空間開頭的偏移。

Table 7-2. PageHeaderData 布局

欄位類型長度描述
pd_lsnXLogRecPtr8 字節LSN: xlog 最後一個字節的下一個字節
pd_suiStartUpID4 字節最後修改的 SUI (目前它只用於堆 AM)
pd_lowerLocationIndex2 字節到自由空間開頭的偏移量。
pd_upperLocationIndex2 字節到自由空間結尾的偏移量。
pd_specialLocationIndex2 字節到特殊空間開頭的偏移量。
pd_pagesize_versionuint162 字節頁面大小和布局版本號資訊。

所有細節都可以在 src/include/storage/bufpage.h 裡找到。

特殊空間是在頁面末尾的區域,它在頁面初始化的時候分配,包含與某種 存取方式相關的資訊。頁頭的最後兩個字節,pd_pagesize_version, 保存頁面大小和一個版本指示器。從 PostgreSQL 7.3 開始, 版本數是 1﹔以前的版本使用版本號 0。(基本的頁面布局以及頭格式沒有變化, 但是堆元組頭的布局改變了。)頁面大小通常只用作交叉檢查﹔在一次安裝中 沒有大於一個頁面尺寸的支援。

在頁頭後面是項標識符(ItemIdData),每個需要四個字節。 一個項標識符包含一個到項開頭的字節偏移量,它自己以字節計的長度, 以及一個屬性位的集合,這些屬性位影響它的解釋。新的項標識符根據需要 從未分配空間的開頭分配。項標識符的數目可以通過查看 pd_lower 來判斷,在分配新標識符的時候會遞增。因為一個項標識符在其釋放前絕對 不會移動,所以它的索引可以用於長時間地參照一個項,即使該項本身因為 壓縮自由空間在頁面內部進行了移動也如此。實際上,PostgreSQL 建立地每個指向項的指針(ItemPointer,也叫做 CTID) 都由一個頁號和一個項標識符的索引組成。

項本身儲存在從未分配空間末尾開始從後向前分配的空間裡。它們的實際 結構因表包含的內容不同而不同。表和序列都使用一種叫做 HeapTupleHeaderData 的結構,在下面描述。

最後一段是“特殊段”,它可以包含任何存取方法想存放的東西。 普通表並不使用這個段(通過設定 pd_special 來指示,以 平衡頁面大小)。

所有表元組都用同樣方法構造。它們有一個定長的頭(在大多數機器上占據23個字節), 後面跟著一個可選的 null 位圖,一個可選的物件 ID 欄位,以及使用者資料。 頭在 Table 7-3 裡詳細描述。實際使用者資料 (元組的欄位)從 t_hoff 標識的偏移量開始,它必須是 該平台的 MAXALIGN 距離的倍數。null 位圖只有在 t_infomask 裡面的 HEAP_HASNULL 位設定了的時候才出現。 如果它出現了,那麼它緊跟在定長頭後面,占據足夠容納每個資料欄位對應一個位的字節數 (也就是說,總共 t_natts 位)。在這個位列裡面,為 1 的位 表示非空,而為 0 的位表示空。如果沒有出現這個位圖,那麼所有資料欄位都 假設為非空的。物件 ID 只有在設定了 t_infomask 裡面的 HEAP_HASOID 位的時候才出現。如果出現,它正好出現在 t_hoff 範圍之前。如果需要補齊 t_hoff,使之 成為 MAXALIGN 的倍數,那麼這些填充將出現在 null 位圖和度相 ID 之間。 (這樣也保証了物件 ID 得到恰當的對齊。)

Table 7-3. HeapTupleHeaderData 布局

欄位類型長度描述
t_xminTransactionId4 字節插入 XID 戳記
t_cminCommandId4 字節插入 CID 戳記(和 t_xmax 重疊)
t_xmaxTransactionId4 字節刪除 XID 戳記
t_cmaxCommandId4 字節刪除 CID 戳記(與 t_xvac 重疊)
t_xvacTransactionId4 字節用於 VACUUM 操作移動元組的 XID
t_ctidItemPointerData6 字節這個或者新元組的目前 ID
t_nattsint162 字節欄位數目
t_infomaskuint162 字節各種標志
t_hoffuint81 字節到使用者資料的偏移量

所有細節都可以在 src/include/access/htup.h 中找到。

對具體資料的解釋只能在從其它表中獲取的資訊的情況下進行, 這些資訊大多數在 pg_attribute 裡。 尤其是 attlen 欄位和 attalign 欄位。 我們沒有辦法直接獲取某個欄位,除非它們是定寬並且沒有 NULL 的。 所有這些復雜的操作都封裝在函數 heap_getattrfastgetattrheap_getsysattr 裡。

要讀取資料的話,你需要輪流檢查每個欄位。首先根據 null 位圖檢查該欄位是否為 NULL。 如果是,那麼跳到下一個欄位。然後保証你的對齊是正確的。如果欄位是一個 定寬欄位,那麼所有字節都簡單地放在那裡。如果它是一個變長欄位(attlen == -1), 那麼它就會更加復雜一些,它會使用變長結構 varattrib。 根據標志地不同,資料可能是內聯的,也可能是壓縮的或者是在其它表中(TOAST)。