【実務・中級編】 TOASTストレージの仕組み – PostgreSQL

PostgreSQLの「隠れた巨大データ」問題:TOASTと上手く付き合うための設計術

やあ。データベースのチューニングに頭を悩ませているなら、ちょうどいいタイミングだ。

今日はPostgreSQLの「TOAST(トースト)」という仕組みについて話そうと思う。これ、名前は美味しそうだけど、実務では「知らないと泣きを見る」地雷ポイントの筆頭なんだ。

インデックスを貼ってもクエリが遅い、テーブルサイズが異常に膨れ上がる……そんな現象に遭遇したことはないか? それ、十中八九TOASTが原因だ。現場のエンジニアとして、この「透明な巨大領域」とどう向き合うべきか、深掘りしていこう。

—

TOASTって結局なんなのか?

PostgreSQLのテーブルには「8KB」というページサイズの制限がある。1行のデータがこの8KBを超えてしまったらどうなるか? そこで登場するのがTOAST(The Oversized-Attribute Storage Technique)だ。

簡単に言うと、「8KBを超えるような大きなデータは、メインのテーブル領域とは別の場所(TOASTテーブル)に追い出して、元のテーブルにはその参照先だけを置いておく」という仕組みだ。

例えば、`TEXT`型で数メガバイトのログやJSONBを放り込むと、PostgreSQLは自動的にそれを圧縮し、細切れにして別の領域へ追いやる。このおかげで、行のサイズ制限を気にせずデータを突っ込めるわけだ。

—

なぜこれが「インデックス設計」の敵になるのか

ここからが本題だ。TOASTには「代償」がある。

1. インデックスの肥大化
TOASTされたデータに対しても、一応インデックスは貼れる。しかし、データが巨大であればあるほど、インデックスの構築や更新コストは跳ね上がる。何より、インデックスそのものが巨大化してメモリ(Buffer Cache)を圧迫し、他のクエリの足を引っ張ることになる。

2. I/Oのオーバーヘッド
クエリで「その列」を参照しようとすると、メインテーブルを読んだ後に、TOASTテーブルへわざわざデータを取りに行く必要がある。つまり、1回のアクセスで物理的なディスクI/Oが複数回発生するんだ。これが重なると、CPU以前にストレージI/Oがボトルネックになる。

—

実践:TOASTを味方につける戦略

もし君が、「ログやリクエストボディを格納するテーブル」を設計しているなら、以下のプラクティスを思い出してほしい。

1. JSONBのインデックスは「必要最小限」に

JSONBにインデックスを貼るとき、`CREATE INDEX ON table USING GIN (data);` とやると、データ全体をインデックス化する。これはTOASTと相性が最悪だ。
特定のキーだけをターゲットにするインデックスにするか、あるいは`jsonb_path_ops`を使ってインデックスサイズを絞り込むのが鉄則だ。

— 全体をインデックスするのではなく、特定のパスだけを対象にする
CREATE INDEX idx_user_prefs_theme ON user_settings
USING GIN ((data->’theme’));

2. STORAGE戦略を使い分ける

PostgreSQLでは、列ごとに「どう保存するか」を指定できる。デフォルトは`MAIN`(圧縮して入らなければTOAST)だが、絶対に圧縮したくない、あるいは逆に必ず圧縮してほしい場合など、`ALTER TABLE`で制御できる。

— この列は絶対に圧縮せず、そのままTOASTへ追い出せ(CPU負荷を下げたい場合)
ALTER TABLE logs ALTER COLUMN raw_data SET STORAGE EXTERNAL;

— 圧縮優先(ディスク容量を節約したい場合)
ALTER TABLE logs ALTER COLUMN raw_data SET STORAGE EXTENDED;

3. 「分ける」という勇気

もしその巨大なデータが、頻繁に更新される他の列と一緒にテーブルに存在しているなら、設計を見直そう。
「メインテーブル(IDや検索用)」と「データテーブル(巨大な本文やJSON)」を1対1で切り出して、必要な時だけJOINする。これだけでメインテーブルのページ密度が上がり、スキャン性能が劇的に改善するはずだ。

—

最後に:エンジニアとしての心得

TOASTを悪者扱いする必要はない。むしろ、これがあるからこそ、PostgreSQLは柔軟なデータ構造を扱える。

でも、「とりあえずTEXT型で突っ込んでおけばいいや」という安易な設計は、数年後の自分やチームメンバーを確実に苦しめることになる。

  • そのデータ、本当に検索条件に使うのか?
  • インデックスを貼るサイズは適切か?
  • テーブルを分けたほうが運用は楽じゃないか?

コードを書く前に、この3つを自問自答してみてくれ。データベースは、君の意図を忠実に実行する正直な相棒だ。その特性を理解してあげれば、これほど頼もしい武器はないよ。

それじゃ、また現場で会おう。良い設計を!

コメント

タイトルとURLをコピーしました