PostgreSQLの「Fillfactor」を制する者は、高負荷時のパフォーマンスを制す
やあ、最近PostgreSQLのチューニングに頭を悩ませているみたいだね。
「INSERTは速いのに、UPDATEが絡むと急にクエリが遅くなる……」
「VACUUMが追いつかなくて、テーブルが肥大化し続けている……」
もし君がそんな現場にいるなら、今日話す「Fillfactor(フィルファクタ)」という設定は、まさに救世主になるはずだ。教科書には載っているけれど、案外みんな「デフォルトのままでいいか」とスルーしがちなこのパラメータ。でも、高負荷な環境で戦うなら、ここをいじれるかどうかでエンジニアとしての「引き出しの深さ」が変わってくるよ。
Fillfactorって結局なに?
簡単に言うと、「データページ(8KB)の中に、どれだけぎっしりデータを詰め込むか」を決めるパーセンテージのことだ。
PostgreSQLのデフォルト値は100。つまり、ページ内にデータがパンパンに詰まるまで詰め込む設定だ。でも考えてみてほしい。もしその行をUPDATEしたらどうなる? 行のサイズが増えた場合、そのページに空きがなければ、PostgreSQLは泣く泣く「ページ分割(Page Split)」を行うことになる。
これが曲者なんだ。ページ分割はI/O負荷が高いし、断片化の原因にもなる。
そこでFillfactorの出番だ。例えば「90」に設定すれば、あえて10%の空きを残してページを作成する。「後からUPDATEでサイズが変わっても、その場で書き込めるように余白を残しておく」という、いわば「ゆとり設計」だね。
実践:どういう時に設定すべき?
闇雲に下げればいいわけじゃない。下げすぎればディスク使用量は増えるし、スキャン効率も落ちる。僕が現場で判断基準にしているポイントを教えるよ。
1. UPDATEが頻繁に発生するテーブル
これが一番重要だ。特に「残高情報」や「ステータス更新」のような、頻繁にカラムが書き換わるテーブル。
ALTER TABLE accounts SET (fillfactor = 80);
こうすることで、UPDATE時に「そのページ内に収まる(HOTアップデートが効く)」確率が劇的に上がる。HOT(Heap Only Tuple)アップデートが効けば、インデックスの更新も不要になるから、パフォーマンス向上は絶大だ。
2. インデックスに対するFillfactor
実はインデックスにもFillfactorは設定できるんだ。インデックスの更新頻度が高い(頻繁にキーが変わる)場合は、ここを調整するのが定石だね。
CREATE INDEX idx_user_status ON users (status) WITH (fillfactor = 70);
インデックスページが頻繁に分割されると、ツリー構造が歪んで検索速度が落ちる。書き込み負荷が高いインデックスなら、少し余裕を持たせてやるのが優しさだ。
現場で気をつけるべき「落とし穴」
ここまで聞くと「じゃあ全テーブル80にしておけば安心だ!」と思うかもしれないけど、ちょっと待って。いくつか注意点がある。
- SELECTメインのテーブルには不要: 読み取り専用に近いテーブルでFillfactorを下げると、単純にページ数が増えて読み込み効率が悪化するだけだ。デフォルトの100のままでいい。
- 適用タイミング: `ALTER TABLE`でFillfactorを変更しても、既存のデータには反映されないんだ。反映させるには `VACUUM FULL` か、あるいはテーブルを作り直す必要がある。計画的にやらないと、本番環境で「あれ、速くならない?」って焦ることになるよ。
- バランスが命: 基本的には90〜100の間で調整するのが無難だ。80以下に下げるのは、相当な書き込み負荷を観測した後の「最終手段」と考えておこう。
まとめ:魔法の杖ではないが、強力な武器になる
Fillfactorの最適化は、パズルのピースを埋める作業に似ている。闇雲に設定を変えるんじゃなくて、まずは `pg_stat_user_tables` の `n_tup_upd` (更新数)や、HOT更新の比率を調べて、「本当に余白が必要か?」をデータで裏付けよう。
「なんとなく設定する」のと「理由があって設定する」のとでは、トラブルが起きた時の対応力が全く違うからね。
もし君のデータベースが、書き込みの嵐の中で悲鳴を上げているなら、ぜひ一度この「ゆとり」を試してみてほしい。きっと、PostgreSQLが少しだけ軽やかに動いてくれるようになるはずさ。
それじゃ、また何かあったら相談してくれ。コードの先にある「動いている仕組み」を理解する楽しさを、これからも忘れずにいこうぜ。
コメント