PostgresEXPLAINのコストを理解する
EXPLAIN
Postgresクエリのパフォーマンスを理解するのに非常に役立ちます。指定されたステートメントに対してPostgreSQLクエリプランナーによって生成された実行プランを返します。 EXPLAIN
コマンドは、ステートメントで参照されるテーブルをインデックススキャンとシーケンシャルスキャンのどちらで検索するかを指定します。
EXPLAIN
の出力を確認するときに最初に気付くことがいくつかあります コマンドはコスト統計であるため、それらが何を意味するのか、どのように計算されるのか、どのように使用されるのか疑問に思うのは自然なことです。
つまり、PostgreSQLクエリプランナーは、起動コストと各操作の合計コストの両方を使用して、クエリにかかる時間を(任意の単位で)見積もります。これについては後で詳しく説明します。クエリを実行するための複数のオプションがある場合、これらのコストを使用して、最も安価な、したがってできれば最速のオプションを選択します。
費用はどの単位ですか?
コストは任意単位です。 よくある誤解は、ミリ秒またはその他の時間単位であるというものですが、そうではありません。
コスト単位は(デフォルトで)1.0単位のコストの単一のシーケンシャルページ読み取りに固定されています(seq_page_cost
)。処理される各行は0.01(cpu_tuple_cost
)、および非シーケンシャルページの読み取りごとに4.0(random_page_cost
)。このような定数は他にもたくさんあり、すべて構成可能です。最後の1つは、少なくとも最新のハードウェアでは特に一般的な候補です。これについては、もう少し詳しく説明します。
初期費用
cost=
の後に表示される最初の数字 「初期費用」として知られています。これは、最初の行をフェッチするのにかかる時間の見積もりです。 。そのため、オペレーションの初期費用には、その子の費用が含まれます。
シーケンシャルスキャンの場合、行のフェッチをすぐに開始できるため、起動コストは通常ゼロに近くなります。ソート操作の場合、行が返され始める前に作業の大部分を実行する必要があるため、より高くなります。
例を見て、1000人のユーザー名で簡単なテストテーブルを作成しましょう:
CREATE TABLE users ( id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY, username text NOT NULL); INSERT INTO users (username) SELECT 'person' || n FROM generate_series(1, 1000) AS n; ANALYZE users;
いくつかの操作を含む簡単なクエリプランを見てみましょう。
EXPLAIN SELECT * FROM users ORDER BY username; QUERY PLAN | --------------------------------------------------------------+ Sort (cost=66.83..69.33 rows=1000 width=17) | Sort Key: username | -> Seq Scan on users (cost=0.00..17.00 rows=1000 width=17)|
上記のクエリプランでは、予想どおり、Seq Scan
の推定ステートメント実行コスト 0.00
です 、およびSort
の場合 66.83
です 。
総費用
起動コストと2つのドットに続く、2番目のコスト統計は、「合計コスト」として知られています。これは、すべての行を返すのにかかる時間の見積もりです。 。
そのクエリプランの例をもう一度見てみましょう:
QUERY PLAN | --------------------------------------------------------------+ Sort (cost=66.83..69.33 rows=1000 width=17) | Sort Key: username | -> Seq Scan on users (cost=0.00..17.00 rows=1000 width=17)|
Seq Scan
の総コストがわかります 操作は17.00
です 。 Sort
の場合 運用は69.33であり、これは(予想どおり)起動コストをはるかに上回っていません。
総コストには通常、先行する操作のコストが含まれます。たとえば、上記の並べ替え操作の合計コストには、シーケンススキャンのコストが含まれます。
重要な例外はLIMIT
です プランナーが早期に中止できるかどうかを推定するために使用する句。必要な行数が少なく、条件が一般的である場合は、スキャンの選択が単純な方が安価であると計算される場合があります(高速になる可能性があります)。
例:
EXPLAIN SELECT * FROM users LIMIT 1; QUERY PLAN | --------------------------------------------------------------+ Limit (cost=0.00..0.02 rows=1 width=17) | -> Seq Scan on users (cost=0.00..17.00 rows=1000 width=17)|
ご覧のとおり、Seq Scanノードで報告される総コストは17.00のままですが、Limit操作の全コストは0.02であると報告されています。これは、プランナーが1000行のうち1行を処理するだけでよいと予測しているためです。したがって、この場合のコストは、合計の1000分の1と見積もられます。
コストの計算方法
これらのコストを計算するために、Postgresクエリプランナーは定数(一部はすでに確認済み)とデータベースのコンテンツに関するメタデータの両方を使用します。メタデータは「統計」と呼ばれることがよくあります。
統計はANALYZE
を介して収集されます (EXPLAIN
と混同しないでください 同じ名前のパラメータ)、pg_statistic
に保存されます 。また、自動真空の一部として自動的に更新されます。
これらの統計には、各テーブルの行数や各列の最も一般的な値など、非常に役立つものがいくつか含まれています。
以前と同じクエリデータを使用して、簡単な例を見てみましょう。
EXPLAIN SELECT count(*) FROM users; QUERY PLAN | -------------------------------------------------------------+ Aggregate (cost=19.50..19.51 rows=1 width=8) | -> Seq Scan on users (cost=0.00..17.00 rows=1000 width=0)|
この場合、プランナーの統計によると、テーブルのデータは7ページ(またはブロック)内に保存されており、1000行が返されます。コストパラメータseq_page_cost
、cpu_tuple_cost
、およびcpu_operator_cost
デフォルトの1
のままにしました 、0.01
、および0.0025
それぞれ。
そのため、SeqScanの総コストは次のように計算されました。
Total cost of Seq Scan = (estimated sequential page reads * seq_page_cost) + (estimated rows returned * cpu_tuple_cost) = (7 * 1) + (1000 * 0.01) = 7 + 10.00 = 17.00
そして、次のような骨材の場合:
Total cost of Aggregate = (cost of Seq Scan) + (estimated rows processed * cpu_operator_cost) + (estimated rows returned * cpu_tuple_cost) = (17.00) + (1000 * 0.0025) + (1 * 0.01) = 17.00 + 2.50 + 0.01 = 19.51
プランナーがコストをどのように使用するか
Postgresが合計コストが最も低いクエリプランを選択することがわかっているので、それを使用して、Postgresが行った選択を理解しようとすることができます。たとえば、クエリで期待するインデックスが使用されていない場合は、enable_seqscan
などの設定を使用できます。 特定のクエリプランの選択を大幅に阻止します。この時点で、このような設定がコストを増やすことで機能すると聞いても驚かないでください!
行番号は、コスト見積もりの非常に重要な部分です。これらは、さまざまな結合順序、結合アルゴリズム、スキャンタイプなどの推定値を計算するために使用されます。行のコスト見積もりが大量に発生すると、コスト見積もりが大量に発生する可能性があり、最終的には最適ではないプランの選択が行われる可能性があります。
EXPLAINANALYZEを使用してクエリプランを取得する
PostgreSQLでSQLステートメントを作成する場合、ANALYZE
コマンドはクエリを最適化するための鍵であり、クエリをより高速かつ効率的にします。クエリプランとPostgreSQLの見積もりを表示することに加えて、EXPLAIN ANALYZE
オプションはクエリを実行します(UPDATE
に注意してください およびDELETE
!)、実行プロセスの各ステップの実際の実行時間と行数を示します。これは、SQLのパフォーマンスを監視するために必要です。
EXPLAIN ANALYZE
を使用できます 推定行数を各操作によって返される実際の行と比較します。
同じデータをもう一度使用して、例を見てみましょう:
QUERY PLAN | -----------------------------------------------------------------------------------------------------------+ Sort (cost=66.83..69.33 rows=1000 width=17) (actual time=20.569..20.684 rows=1000 loops=1) | Sort Key: username | Sort Method: quicksort Memory: 102kB | -> Seq Scan on users (cost=0.00..17.00 rows=1000 width=17) (actual time=0.048..0.596 rows=1000 loops=1)| Planning Time: 0.171 ms | Execution Time: 20.793 ms |
合計実行コストはまだ69.33であり、その大部分はソート操作であり、17.00はシーケンシャルスキャンによるものであることがわかります。クエリの実行時間は21ミリ秒未満であることに注意してください。
シーケンシャルスキャンとインデックススキャン
それでは、インデックスを追加して、テーブル全体のコストのかかる並べ替えを回避してみましょう。
CREATE INDEX people_username_idx ON users (username); EXPLAIN ANALYZE SELECT * FROM users ORDER BY username; QUERY PLAN | ---------------------------------------------------------------------------------------------------------------------------------+ Index Scan using people_username_idx on users (cost=0.28..28.27 rows=1000 width=17) (actual time=0.052..1.494 rows=1000 loops=1)| Planning Time: 0.186 ms | Execution Time: 1.686 ms |
ご覧のとおり、クエリプランナーはインデックススキャンを選択しました。これは、そのプランの合計コストが28.27(69.33より低い)であるためです。クエリの実行時間が2ミリ秒をわずかに下回るようになったため、インデックススキャンはシーケンシャルスキャンよりも効率的だったようです。
プランナーがより正確に見積もるのを支援する
プランナーがより正確に見積もるには、次の2つの方法があります。
- より良い統計を収集するのに役立ちます
- 計算に使用する定数を調整します
テーブル内のデータに大きな変更を加えた後は、統計が特に悪くなる可能性があります。そのため、テーブルに大量のデータをロードする場合は、手動のANALYZE
を実行することで、Postgresを支援できます。 その上に。統計はメジャーバージョンのアップグレードでも保持されないため、これを行うためのもう1つの重要な時期です。
当然、テーブルも時間の経過とともに変化するため、自動真空設定を調整して、ワークロードに対して十分な頻度で実行されるようにすることが非常に役立ちます。
分布が歪んでいる列の推定値が正しくない場合は、ALTER TABLE SET STATISTICS
を使用して、Postgresが収集する情報の量を増やすことでメリットが得られる可能性があります。 コマンド、またはdefault_statistics_target
データベース全体に対して。
誤った見積もりのもう1つの一般的な原因は、デフォルトで、Postgresが2つの列が独立していると想定することです。これを修正するには、拡張統計を介して同じテーブルから2つの列の相関データを収集するように依頼します。
コンスタントチューニングの面では、ハードウェアに合わせてチューニングできるパラメーターがたくさんあります。 SSDで実行していると仮定すると、少なくともrandom_page_cost
の設定を調整する必要があります。 。これはデフォルトで4に設定されており、seq_page_cost
の4倍の費用がかかります。 先ほど見ました。この比率は回転するディスクでは理にかなっていますが、SSDではランダムI/Oにペナルティを課しすぎる傾向があります。そのため、1に近い、または1〜2の設定の方が理にかなっている場合があります。 ScaleGridでは、デフォルトで1になります。
クエリプランからコストを削除できますか?
上記の理由の多くのために、ほとんどの人はEXPLAIN
を実行するときにコストを残します 。ただし、必要に応じて、COSTS
を使用してオフにすることができます パラメータ。
EXPLAIN (COSTS OFF) SELECT * FROM users LIMIT 1; QUERY PLAN | -----------------------+ Limit | -> Seq Scan on users|
結論
要約すると、クエリプランのコストは、任意の単位でのSQLクエリにかかる時間に関するPostgresの見積もりです。
構成可能な定数と収集した統計に基づいて、全体的なコストが最も低いプランを選択します。
これらのコストをより正確に見積もるのを支援することは、適切な選択を行い、クエリのパフォーマンスを維持するために非常に重要です。
|