52.3. データベースページファイル

本節ではPostgreSQLのテーブルおよびインデックスで使われるページ書式の概略について説明します。 " type="a" --> [1] " type="a" --> TOASTのテーブルとシーケンスは、通常のテーブルと同様に整形されています。

以下の説明では1バイトは8ビットからなることを前堤としています。 さらに、アイテムという単語は、ページに格納される個別のデータ値のことを指しています。 テーブル内ではアイテムは行であり、インデックス内ではアイテムはインデックスのエントリです。

テーブルとインデックスは全て、固定サイズ(通常8キロバイト。サーバのコンパイル時に異なるサイズを設定可能)のページの集まりとして格納されます。 テーブルでは、全てのページは論理上等価です。 したがって、ある項目(行)はどのページにでも格納することができます。 インデックスでは、初めのページは通常、制御用の情報を保持するメタページとして予約されます。 また、インデックスではインデックスアクセスメソッドに依存した様々なページ種類があります。

表52-2はページの全体的なレイアウトを示しています。 各ページには5つの部分があります。

表 52-2. ページレイアウト全体

アイテム説明
PageHeaderData長さは20バイト。空き領域ポインタを含む、ページについての一般情報です。
ItemIdData実際のアイテムを指す(オフセットと長さの)ペアの配列です。 1アイテムにつき4バイトです。
空き領域割り当てられていない空間です。 新規のアイテムポインタはこの領域の先頭から、新規のアイテムは最後から割り当てられます。
アイテム実際のアイテムそのものです。
特別な空間インデックスアクセスメソッド特有のデータです。異なるメソッドは異なるデータを格納します。通常のテーブルでは空です。

それぞれのページの最初の20バイトはページヘッダ(PageHeaderData)から構成されています。 その書式を表52-3にて説明します。 最初の2つのフィールドは、このページに関連する最も最近のWAL項目を表しています。 その後に2バイトの整数フィールドが3つ続きます(pd_lowerpd_upperpd_special)。 これらには、割り当てられていない空間の始まり、割り当てられていない空間の終わり、そして特別な空間の始まりのバイトオフセットが格納されています。 ページヘッダの最後の2バイトであるpd_pagesize_versionは、ページサイズとバージョン指示子の両方を格納します。 PostgreSQL 8.1のバージョン番号は3、PostgreSQL 8.0のバージョン番号は2、PostgreSQL 7.3と7.4のバージョン番号は1です。 それより前のリリースのバージョン番号は0です (バージョン間で基本的なページレイアウトやヘッダの書式は変更されていませんが、ヒープ行ヘッダのレイアウトが変更されました)。 ページサイズは基本的に、照合用としてのみ存在しています。 同一インストレーションでの複数のページサイズはサポートされていません。

表 52-3. PageHeaderDataのレイアウト

フィールド長さ説明
pd_lsnXLogRecPtr8バイトLSN: 最終変更に対応するxlogの最後のバイトの次のバイト
pd_tliTimeLineID4バイト最終変更のTLI
pd_lowerLocationIndex2 バイト空き領域の始まりに対するオフセット
pd_upperLocationIndex2バイト空き領域の終わりに対するオフセット
pd_specialLocationIndex2バイト特別な空間の始まりに対するオフセット
pd_pagesize_versionuint162バイトページサイズおよびレイアウトのバージョン番号の情報

詳細情報についてはsrc/include/storage/bufpage.hを参照してください。

ページヘッダに続くのはアイテム識別子(ItemIdData)です。 識別子ごとに4バイトを必要とします。 アイテム識別子は、アイテムが開始されるバイトオフセット、バイト単位の長さ、そしてその解釈に影響する属性ビット群を持っています。 新しいアイテム識別子は必要に応じて、未割当て空間の最初から割り当てられます。 アイテム識別子の数は、新しい識別子を割り当てるために増加されるpd_lowerを見ることで決定できます。 アイテム識別子は解放されるまで動かされることがないので、アイテム自体が空き領域をまとめるためにページ上で移動される場合でも、そのインデックスはアイテムを参照するために長期にわたって使うことができます。 実際、PostgreSQLが作る、アイテムへのポインタ(ItemPointerCTIDとも言います)はページ番号とアイテム識別子のインデックスによって構成されています。

