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 ページレイアウト全体
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_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が追跡するものです。
表65.3 PageHeaderDataのレイアウト
フィールド | 型 | 長さ | 説明 |
---|---|---|---|
pd_lsn | PageXLogRecPtr | 8バイト | LSN: このページへの最終変更に対応するWALレコードの最後のバイトの次のバイト |
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。存在しなければゼロ。 |
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が作る、アイテムへのポインタ(ItemPointer
、CTID
とも言います)はページ番号とアイテム識別子のインデックスによって構成されています。
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 ページレイアウト
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のレイアウト
フィールド | 型 | 長さ | 説明 |
---|---|---|---|
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バイト | ユーザデータに対するオフセット |
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_getattr、fastgetattrおよび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
テーブルアクセスメソッドは常にこの書式を使用します。
既存のすべてのインデックスメソッドも、この基本書式を使用しています。しかし、インデックスメタページに保持されるデータは通常、アイテムレイアウト規則に従っていません。