19

今日は、理解できないクエリ プランについて頭を悩ませるのに 1 時間以上費やしました。クエリは で、UPDATEまったく実行されませんでした。完全にデッドロック:pg_locks何も待っていないことを示しています。さて、私は自分自身が最高のクエリ プラン リーダーだとも最低のクエリ プラン リーダーだとも思っていませんが、これは非常に難しいと思います。私はこれらをどのように読むのだろうかと思っています。エラーを特定するために Pg エースが従う方法論はありますか?

この問題を回避する方法については別の質問をする予定ですが、今はこれらのタイプの計画の読み方について具体的に話しています。

                                         QUERY PLAN                                         
--------------------------------------------------------------------------------------------
 Nested Loop Anti Join  (cost=47680.88..169413.12 rows=1 width=77)
   Join Filter: ((co.fkey_style = v.chrome_styleid) AND (co.name = o.name))
   ->  Nested Loop  (cost=5301.58..31738.10 rows=1 width=81)
         ->  Hash Join  (cost=5301.58..29722.32 rows=229 width=40)
               Hash Cond: ((io.lot_id = iv.lot_id) AND ((io.vin)::text = (iv.vin)::text))
               ->  Seq Scan on options io  (cost=0.00..20223.32 rows=23004 width=36)
                     Filter: (name IS NULL)
               ->  Hash  (cost=4547.33..4547.33 rows=36150 width=24)
                     ->  Seq Scan on vehicles iv  (cost=0.00..4547.33 rows=36150 width=24)
                           Filter: (date_sold IS NULL)
         ->  Index Scan using options_pkey on options co  (cost=0.00..8.79 rows=1 width=49)
               Index Cond: ((co.fkey_style = iv.chrome_styleid) AND (co.code = io.code))
   ->  Hash Join  (cost=42379.30..137424.09 rows=16729 width=26)
         Hash Cond: ((v.lot_id = o.lot_id) AND ((v.vin)::text = (o.vin)::text))
         ->  Seq Scan on vehicles v  (cost=0.00..4547.33 rows=65233 width=24)
         ->  Hash  (cost=20223.32..20223.32 rows=931332 width=44)
               ->  Seq Scan on options o  (cost=0.00..20223.32 rows=931332 width=44)
(17 rows)

このクエリプランの問題-私は理解していると思います-おそらく次のことを最もよく言いますRhodiumToad(彼は間違いなくこれに優れているので、彼の説明が優れていることに賭けます)irc://irc.freenode.net/#postgresql

ああ、その計画は悲惨な可能性があります。その計画の問題は、各行に対して非常に高価なハッシュジョインを実行していることです 。問題は、他の結合からのrows = 1の推定であり、プランナーは、内部外側のパスが 1 つの行のみを返すと推定されるネストループのパス。明らかに、プランナーの見積もりでは、高価な部分は一度だけ実行されますが、これは実際には本当に台無しになる明らかな傾向があるため、問題は、プランナーが自分の見積もりを理想的に信じていることです。 1行を返す」および「1行以上を返すことはできません」が、それを既存のコードに組み込む方法がまったく明確ではありません

彼は続けてこう言います:

どの結合にも影響する可能性がありますが、通常はサブクエリに対する結合が最も可能性が高いです

この計画を読んだとき、最初に気づいたのは でNested Loop Anti Join、これには のコストがかかりました169,413(上限に固執します)。Nested Loopこの Anti-Join は、 at cost of31,738の結果とHash Joinat a cost ofの結果に分解され137,424ます。現在、137,424はよりもはるかに大きい31,738ので、問題はハッシュ結合であることがわかりました。

EXPLAIN ANALYZE次に、クエリの外にある Hash Join セグメントに進みます。7秒で実行されました。(lot_id、vin)、および (co.code、および v.code) にインデックスがあることを確認しました -- ありました。個別に無効seq_scanhashjoinすると、2 秒未満の速度の増加に気付きました。1 時間後に進行しなかった理由を説明するのに十分ではありません。

しかし、結局、私は完全に間違っています!はい、それはクエリの遅い部分でしたが、rows="1"ビットのためです(私はそれがにあったと思いますNested Loop Anti Join)。これは、行の量を誤って見積もるプランナーのバグ (能力の欠如) ですか? 同じ結論に達するために、これをどのように読むべきRhodiumToadですか?

それは単にrows="1"私がこれを理解するきっかけになるはずですか?

関連するすべてのテーブルで実行VACUUM FULL ANALYZEしましたが、これは Postgresql 8.4 です。

4

2 に答える 2

23

このような問題を見抜くには、どこで問題が発生する可能性があるかについての経験が必要です。ただし、クエリ プランの問題を見つけるには、生成されたプランを徹底的に検証し、行数の見積もりが妥当で、コストの見積もりが費やされた時間と一致するかどうかを確認してください。ところで。2 つのコスト見積もりは下限でも上限でもありません。1 つ目は出力の最初の行を生成するための推定コスト、2 つ目の数値は推定総コストです。詳細についてはドキュメントの説明を参照してください。利用可能なプランナー ドキュメントもあります。また、さまざまなアクセス方法がどのように機能するかを知ることも役立ちます。出発点として、ウィキペディアには、ネストされたループハッシュ、およびマージ結合に関する情報があります。

あなたの例では、次から始めます。

           ->  Seq Scan on options io  (cost=0.00..20223.32 rows=23004 width=36)
                 Filter: (name IS NULL)

実行EXPLAIN ANALYZE SELECT * FROM options WHERE name IS NULL;して、返された行が見積もりと一致するかどうかを確認します。通常、係数 2 は問題ではありません。桁違いの違いを見つけようとしています。

次にEXPLAIN ANALYZE SELECT * FROM vehicles WHERE date_sold IS NULL;、予想される行数を返すを参照してください。

次に、ハッシュ結合まで 1 レベル上がります。

     ->  Hash Join  (cost=5301.58..29722.32 rows=229 width=40)
           Hash Cond: ((io.lot_id = iv.lot_id) AND ((io.vin)::text = (iv.vin)::text))

EXPLAIN ANALYZE SELECT * FROM vehicles AS iv INNER JOIN options io ON (io.lot_id = iv.lot_id) AND ((io.vin)::text = (iv.vin)::text) WHERE iv.date_sold IS NULL AND io.name IS NULL;結果が 229 行になるかどうかを確認します。

もう 1 レベル上に追加するINNER JOIN options co ON (co.fkey_style = iv.chrome_styleid) AND (co.code = io.code)と、1 行だけが返されることが期待されます。実際の行数が 1 から 100 になると、ネストされたループを含む内側のループをトラバースする総コストの見積もりが 100 倍になるため、おそらくここで問題が発生します。

プランナーが犯している根本的な間違いは、結合するための 2 つの述部coが互いに独立していて、それらの選択性を倍加していると想定している可能性があります。実際には、それらは強く相関している可能性があり、選択性は s1*s2 ではなく MIN(s1, s2) に近くなります。

于 2010-02-26T12:19:07.367 に答える
2

テーブルを分析しましたか?そして、pg_stats はこれらのテーブルについて何を伝えなければならないのでしょうか? クエリプランは統計に基づいています。これらは問題ないはずです。そして、どのバージョンを使用していますか?8.4?

コストは、統計、relpage の量、行の量、および Planner コスト定数の postgresql.conf の設定を使用して計算できます。

work_mem も関与しています。低すぎる可能性があり、プランナーが seqscan を実行してパフォーマンスを低下させる可能性があります...

于 2010-02-25T21:23:31.473 に答える