TOAST

PostgreSQL 9.0.0文書
前のページ | 巻戻し | 第 54章データベースの物理的な格納 | 早送り | 次のページ

# 54.2. TOAST

本節ではTOAST（過大属性格納技法：The Oversized-Attribute Storage Technique）の概要について説明します。

PostgreSQLは固定長のページサイズ（通常8キロバイト）を使用し、複数ページにまたがるタプルを許しません。 そのため、大規模なフィールド値を直接格納できません。 大規模なフィールド値を圧縮したり、複数の物理的な行に分割したりすることで、この限界はなくなりました。 これはユーザからは透過的に発生し、また、バックエンドのコード全体には小さな影響しか与えませんでした。 この技法はTOAST（または"パンをスライスして以来最善のもの"）という愛称で呼ばれます。 [訳注：“パンをスライスして以来最善のもの（the best thing since sliced bread）”は素晴らしいものを意味します。]

一部のデータ型のみがTOASTをサポートします。 大規模なフィールド値を生成することがないデータ型にオーバーヘッドを負わせる必要はありません。 TOASTをサポートするためには、データ型は可変長（varlena）表現を持たなければなりません。 格納する値の最初の32ビットワードにはバイト単位の値の（このワード自体を含む）長さが含まれます。 TOASTは残りの表現について制限しません。 TOAST可能なデータ型をサポートするC言語関数は全て、TOAST化された入力値を注意して扱わなければなりません （通常これは、入力に対して何か作業をする前に`PG_DETOAST_DATUM`を呼び出すことで行われますが、もっと効率的な方法が可能な場合もあります）。

TOASTはvarlenaの長さワードの2ビット(ビッグエンディアンのマシンでは上位ビット、リトルエンディアンのマシンでは下位ビット)を勝手に使用します。 そのため、全てのTOAST可能なデータ型の値の論理サイズは1ギガバイト（230 - 1バイト）までになります。 両ビットが0の場合、値はそのデータ型の普通のTOAST化されていない値となり、長さワードの残りのビットはデータの（長さワードを含む）総サイズ（バイト単位）となります。 上位側または下位側のどちらか片方のビットが設定された場合、値は通常の4バイトのヘッダを持たず1バイトのヘッダを持ちます。 また、長さワードの残りビットはデータの（長さワードを含む）総サイズ（バイト単位）となります。 特殊な状況として、長さワードの残りビットがすべて0（自身の長さを含みますのでありえません）の場合、その値は別のTOASTテーブルに保存される行外データへのポインタです。 （TOASTポインタのサイズはデータの第2バイト内で与えられます。） 単一バイトヘッダを持つ値は特定の境界に整列されません。 最後に上位側または下位側のビットが0で隣のビットが設定されている場合、データの内容は圧縮され、使用前に伸長しなければなりません。 この場合、長さワードの残りビットは元データのサイズではなく圧縮したデータの総サイズになります。 圧縮が行外データでも起こりえますが、varlenaヘッダには圧縮されているかどうかについての情報がないことに注意してください。 その代わりTOASTポインタの内容にこの情報が含まれています。

テーブルの任意の列がTOAST可能な場合、そのテーブルは連携するTOASTテーブルを持ちます。 TOASTテーブルのOIDはテーブルの`pg_class`.`reltoastrelid`項目に格納されます。 詳細は後で説明しますが、行外のTOAST化された値はTOASTテーブル内に保持されます。

使用される圧縮技術は、LZ系の圧縮技術の1つで単純かつ非常に高速なものです。 詳細は`src/backend/utils/adt/pg_lzcompress.c`を参照してください。

行外の値は（圧縮される場合は圧縮後に）最大`TOAST_MAX_CHUNK_SIZE`バイトの塊に分割されます （デフォルトではこの値は4チャンク行が1ページに収まり、およそ2000バイトになるように選ばれます）。 各塊は、データを持つテーブルと連携するTOASTテーブル内に個別の行として格納されます。 全てのTOASTテーブルは`chunk_id`列（特定のTOAST化された値を識別するOID）、`chunk_seq`列（値の塊に対する連番）、`chunk_data`（塊の実際のデータ）列を持ちます。 `chunk_id`と`chunk_seq`に対する一意性インデックスは値の抽出を高速化します。 したがって、行外のTOAST化された値を示すポインタデータには、検索先となるTOASTテーブルのOIDと指定した値のOID(`chunk_id`)を格納しなければなりません。 簡便性のために、ポインタデータには論理データサイズ（元々の非圧縮のデータ長）と実際の格納サイズ（圧縮時には異なります）も格納されます。 varlenaヘッダバイトに収納するためにTOASTポインタデータの総サイズは、表現される値の実サイズに関係なく、18バイトになります。

TOASTのコードは、テーブル内に格納される値が`TOAST_TUPLE_THRESHOLD`（通常2キロバイト）を超える時にのみ実行されます。 TOASTコードは、行の値がより小さくなるかそれ以上の縮小ができなくなるまで、フィールド値の圧縮や行外への移動を行います。 更新操作中、変更されない値は通常そのまま残ります。 行外の値を持つ行の更新では、行外の値の変更がなければTOASTするコストはかかりません。

TOASTコードでは、以下のTOAST可能な列を格納するための4つの異なる戦略を認めます。

- `PLAIN`は圧縮や行外の格納を防止します。 さらにvarlena型での単一バイトヘッダの使用を無効にします。 これはTOAST化不可能のデータ型の列に対してのみ取り得る戦略です。

- `EXTENDED`では、圧縮と行外の格納を許します。 これはほとんどのTOAST可能のデータ型のデフォルトです。 圧縮がまず行われ、それでも行が大き過ぎるのであれば行外に格納します。

- `EXTERNAL`は非圧縮の行外格納を許します。 `EXTERNAL`を使用すると、`text`と`bytea`列全体に対する部分文字列操作が高速化されます。 こうした操作は非圧縮の行外の値から必要な部分を取り出す時に最適化されるためです （格納領域が増加するという欠点があります）。

- `MAIN`は圧縮を許しますが、行外の格納はできません （実際にはこうした列についても行外の格納は行われます。しかし、他に行を縮小させページに合わせる方法がない場合の最後の手段としてのみです）。

TOAST可能なデータ型はそれぞれ、そのデータ型の列用のデフォルトの戦略を指定します。 しかし`ALTER TABLE SET STORAGE`を使用して、あるテーブル列の戦略を変更することができます。

この機構には、ページをまたがる行の値を許可するといった素直な手法に比べて多くの利点があります。 通常問い合わせは比較的小さなキー値に対する比較で条件付けされるものと仮定すると、エクゼキュータの仕事のほとんどは主だった行の項目を使用して行われることになります。 TOAST化属性の大規模な値は、（それが選択されている時）結果集合をクライアントに戻す時に引き出されるだけです。 このため、主テーブルは行外の格納を使用しない場合に比べて、かなり小さくなり、その行は共有バッファキャッシュにより合うようになります。 ソート集合もまた小さくなり、ソートが完全にメモリ内で行われる頻度が高くなります。 小規模な試験結果ですが、典型的なHTMLページとそのURLを持つテーブルでは、TOASTテーブルを含め、元々のデータサイズのおよそ半分で格納でき、さらに、主テーブルには全体のデータのおよそ10%のみ（URLと一部の小さなHTMLページ）が格納されました。 比較のために全てのHTMLページを7キロバイト程度に切り詰めたTOAST化しないテーブルと比べ、実行時に違いはありませんでした。

前のページ | ホーム | 次のページ
データベースファイルのレイアウト | 上に戻る | 空き領域マップ