プログラミング
random_page_costに関するさらなる考察
Some more thoughts on random_page_cost (vondra.me)
要約
PostgreSQLの`random_page_cost`パラメータは、ストレージのランダムI/Oコストをモデル化しますが、SSDの普及によりそのデフォルト値(4.0)が実情に合わなくなっています。著者は、`random_page_cost`が単なるI/Oコストではなく、メモリ使用量、キャッシュ効果、アクティブセットの局所性といった、コストモデルが捉えきれていない他の要因を補償する役割を果たしていると考察しています。そのため、このパラメータの調整はシステム固有であり、プロファイリング結果だけでなく、実際のクエリパフォーマンスの監視に基づいて行う必要があると結論付けています。
全文翻訳
Tomas Vondraのブログ
Postgresのコードとコミュニティについて
random_page_costに関するさらなる考察
数ヶ月前、現在のストレージがランダムアクセスとシーケンシャルアクセスをどのように処理するかをより良く反映するようにrandom_page_costを調整することについて投稿しました。その後、このトピックについて多くの素晴らしい議論がありましたが、結局他のことに気を取られてしまいました。
POSETTEが先週開催され、このトピックに関する私の事前録画された講演(そして他にも素晴らしい講演がたくさんありました)がありました。それで、random_page_costについて少し違う考え方を始めたことを思い出しました。そこで、さらにいくつかの考察を加えて更新します。
以前の投稿やPOSETTEでの講演で、これらのことの一部にすでに触れています。
それに入る前に、回転式SATAドライブからのrandom_page_costの結果を示すチャートを共有させてください。メンテナンスのためにケースを開けるまで、これらのディスクがマシンに入っていることを完全に忘れていました。これらは、約2000年頃の元の実験で使用されたストレージに近い可能性が高いです。それらはPATAまたはSCSIドライブだったかもしれませんが、それでも回転式です。おそらく、4.0のデフォルトに近い値が得られるでしょうか?
明らかにそうではありません。実際、推定random_page_costは約125で、SSDストレージの推定値の2〜4倍です。したがって、SSDではデフォルトに近づいていますが、それは偶然です。
古い実験の根本的な部分で、私たちが思い出せなかったものがあるのでしょうか?あるいは、「生の」結果は何らかの方法で調整されたのでしょうか?しかし、4.0のデフォルト値が生のランダムI/Oコストであったことはないようです。
過去にrandom_page_costを増やそうとした人々から多くのフィードバックを得ました。彼らの経験では、パフォーマンスが向上することはなく、むしろ悪化しました。コストモデルの精度が低下するのに、これはどのようにして可能なのでしょうか?
私は、random_page_costがコストモデルの不完全さを「補償」しているのだと信じています。これは、多数のランダムI/Oを実行するプランに関連する様々なキャッシュ効果やリソースを考慮していません。
すべてのコストモデルは近似であり、比較的粗雑なものです。あらゆる些細な詳細を正確に模倣できる、高速で安価なコストモデルを持つことは不可能です。より詳細にすることはできますが、ある時点では元のシステムと同じくらい大きくなるでしょう。そして、なぜモデルを持つ必要があるのでしょうか?それは非常に役に立たないでしょう。
私たちのコストモデルには、ここで重要だと思われるギャップがいくつかあります。
コストモデルは(主に)メモリを無視する
操作のコストは、操作の実行に使用されるCPUとI/Oの量から計算されます。これらは2つの主要なリソースですが、メモリを無視しています。
work_memはありますが、それはより安全な制限であり、ワークバッファ(例:ソートやハッシュ用)のサイズを制限します。そして、それは操作が必要とするI/Oの量に影響を与える可能性があります(例:ハッシュ結合のバッファが小さいと、ディスクへのスピルが増える)。
しかし、プランによって「使用」される他のメモリを追跡するわけではなく、メモリの他の使用を無視します。
100GBのテーブルがあり、その中に1GBの「興味深い」データ(クエリ述語に一致する)が含まれているとします。テーブルをシーケンシャルにスキャンするか、インデックスを介してスキャンできます。テーブルが「コールド」の場合、シーケンシャルスキャンはメモリ(共有バッファまたはページキャッシュ)から約100GBの他のデータをプッシュする可能性があります。クエリはwork_mem=4MBで問題なく実行されるかもしれませんが、実質的に100GBのメモリを「使用」する可能性があります。
一方、インデックスはテーブルの約1%(興味深いデータの1GB)にしかアクセスする必要がないかもしれません。ランダムI/Oはシーケンシャルスキャンよりも時間がかかる可能性がありますが、一方でメモリは1GBしか「使用」しません。キャッシュから他のデータを追い出すのはずっと少なくなるかもしれません。
しかし、コストモデルはこれに完全に気づいていません。
アクセスの局所性
もう一つの理由は、アクティブセットという概念です。1TBのデータベースがあるかもしれませんが、ほとんどの実用的なシステムでは、データのほんの一部しかアクセスされません。ユーザーは最新の注文などしか見ません。これが私たちがデータベースの「アクティブセット」と呼ぶものです。
鍵は、アクティブセットをメモリに保持することです。そして、多くのランダムI/Oを実行するプランは、より局所的である傾向があります。つまり、「興味深い」データのみにアクセスします。インデックススキャンは、通常、シーケンシャルスキャンよりもはるかに少ないデータ量にアクセスします。I/Oはより高価になる可能性があります(ランダム、複数回ページを訪問する)、しかしそれは「興味深い」データだけです。
シーケンシャルスキャンはアクティブセットを大幅に拡大する可能性があります - おそらくデータベース全体に。これは、実際にデータベース全体を収容するのに十分なRAMがない限り、あまり良くありません。インデックススキャンは、はるかに小さいアクティブセットを可能にします。
しかし、プランナーはアクティブセットを全く認識していません。すべてのクエリがコールドデータから始まると仮定してクエリを計画します。キャッシュ効果は非常に限定的な場合にのみ考慮されます。例えば、同じクエリ内でのキャッシュです。
プランナーは、他の多くのことにも気づいていません。他のクエリが同じリソースを競合しておらず、帯域幅が無限であるかのように、クエリを個別に計画します。シーケンシャルスキャンは単一のバックエンドには最適かもしれませんが、100個のバックエンドすべてがシーケンシャルスキャンを実行している場合、ストレージ帯域幅に達します。
結論
私は今、random_page_costをこれらの効果すべてのプロキシとして見ています。
プランナーは、CPU/ディスクと同じようにメモリをコスト計算していませんし、キャッシュ効果も理解していません。低いrandom_page_cost値は、より局所的でアクティブセットを制御下に保つプランへとそれを押しやります。長期的には、これが勝利戦略のように思えます。
これは、ストレージをプロファイリングし、シーケンシャルI/OとランダムI/Oの処理方法を測定した後に「正しい」random_page_cost値を示してくれるツールは存在し得ないことを意味します。私はそれを望んでいますが、random_page_costが生のI/Oコストではない場合、それは機能しないでしょう。
random_page_costのチューニングは、監視からのフィードバックによって駆動される、システム固有のものでなければなりません。例えば、pg_stat_statementsで上位のクエリを確認し、それらがより低いrandom_page_cost値から恩恵を受けるかどうかを確認できます。次に、値を調整してみて、それが望ましい影響を与えるか(または他のクエリを損なうか)を監視します。
2026年6月23日 パフォーマンス random_page_cost コスト計算 ランダムI/O チューニング AIOプリフェッチ postgres
この投稿についてフィードバックはありますか? tomas@vondra.meまでメールでご連絡ください。