「SSDなのにインデックスが使われない?」PostgreSQLの`random_page_cost`が引き起こす悲劇
現場でPostgreSQLを触っていると、たまにこんな相談を受けます。
「クエリが全然インデックスを使ってくれないんです。Explainで見ると、わざわざ重いシーケンシャルスキャン(全件走査)を選んでいて……」
インデックスはちゃんと貼ってある。データ量もそこそこある。なのにPostgreSQL様が「いや、全件読み込んだ方が速いっすよ」と判断してしまっている。これ、実はPostgreSQLの歴史的背景と、今のハードウェアの進化が噛み合っていない時に起きる典型的な「あるある」なんです。
今日は、そんな時に真っ先に疑うべき設定、`random_page_cost`の話をしましょう。
—
なぜデフォルトの「4.0」は現代のSSDと相性が悪いのか
PostgreSQLのオプティマイザは、クエリの実行計画を立てる際、「このデータを取るのにどれくらいコストがかかるか」を計算します。その計算の基準となるのが設定値です。
その中の一つである `random_page_cost` は、ディスク上のランダムアクセス(インデックススキャンなど)にかかるコストの重み付けです。
実は、この値のデフォルトは「4.0」。
これ、元々は「HDD(回転ディスク)」の特性を前提に決められた値なんです。昔のHDDはヘッドを物理的に動かさないといけなかったので、シーケンシャルアクセスに比べてランダムアクセスは圧倒的に遅かった。だから「ランダムアクセスはシーケンシャルの4倍くらい重いぞ」と教えてあげていたわけです。
でも、今はSSDの時代ですよね?
SSDにおいて、ランダムアクセスとシーケンシャルアクセスの速度差は、HDDほど極端ではありません。それなのに設定値が「4.0」のままだと、PostgreSQLは未だに「ランダムアクセスはすごくコストが高いから、なるべく避けてシーケンシャルスキャンしよう」と判断してしまいます。
結果として、本来ならインデックスを使ってサクッと終わるはずのクエリが、わざわざ重いフルスキャンを選択してしまう……これが「SSDなのに遅い」の正体です。
—
実践:どれくらいまで下げるべきか
SSD環境なら、この値を 「1.1」〜「1.5」 程度まで下げるのが定石です。
もしAWSのEBS(特にio1やgp3)や、一般的なNVMe SSDを使っているなら、まずは「1.1」で試してみることをおすすめします。これだけで、クエリプランナーの判断が劇的に変わることがあります。
設定の確認と変更方法
今の設定値を確認してみましょう。
— 現在の設定値を確認
SHOW random_page_cost;
もし「4.0」と表示されたら、設定ファイル(postgresql.conf)またはセッション単位で変更を検討します。
— セッション単位でテストしてみる(本番適用前に効果を検証!)
SET random_page_cost = 1.1;
— EXPLAIN ANALYZE でプランが変わるかチェック
EXPLAIN ANALYZE SELECT FROM users WHERE last_login > ‘2023-01-01’;
もしこれで、期待通りに `Index Scan` が選ばれるようになったら大成功です。
—
注意点:魔法の杖ではない
勘違いしてほしくないのは、「下げれば下げるほど速くなるわけではない」 ということ。
`random_page_cost` を極端に下げすぎると、オプティマイザが「インデックススキャンこそ正義!」と思い込みすぎて、逆に効率の悪いインデックススキャンを強行してしまうケースもあります。
特に以下の点には注意してください。
- 統計情報は最新か?: `ANALYZE` を実行して統計情報を最新にしておかないと、コスト計算の前提が崩れます。
- seq_page_costとの兼ね合い: ほとんどの場合 `random_page_cost` だけで解決しますが、あまりに大規模なテーブルを扱う場合は、`seq_page_cost`(デフォルト1.0)との相対的なバランスを見るのがエンジニアの腕の見せ所です。
- グローバル設定には慎重に: `postgresql.conf` で全体を変えるのが怖いなら、まずは特定のデータベース単位や、特定のクエリ(ヒント句のような使い方はできませんが、セッション単位の設定変更)で試すのが安全です。
—
先輩からのアドバイス
データベースのチューニングは、教科書通りの数値を当てはめる作業ではありません。「今のハードウェア構成なら、この値が妥当かな?」と仮説を立てて、`EXPLAIN ANALYZE` という鏡を見ながら調整していく……いわば職人芸に近いものです。
「なんか最近インデックス使ってくれないな」と思ったら、まずはこの `random_page_cost` を疑ってみてください。OSやディスクの進化に合わせて、データベースの設定もアップデートしてあげる。それが、長く付き合うDBを健康に保つコツですよ。
さて、そろそろログを見て、スロークエリの改善に戻りますか。一緒に頑張りましょう!
コメント