Article

PostgreSQLのrandom_page_costをSSDだけで決めない理由

random_page_costは、キャッシュを含む読み取りの見積もりを実行計画へ反映する設定です。seq_page_costとの関係、行の並びとの相関、テーブルスペースの上書きを押さえ、1.1へ下げる前に見るべき条件を整理します。

Share

こはるの読みどころ

インデックスが選ばれたかだけでなく、なぜ安く見積もられたかまで追ってみよう。設定値と実行時間を分けて読むのがポイントだよ。

こはるの読みどころ

PostgreSQLでインデックスが選ばれないとき、random_page_costを下げるという助言を見かけます。Christophe Pettus氏の設定解説は、その数値をストレージの速さだけで読んでよいのか、という問いを投げかけています。

SSDを使っていても、必要なデータがメモリにある場合と、広い範囲をストレージから読む場合では事情が違います。同じ設定変更が、ある検索には効いても別の検索には合わない理由がここにあります。

判断の手掛かりは、キャッシュ、テーブル内の行の並び、実際に適用される設定です。PostgreSQL 18の公式資料で仕組みを補いながら、数値を変える前に見るべきところをつなげていきます。

既定値4.0にはキャッシュの前提も含まれる

プランナは実行方法を比較するために、読み取りやCPU処理へコストを割り当てます。逐次的なページ取得の基準が seq_page_cost、非逐次的な取得の見積もりが random_page_cost で、既定値はそれぞれ 1.0 と 4.0 です。これらは相対的な尺度です。公式のコスト定数の説明で定義されています。

この4倍という比率を、そのまま機器の読み取り速度の比率とは扱えません。キャッシュにあるページを読むことも織り込まれているからです。

説明の沿革にも、その性格が表れています。2025年10月30日の公式ドキュメント改訂では、ランダム読み取りを40倍、キャッシュ命中率を90%とする例と、SSD向けの1.1という例が削除されました。キャッシュの説明は残り、ネットワーク接続ストレージの遅延についての説明が加わっています。これは説明文の改訂で、既定値の変更ではありません。

Pettus氏は、ワーキングセットがメモリに収まるSSD上のOLTPに1.1を勧めています。ここでのワーキングセットは、処理が繰り返し参照するデータの範囲です。この条件付きの運用上の助言を、すべてのSSD環境に共通する推奨値へ広げないことが出発点ですね。

行の物理的な並びでインデックス走査の見積もりが変わる

通常のインデックス走査では、インデックスで見つけた行をテーブル本体、つまりヒープから取得します。その行が物理的にも近くに並んでいれば、ばらばらの場所にある場合とはページ取得の見積もりが変わります。

その手掛かりが pg_stats.correlation です。列の値の順序と物理的な行の順序の相関を表し、範囲は−1から+1です。0付近よりも±1付近のほうが、インデックス走査を安く見積もる材料になります。pg_statsの定義で確認できます。

PostgreSQL 18のコスト計算実装では、通常の単発のインデックス走査について、相関がない場合と完全にある場合のヒープI/Oコストを求め、相関の二乗で補間します。完全に相関する場合は、最初のページをランダム、その後を逐次取得として計算します。

違いを数値で見るため、両方とも100ページを取得すると仮定し、ヒープI/O部分だけを計算してみます。これは実測値ではなく、計算式を説明する例です。

仮定したページ取得 random_page_cost = 4.0 random_page_cost = 1.1
100ページをランダム取得 100 × 4.0 = 400 100 × 1.1 = 110
最初の1ページだけランダム、残り99ページは逐次取得 4.0 + 99 × 1.0 = 103 1.1 + 99 × 1.0 = 100.1

seq_page_cost は1.0に固定しています。実際には取得ページ数の推定も異なり、インデックス自体のアクセスやCPU処理も加わるため、この表は走査全体のコストではありません。それでも、同じ設定変更の効き方がデータの並びで変わることは分かります。

SHOWだけでなくテーブルスペースの上書きも調べる

仕組みが分かったら、比較の前に現在の設定を押さえます。SHOW は実行時パラメータを表示しますが、テーブルスペースに設定されたページコストは別に確認する必要があります。SHOWの仕様とALTER TABLESPACEの上書き規則を合わせて読むと、この違いが分かります。

次のSQLは設定とカタログを読むだけで、設定変更は行いません。spcoptions はpg_tablespaceに保存されたオプションです。

SQL
-- セッションで見えるページコストを確認する
SHOW random_page_cost;
SHOW seq_page_cost;

-- テーブルスペース単位の上書きを確認する
SELECT spcname, spcoptions
FROM pg_tablespace
ORDER BY spcname;

対象テーブルやインデックスのテーブルスペースに上書きがあれば、その値が使われます。セッションで値を変えても思ったように計画が動かないときは、適用先の違いを先に切り分けたいところです。

1.1を試すなら推定コストと実行時間を分けて比べる

値を下げてインデックス走査が選ばれても、それだけでは高速化の確認になりません。EXPLAINの読み方に沿って、推定行数と実行時の行数、実行時間、バッファ利用を並べて見ます。EXPLAIN (ANALYZE, BUFFERS) は実際にクエリを実行するので、検証環境で代表的な読み取りクエリを対象にします。

比較は次の順序にすると、設定変更と観測結果を結び付けやすくなります。

  1. 現在の設定で計画と実行結果を保存し、推定行数が実行時の行数から大きく外れていないかを見る。
  2. テーブルスペースの上書きを確認したうえで、トランザクション内の SET LOCAL random_page_cost = 1.1; を使って同じクエリを比較する。SET LOCALの効果は、そのトランザクションの終了までです。
  3. 一つの検索だけで決めず、少数行を探す検索と広い範囲を読む検索を含め、実行時間の改善と悪化を記録する。

バッファの数字にも境界があります。shared hit は共有バッファで見つかったことを示しますが、read が必ず物理ストレージへの読み取りを意味するわけではありません。OSのページキャッシュから供給される場合もあり、公式のI/O統計の説明も、OS側の観測を組み合わせるよう案内しています。

random_page_costを決める問いは「SSDかどうか」から、「このワークロードのページ取得をどう見積もるか」へ進みます。行の並びと適用設定を確かめ、代表的な処理の実行時間で判断すれば、1.1は丸暗記する答えではなく、検証する候補として使えます。

出典

  • Title: Christophe Pettus: All Your GUCs in a Row: random_page_cost and seq_page_cost
  • URL: https://postgr.es/p/9yN

Share

Related Articles

カテゴリやタグが近い記事を続けて読めるように並べています。