バージョンごとのドキュメント一覧

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

<title>Database Page Layout</title>

This section provides an overview of the page format used within <productname>PostgreSQL</productname> tables and indexes.<footnote> 本節ではPostgreSQLのテーブルおよびインデックスで使われるページ書式の概略について説明します。 [17] Sequences and <acronym>TOAST</acronym> tables are formatted just like a regular table. TOASTのテーブルとシーケンスは、通常のテーブルと同様に整形されています。

In the following explanation, a <firstterm>byte</firstterm> is assumed to contain 8 bits. In addition, the term <firstterm>item</firstterm> refers to an individual data value that is stored on a page. In a table, an item is a row; in an index, an item is an index entry. 以下の説明では1バイトは8ビットからなることを前提としています。 さらに、アイテムという単語は、ページに格納される個別のデータ値のことを指しています。 テーブル内ではアイテムは行であり、インデックス内ではアイテムはインデックスのエントリです。

Every table and index is stored as an array of <firstterm>pages</firstterm> of a fixed size (usually 8 kB, although a different page size can be selected when compiling the server). In a table, all the pages are logically equivalent, so a particular item (row) can be stored in any page. In indexes, the first page is generally reserved as a <firstterm>metapage</firstterm> holding control information, and there can be different types of pages within the index, depending on the index access method. テーブルとインデックスはすべて、固定サイズ(通常8キロバイト。サーバのコンパイル時に異なるサイズを設定可能)のページの集まりとして格納されます。 テーブルでは、すべてのページは論理上等価です。 したがって、あるアイテム(行)はどのページにでも格納することができます。 インデックスでは、初めのページは通常、制御用の情報を保持するメタページとして予約されます。 また、インデックスではインデックスアクセスメソッドに依存した様々なページ種類があります。

<xref linkend="page-table"/> shows the overall layout of a page. There are five parts to each page. 表 65.2はページの全体的なレイアウトを示しています。 各ページには5つの部分があります。

表65.2 ページレイアウト全体

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

