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つを自問自答してみてくれ。データベースは、君の意図を忠実に実行する正直な相棒だ。その特性を理解してあげれば、これほど頼もしい武器はないよ。
それじゃ、また現場で会おう。良い設計を!
コメント