小規模な Postgres 10 データ ウェアハウスについて、分析クエリの改善をチェックしていたところ、改善の可能性が基本的にこのサブクエリに帰着するかなり遅いクエリを発見しました (従来のグループあたり最大 n 問題)。
SELECT s_postings.*
FROM dwh.s_postings
JOIN (SELECT s_postings.id,
max(s_postings.load_dts) AS load_dts
FROM dwh.s_postings
GROUP BY s_postings.id) AS current_postings
ON s_postings.id = current_postings.id AND s_postings.load_dts = current_postings.load_dts
次の実行計画を使用します。
"Gather (cost=23808.51..38602.59 rows=66 width=376) (actual time=1385.927..1810.844 rows=170847 loops=1)"
" Workers Planned: 2"
" Workers Launched: 2"
" -> Hash Join (cost=22808.51..37595.99 rows=28 width=376) (actual time=1199.647..1490.652 rows=56949 loops=3)"
" Hash Cond: (((s_postings.id)::text = (s_postings_1.id)::text) AND (s_postings.load_dts = (max(s_postings_1.load_dts))))"
" -> Parallel Seq Scan on s_postings (cost=0.00..14113.25 rows=128425 width=376) (actual time=0.016..73.604 rows=102723 loops=3)"
" -> Hash (cost=20513.00..20513.00 rows=153034 width=75) (actual time=1195.616..1195.616 rows=170847 loops=3)"
" Buckets: 262144 Batches: 1 Memory Usage: 20735kB"
" -> HashAggregate (cost=17452.32..18982.66 rows=153034 width=75) (actual time=836.694..1015.499 rows=170847 loops=3)"
" Group Key: s_postings_1.id"
" -> Seq Scan on s_postings s_postings_1 (cost=0.00..15911.21 rows=308221 width=75) (actual time=0.032..251.122 rows=308168 loops=3)"
"Planning time: 1.184 ms"
"Execution time: 1912.865 ms"
行の見積もりは絶対に間違っています! 私にとって奇妙なことは、結合を今すぐ右結合に変更した場合です。
SELECT s_postings.*
FROM dwh.s_postings
RIGHT JOIN (SELECT s_postings.id,
max(s_postings.load_dts) AS load_dts
FROM dwh.s_postings
GROUP BY s_postings.id) AS current_postings
ON s_postings.id = current_postings.id AND s_postings.load_dts = current_postings.load_dts
実行計画では:
"Hash Right Join (cost=22829.85..40375.62 rows=153177 width=376) (actual time=814.097..1399.673 rows=170848 loops=1)"
" Hash Cond: (((s_postings.id)::text = (s_postings_1.id)::text) AND (s_postings.load_dts = (max(s_postings_1.load_dts))))"
" -> Seq Scan on s_postings (cost=0.00..15926.10 rows=308510 width=376) (actual time=0.011..144.584 rows=308419 loops=1)"
" -> Hash (cost=20532.19..20532.19 rows=153177 width=75) (actual time=812.587..812.587 rows=170848 loops=1)"
" Buckets: 262144 Batches: 1 Memory Usage: 20735kB"
" -> HashAggregate (cost=17468.65..19000.42 rows=153177 width=75) (actual time=553.633..683.850 rows=170848 loops=1)"
" Group Key: s_postings_1.id"
" -> Seq Scan on s_postings s_postings_1 (cost=0.00..15926.10 rows=308510 width=75) (actual time=0.011..157.000 rows=308419 loops=1)"
"Planning time: 0.402 ms"
"Execution time: 1469.808 ms"
行の見積もりははるかに優れています!
たとえば、並列シーケンシャル スキャンは、状況によってはパフォーマンスを低下させる可能性がありますが、行の見積もりを変更するべきではないことを認識しています!? 私が正しく覚えていれば、集計関数もインデックスの適切な使用をとにかくブロックし、追加の多変量統計 (例: tuple ) を使用しても潜在的な利益は見られませんid, load_dts
。データベースはVACUUM ANALYZE
d.
私にとって、クエリは論理的に同じです。
クエリ プランナーをサポートして、見積もりに関するより良い仮定を作成したり、クエリを改善したりする方法はありますか? たぶん、誰かがこの違いが存在する理由を知っていますか?
編集:以前は、参加条件ON s_postings.id::text = current_postings.id::text
を変更して、ON s_postings.id = current_postings.id
誰も混乱させないようにしました。この変換を削除しても、クエリ プランは変更されません。
Edit2: 以下に示すように、greatest-n-per-group
問題には別の解決策があります。
SELECT p.*
FROM (SELECT p.*,
RANK() OVER (PARTITION BY p.id ORDER BY p.load_dts DESC) as seqnum
FROM dwh.s_postings p
) p
WHERE seqnum = 1;
非常に優れたソリューションですが、悲しいことに、クエリ プランナーも行数を過小評価しています。
"Subquery Scan on p (cost=44151.67..54199.31 rows=1546 width=384) (actual time=1742.902..2594.359 rows=171269 loops=1)"
" Filter: (p.seqnum = 1)"
" Rows Removed by Filter: 137803"
" -> WindowAgg (cost=44151.67..50334.83 rows=309158 width=384) (actual time=1742.899..2408.240 rows=309072 loops=1)"
" -> Sort (cost=44151.67..44924.57 rows=309158 width=376) (actual time=1742.887..1927.325 rows=309072 loops=1)"
" Sort Key: p_1.id, p_1.load_dts DESC"
" Sort Method: quicksort Memory: 172275kB"
" -> Seq Scan on s_postings p_1 (cost=0.00..15959.58 rows=309158 width=376) (actual time=0.007..221.240 rows=309072 loops=1)"
"Planning time: 0.149 ms"
"Execution time: 2666.645 ms"