The first 24 bytes of each page consists of a page header (<structname>PageHeaderData</structname>). Its format is detailed in <xref linkend="pageheaderdata-table"/>. The first field tracks the most recent WAL entry related to this page. The second field contains the page checksum if <xref linkend="app-initdb-data-checksums"/> are enabled. Next is a 2-byte field containing flag bits. This is followed by three 2-byte integer fields (<structfield>pd_lower</structfield>, <structfield>pd_upper</structfield>, and <structfield>pd_special</structfield>). These contain byte offsets from the page start to the start of unallocated space, to the end of unallocated space, and to the start of the special space. The next 2 bytes of the page header, <structfield>pd_pagesize_version</structfield>, store both the page size and a version indicator. Beginning with <productname>PostgreSQL</productname> 8.3 the version number is 4; <productname>PostgreSQL</productname> 8.1 and 8.2 used version number 3; <productname>PostgreSQL</productname> 8.0 used version number 2; <productname>PostgreSQL</productname> 7.3 and 7.4 used version number 1; prior releases used version number 0. (The basic page layout and header format has not changed in most of these versions, but the layout of heap row headers has.) The page size is basically only present as a cross-check; there is no support for having more than one page size in an installation. The last field is a hint that shows whether pruning the page is likely to be profitable: it tracks the oldest un-pruned XMAX on the page. それぞれのページの最初の24バイトはページヘッダ(PageHeaderData)から構成されています。 その書式を表 65.3にて説明します。 最初のフィールドは、このページに関連する最も最近のWAL項目を表しています。 2番目のフィールドにはdata checksumsが有効な場合にページチェックサムが格納されています。 次にフラグビットを含む2バイトのフィールドがあります。 その後に2バイトの整数フィールドが3つ続きます(pd_lowerpd_upperpd_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が追跡するものです。

表65.3 PageHeaderDataのレイアウト

<title>PageHeaderData Layout</title> <titleabbrev>PageHeaderData Layout</titleabbrev>
フィールド長さ説明
pd_lsnPageXLogRecPtr8バイトLSN: このページへの最終変更に対応するWALレコードの最後のバイトの次のバイト
pd_checksumuint162バイトページチェックサム
pd_flagsuint162バイトフラグビット
pd_lowerLocationIndex2 バイト空き領域の始まりに対するオフセット
pd_upperLocationIndex2バイト空き領域の終わりに対するオフセット
pd_specialLocationIndex2バイト特別な空間の始まりに対するオフセット
pd_pagesize_versionuint162バイトページサイズおよびレイアウトのバージョン番号の情報
pd_prune_xidTransactionId4バイトページ上でもっとも古い切り詰められていないXMAX。存在しなければゼロ。

All the details can be found in <filename>src/include/storage/bufpage.h</filename>. 詳細情報についてはsrc/include/storage/bufpage.hを参照してください。

Following the page header are item identifiers (<type>ItemIdData</type>), each requiring four bytes. An item identifier contains a byte-offset to the start of an item, its length in bytes, and a few attribute bits which affect its interpretation. New item identifiers are allocated as needed from the beginning of the unallocated space. The number of item identifiers present can be determined by looking at <structfield>pd_lower</structfield>, which is increased to allocate a new identifier. Because an item identifier is never moved until it is freed, its index can be used on a long-term basis to reference an item, even when the item itself is moved around on the page to compact free space. In fact, every pointer to an item (<type>ItemPointer</type>, also known as <type>CTID</type>) created by <productname>PostgreSQL</productname> consists of a page number and the index of an item identifier. ページヘッダに続くのはアイテム識別子(ItemIdData)です。 識別子ごとに4バイトを必要とします。 アイテム識別子は、アイテムが開始されるバイトオフセット、バイト単位の長さ、そしてその解釈に影響する属性ビット群を持っています。 新しいアイテム識別子は必要に応じて、未割当て空間の最初から割り当てられます。 アイテム識別子の数は、新しい識別子を割り当てるために増加されるpd_lowerを見ることで決定できます。 アイテム識別子は解放されるまで動かされることがないので、アイテム自体が空き領域をまとめるためにページ上で移動される場合でも、そのインデックスはアイテムを参照するために長期にわたって使うことができます。 実際、PostgreSQLが作る、アイテムへのポインタ(ItemPointerCTIDとも言います)はページ番号とアイテム識別子のインデックスによって構成されています。

The items themselves are stored in space allocated backwards from the end of unallocated space. The exact structure varies depending on what the table is to contain. Tables and sequences both use a structure named <type>HeapTupleHeaderData</type>, described below. アイテム自体は、未割り当て空間の最後から順番に割り当てられた空間に格納されます。 正確な構造は、テーブルに何を含めるかによって異なります。 テーブルとシーケンスの両方が、以下で説明するHeapTupleHeaderDataという構造を使用します。

The final section is the <quote>special section</quote> which can contain anything the access method wishes to store. For example, b-tree indexes store links to the page's left and right siblings, as well as some other data relevant to the index structure. Ordinary tables do not use a special section at all (indicated by setting <structfield>pd_special</structfield> to equal the page size). 最後のセクションは、アクセスメソッドが格納しようとするものを何でも含めることのできる特別なセクションです。 例えば、B-treeインデックスは、そのページの両隣のページへのリンク、ならびに、インデックス構造体に関連したその他の何らかのデータを持ちます。 通常のテーブルではこれはまったく使用されません(ページサイズを同じにするためにpd_specialを設定することで示されます)。

<xref linkend="storage-page-layout-figure"/> illustrates how these parts are laid out in a page. 図 65.1は、これらの部分がページ内でどのようにレイアウトされているかを図解しています。

図65.1 ページレイアウト

<title>Page Layout</title>

65.6.1. テーブル行のレイアウト #

<title>Table Row Layout</title>

All table rows are structured in the same way. There is a fixed-size header (occupying 23 bytes on most machines), followed by an optional null bitmap, an optional object ID field, and the user data. The header is detailed in <xref linkend="heaptupleheaderdata-table"/>. The actual user data (columns of the row) begins at the offset indicated by <structfield>t_hoff</structfield>, which must always be a multiple of the MAXALIGN distance for the platform. The null bitmap is only present if the <firstterm>HEAP_HASNULL</firstterm> bit is set in <structfield>t_infomask</structfield>. If it is present it begins just after the fixed header and occupies enough bytes to have one bit per data column (that is, the number of bits that equals the attribute count in <structfield>t_infomask2</structfield>). In this list of bits, a 1 bit indicates not-null, a 0 bit is a null. When the bitmap is not present, all columns are assumed not-null. The object ID is only present if the <firstterm>HEAP_HASOID_OLD</firstterm> bit is set in <structfield>t_infomask</structfield>. If present, it appears just before the <structfield>t_hoff</structfield> boundary. Any padding needed to make <structfield>t_hoff</structfield> a MAXALIGN multiple will appear between the null bitmap and the object ID. (This in turn ensures that the object ID is suitably aligned.) テーブル行はすべて同じ方法で構成されています。 固定サイズのヘッダ(ほとんどのマシンで23バイトを占有します)があり、その後にオプションのNULLビットマップ、オプションのオブジェクトIDフィールド、およびユーザデータが続きます。 ヘッダについては表 65.4で説明します。 実際のユーザデータ(行内の列)は、常にプラットフォームのMAXALIGN距離の倍数であるt_hoffで示されるオフセットから始まります。 NULLビットマップはHEAP_HASNULLビットがt_infomaskで設定されている場合にのみ存在します。 存在する場合は、固定ヘッダのすぐ後ろから始まり、データ列ごとに1ビットとするのに十分なバイト数を占有します(すなわち、t_infomask2内の属性の個数と等しいビット数です)。 このビットのリスト内では、1ビットは非NULLを、0ビットはNULLを示します。 このビットマップが存在しない場合、すべての列が非NULLとみなされます。 オブジェクトIDはHEAP_HASOID_OLDビットがt_infomaskで設定されている場合にのみ存在します。 存在する場合、これはt_hoff境界の直前に現れます。 t_hoffをMAXALIGNの倍数とするために必要なパッドは全て、NULLビットマップとオブジェクトIDの間に現れます (このことにより、オブジェクトIDの位置揃えが確実に適切になります)。

表65.4 HeapTupleHeaderDataのレイアウト

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

All the details can be found in <filename>src/include/access/htup_details.h</filename>. 詳細情報についてはsrc/include/access/htup_details.hを参照してください。

Interpreting the actual data can only be done with information obtained from other tables, mostly <structname>pg_attribute</structname>. The key values needed to identify field locations are <structfield>attlen</structfield> and <structfield>attalign</structfield>. There is no way to directly get a particular attribute, except when there are only fixed width fields and no null values. All this trickery is wrapped up in the functions <firstterm>heap_getattr</firstterm>, <firstterm>fastgetattr</firstterm> and <firstterm>heap_getsysattr</firstterm>. 実際のデータの解釈は、他のテーブル、ほとんどの場合、pg_attributeから取得された情報でのみ行うことができます。 フィールド位置を識別するために必要なキー値は、attlenおよびattalignです。 フィールドの幅が固定されていてNULL値が存在しない場合を除き、特定の属性を直接取得する方法はありません。 この仕組みはすべて、heap_getattrfastgetattrおよびheap_getsysattr関数にラップされています。

To read the data you need to examine each attribute in turn. First check whether the field is NULL according to the null bitmap. If it is, go to the next. Then make sure you have the right alignment. If the field is a fixed width field, then all the bytes are simply placed. If it's a variable length field (attlen = -1) then it's a bit more complicated. All variable-length data types share the common header structure <type>struct varlena</type>, which includes the total length of the stored value and some flag bits. Depending on the flags, the data can be either inline or in a <acronym>TOAST</acronym> table; it might be compressed, too (see <xref linkend="storage-toast"/>). データを読むためには、それぞれの属性を順番に検査する必要があります。 まず、NULLビットマップに従ってフィールドがNULLかどうかを検査します。 もしNULLであれば、次に進みます。 次に、位置揃えが正しいことを確認してください。 フィールドの幅が固定されていれば、すべてのバイトが単純に配置されます。 可変長のフィールド(attlen == -1)の場合はもう少し複雑です。 可変長のデータ型はすべて、格納する値の長さといくつかのフラグビットを持つstruct varlenaという共通ヘッダ構造体を共有します。 フラグによって、データは行内、または別のテーブル(TOAST)のいずれかとなったり、圧縮済みとなったりします (65.2を参照してください)。



[17] Actually, use of this page format is not required for either table or index access methods. The <literal>heap</literal> table access method always uses this format. All the existing index methods also use the basic format, but the data kept on index metapages usually doesn't follow the item layout rules. 実際には、テーブルアクセスメソッドもインデックスアクセスメソッドも、このページ書式を使用する必要はありません。 heapテーブルアクセスメソッドは常にこの書式を使用します。 既存のすべてのインデックスメソッドも、この基本書式を使用しています。しかし、インデックスメタページに保持されるデータは通常、アイテムレイアウト規則に従っていません。