「SSD時代に `random_page_cost` を疑わないのは罪」という話
PostgreSQLのチューニングにおいて、`random_page_cost` は最も「誤解されやすく、かつ劇的な効果を生む」パラメータの一つです。
PostgreSQLを長年触っているエンジニアなら、`postgresql.conf` のデフォルト値が `4.0` であることはご存知でしょう。この値は、HDD時代、つまり物理的なヘッドのシーク時間を考慮して「ランダムアクセスはシーケンシャルアクセスの4倍コストがかかる」という前提で設計されたものです。
しかし、皆さんの本番環境、いまさらHDDなんて使っていませんよね?
もし、SSDやNVMeストレージ上で稼働しているシステムで、この値をデフォルトのまま放置しているなら、それはデータベースに「嘘の地図」を渡しているようなものです。今日は、なぜこの値がクエリプランナを狂わせるのか、その深層に踏み込んでみたいと思います。
—
なぜプランナはインデックスを「嫌う」のか
`random_page_cost` が高いままだと、オプティマイザはどう考えるか。答えはシンプルです。「インデックススキャンはコストが高いから、いっそ全件シーケンシャルスキャン(Seq Scan)したほうがマシだ」という判断を下します。
特にデータセットがメモリ(`shared_buffers`)に乗り切らないような、中〜大規模なテーブルでこの傾向が顕著です。プランナは、インデックスを辿ってランダムにブロックを読み出すコストを不当に高く見積もり、結果として「全件読み込んでフィルタリングする」という、無駄の多い実行計画を採用してしまいます。
SSD環境における最適値の「正解」
現代のSSD環境であれば、`random_page_cost` は `1.1`、あるいは極限まで攻めるなら `1.0` に設定するのが定石です。
なぜ `1.0` なのか。それは、SSDにおいてはランダムアクセスとシーケンシャルアクセスのコスト差が物理的にほぼ存在しないからです。これを `1.1` や `1.0` に下げることで、プランナの心理は一変します。
「ああ、インデックスを辿るコストは意外と安いんだな。なら、効率的にターゲットを絞り込もう」
こうして、今まで無視されていたインデックスが急に採用され始め、クエリのレスポンスが数ミリ秒に改善される。これがチューニングの醍醐味です。
チューニングに伴う「地雷」の回避
ただし、ここで一つ注意点があります。`random_page_cost` だけをいじって満足してはいけません。
- `seq_page_cost` との相関:
`random_page_cost` を下げたなら、相対的に `seq_page_cost`(デフォルト `1.0`)とのバランスを見直す必要があります。基本的には `1.0` のままで構いませんが、もし皆さんのストレージが非常に高速なら、ここをどう動かすべきか慎重な検証が必要です。
- `effective_cache_size` の再設定:
プランナに「どれくらいOSのキャッシュが効いているか」を教えるこのパラメータもセットで調整してください。ここが過小評価されていると、せっかくインデックススキャンを推奨しても、プランナが「やっぱりメモリに乗らないからコストが高い」と判断してしまうことがあります。
どうやって検証すべきか
実戦でこれを試すときは、必ず `EXPLAIN (ANALYZE, BUFFERS)` を使ってください。
1. 現状の実行計画を確認: 意図しない `Seq Scan` が発生していないか?
2. `SET LOCAL random_page_cost = 1.1;` を発行: セッション単位で値を変更し、再度 `EXPLAIN` を実行。
3. コストの変動と実行計画の変化を比較:
本当に狙い通りのインデックスが選ばれているか? 実際の実行時間は短縮されたか?
このプロセスをサボらないでください。データベースは生き物です。推測で設定値を書き換えるのではなく、プランナの「脳内」を覗き込みながら、納得できるまで追い込む。これこそが、職人としての流儀だと僕は信じています。
—
最後に
「デフォルト値」というものは、あくまで「最悪の環境でも動くための安全圏」に過ぎません。
皆さんの手元にあるその高性能なSSDは、デフォルト設定という名の足かせによって、その真価の半分も発揮できていないかもしれません。ぜひ一度、`random_page_cost` の値を見直し、プランナと対話してみてください。その先には、今まで見えなかった速度域が待っています。
それでは、良いクエリチューニングライフを。
コメント