無料スクリプト配布のPHP.TO   PHPの実用的なtips PHPマニュアル MySQLマニュアル Apacheマニュアル PostgreSQLマニュアル マニュアル検索    

58.6. データベースページのレイアウト

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

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

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

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

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

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

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

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

フィールド 長さ 説明
pd_lsn XLogRecPtr 8バイト LSN: このページへの最終変更に対応するxlogレコードの最後のバイトの次のバイト
pd_checksum uint16 2バイト ページチェックサム
pd_flags uint16 2バイト フラグビット
pd_lower LocationIndex 2 バイト 空き領域の始まりに対するオフセット
pd_upper LocationIndex 2バイト 空き領域の終わりに対するオフセット
pd_special LocationIndex 2バイト 特別な空間の始まりに対するオフセット
pd_pagesize_version uint16 2バイト ページサイズおよびレイアウトのバージョン番号の情報
pd_prune_xid TransactionId 4バイト ページ上でもっとも古い切り詰められていないXMAX。存在しなければゼロ。

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

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

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

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

テーブル行はすべて同じ方法で構成されています。 固定サイズのヘッダ(ほとんどのマシンで23バイトを占有します)があり、その後にオプションのNULLビットマップ、オプションのオブジェクトIDフィールド、およびユーザデータが続きます。 ヘッダについては 表58-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の位置揃えが確実に適切になります)。

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

フィールド 長さ 説明
t_xmin TransactionId 4バイト 挿入XIDスタンプ
t_xmax TransactionId 4バイト 削除XIDスタンプ
t_cid CommandId 4バイト 挿入、削除の両方または片方のCIDスタンプ(t_xvacと共有)
t_xvac TransactionId 4バイト 行バージョンを移すVACUUM操作用XID
t_ctid ItemPointerData 6バイト この行または最新バージョンの行の現在のTID
t_infomask2 uint16 2バイト 属性の数と各種フラグビット
t_infomask uint16 2バイト 様々なフラグビット
t_hoff uint8 1バイト ユーザデータに対するオフセット

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

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

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

注意

[1]

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


powered by SEO.CUG.NET