アイテム自体は、未割り当て空間の最後から順番に割り当てられた空間に格納されます。 正確な構造は、テーブルに何を含めるかによって異なります。 テーブルとシーケンスの両方が、以下で説明するHeapTupleHeaderDataという構造を使用します。

最後のセクションは、アクセスメソッドが格納しようとするものを何でも含めることのできる"特別なセクション"です。 例えば、B-treeインデックスは、そのページの両隣のページへのリンク、ならびに、インデックス構造体に関連したその他の何らかのデータを持ちます。 通常のテーブルではこれはまったく使用されません(ページサイズを同じにするためにpd_specialを設定することで示されます)。

テーブル行は全て、同じ方法で構成されています。 固定サイズのヘッダ(ほとんどのマシンで27バイトを占有します)があり、その後にオプションのNULLビットマップ、オプションのオブジェクトIDフィールド、およびユーザデータが続きます。 ヘッダについては表52-4で説明します。 実際のユーザデータ(行内の列)は、常にプラットフォームのMAXALIGN距離の倍数であるt_hoffで示されるオフセットから始まります。 NULLビットマップはHEAP_HASNULLビットがt_infomaskで設定されている場合にのみ存在します。 存在する場合は、固定ヘッダのすぐ後ろから始まり、データ列ごとに1ビットとするのに十分なバイト数を占有します(合計すると、t_nattsのビット数となります)。 このビットのリスト内では、1ビットは非NULLを、0ビットはNULLを示します。 このビットマップが存在しない場合、全ての列が非NULLとみなされます。 オブジェクトIDはHEAP_HASOIDビットがt_infomaskで設定されている場合にのみ存在します。 存在する場合、これはt_hoff境界の直前に現れます。 t_hoffをMAXALIGNの倍数とするために必要なパッドは全て、NULLビットマップとオブジェクトIDの間に現れます (このことにより、オブジェクトIDの位置揃えが確実に適切になります)。

表 52-4. HeapTupleHeaderDataのレイアウト

フィールド長さ説明
t_xminTransactionId4バイト挿入XIDスタンプ
t_cminCommandId4バイト挿入CIDスタンプ
t_xmaxTransactionId4バイト削除XIDスタンプ
t_cmaxCommandId4バイト削除CIDスタンプ(t_xvacと共有)
t_xvacTransactionId4バイト行バージョンを移すVACUUM操作用XID
t_ctidItemPointerData6バイトこの行または最新バージョンの行の現在のTID
t_nattsint162バイト属性の数
t_infomaskuint162バイト様々なフラグビット
t_hoffuint81バイトユーザデータに対するオフセット

詳細情報についてはsrc/include/access/htup.hを参照してください。

実際のデータの解釈は、他のテーブル、ほとんどの場合、pg_attributeから取得された情報でのみ行うことができます。 フィールド位置を識別するために必要なキー値は、attlenおよびattalignです。 フィールドの幅が固定されていてNULL値が存在しない場合を除き、特定の属性を直接取得する方法はありません。 この仕組みは全て、heap_getattrfastgetattrおよびheap_getsysattr関数にラップされています。

データを読むためには、それぞれの属性を順番に検査する必要があります。 まず、NULLビットマップに従ってフィールドがNULLかどうかをチェックします。 もしNULLであれば、次に進みます。 次に、位置揃えが正しいことを確認してください。 フィールドの幅が固定されていれば、全てのバイトが単純に配置されます。 可変長のフィールド(attlen == -1)の場合はもう少し複雑です。 可変長のデータ型は全て、格納する値の長さといくつかのフラグビットを持つvarattribという共通ヘッダ構造体を共有します。 フラグによって、データは行内、または別のテーブル(TOAST)のいずれかとなったり、圧縮済みとなったりします (項52.2を参照してください)。

注意

[1]

実際にはインデックスアクセスメソッドはこのページ書式を使用する必要はありません。 既存の全てのインデックスメソッドがこの基本書式を使用しています。 しかし、インデックスメタページに保持されるデータは通常、アイテムレイアウト規則に正確には従っていません。