「またTOASTでハマってるのか? まあ、無理もないよ。PostgreSQLを触り始めて一番最初に『魔法の箱』だと思って、そのあと『実は厄介な地雷』だと気づくのがこのTOASTだからね」
そんな声が聞こえてきそうな気がしたので、今日は少し腰を据えて、PostgreSQLの「巨大データ処理の裏方」であるTOASTについて話をしようと思う。教科書的な定義はドキュメントに譲るとして、現場で「なぜ遅いのか」「なぜインデックスが効かないのか」を解決するための、実践的な勘所を共有するよ。
—
TOASTは「溢れたデータの避難所」だ
まず基本のおさらい。PostgreSQLには「1ページ(デフォルト8KB)に全データを詰め込む」という鉄則がある。でも、数MBのJSONや長文テキストを保存しようとしたらどうなる? ページに収まりきらないよね。
そこで登場するのが TOAST (The Oversized-Attribute Storage Technique) だ。
ページから溢れそうなデータを見つけると、PostgreSQLは気を利かせてデータを別テーブル(TOASTテーブル)に追い出し、メインテーブルには「ポインタ」だけを残してくれる。
これが何を意味するか。「メインテーブルの検索速度を維持しつつ、巨大データも扱える」という、エンジニアにとっての夢のような仕組みなんだ。でも、ここには落とし穴がある。
—
「しきい値」を意識したことはあるか?
TOASTが発動するデフォルトのしきい値は「2KB」だ。つまり、2KBを超える列があると、PostgreSQLは自動的に圧縮や外部格納を検討し始める。
ここで現場の知恵を一つ。もし、頻繁にアクセスする列が2KBギリギリのサイズで、TOASTに押し出されたり戻ったりを繰り返すと、クエリのたびに「ポインタを辿って別のテーブルを読みに行く」というオーバーヘッドが発生する。
そんな時は、`STORAGE`戦略を調整するのも手だ。
— 特定のカラムだけTOASTの挙動を変える
ALTER TABLE my_table ALTER COLUMN large_json SET STORAGE EXTERNAL;
`EXTENDED`(デフォルト)だと「圧縮+外部格納」だけど、CPU負荷を下げたい場合や、すでに圧縮済みのデータを入れる場合は、`EXTERNAL`(圧縮なし・外部格納のみ)を選ぶだけで、CPUのリソースを節約できる場面がある。
—
インデックス性能への「見えないダメージ」
ここからが一番伝えたいことだ。「TOASTされた列にインデックスを貼る」という行為について。
みんなやりがちなんだけど、数MBのJSON全体にそのままインデックスを貼ろうとすると、インデックスサイズが巨大化して、メモリ(shared_buffers)を食いつぶすことになる。結果、インデックス検索どころか、スキャンそのものが遅くなる。
解決策:インデックスの「的」を絞れ
巨大なデータを扱うときは、必ず「必要な部分だけ」をインデックス化する癖をつけよう。
— JSONの特定のキーだけインデックスにする(これなら軽量だ)
CREATE INDEX idx_json_user_id ON my_table ((data->>’user_id’));
こうすれば、TOASTされた本体をわざわざロードしなくても、インデックスツリーだけで検索が完結する。インデックス設計の鉄則は、「いかにポインタを辿らせないか」だ。
—
現場で役立つチェックリスト
TOASTが原因でパフォーマンスが悪化しているかも?と思ったら、以下のコマンドで現状を確認してみてくれ。
— どのテーブルがTOASTテーブルを使っているか確認する
SELECT relname, reltoastrelid FROM pg_class WHERE reltoastrelid != 0;
— 特定のテーブルのストレージ情報を確認
SELECT attname, attstorage FROM pg_attribute
WHERE attrelid = ‘my_table’::regclass;
もし、特定のテーブルの読み込みが異常に遅いなら、`pg_toast` スキーマにあるテーブルの肥大化を疑うべきだ。`VACUUM`が効いていないと、TOASTテーブルはどんどん太っていく。
—
最後に:エンジニアとしてのアドバイス
「データベースに何でも詰め込める」というのはPostgreSQLの強みだけど、「詰め込みすぎないのが腕の見せ所」でもある。
- 巨大データは、本当にDBに保存する必要があるのか?
- S3のようなオブジェクトストレージに逃がして、DBにはパス(URI)だけ持つべきではないか?
こういう判断を設計段階で下せるのが、プロのエンジニアだ。TOASTは便利な魔法だけど、それに頼りすぎると、後で泣くのは自分自身だよ。
もし今、パフォーマンス問題に悩んでいるなら、まずは`EXPLAIN (ANALYZE, BUFFERS)`を引いてみてくれ。`Heap Fetches`が異常に高かったら、それはTOASTが悲鳴を上げている証拠だ。
さて、今日はここまで。また現場で困ったことがあったら聞きに来てくれ。一緒に最高にクールなDBアーキテクチャを作っていこう。
コメント