アプリケーションとそのデータベースを従来のPostgreSQLデータベースからAmazon Aurora RDS PostgreSQLデータベース(どちらもバージョン9.6を使用)に移行した後、特定のクエリの実行がAuroraの場合よりもはるかに遅く(約10倍遅く)なっていることがわかりましたPostgreSQL。
ハードウェアとpg_confのどちらのデータベースでも、両方のデータベースは同じ構成です。
クエリ自体はかなり単純です。 Javaで記述されたバックエンドから生成され、クエリの記述にjOOQを使用します。
_with "all_acp_ids"("acp_id") as (
select acp_id from temp_table_de3398bacb6c4e8ca8b37be227eac089
)
select distinct "public"."f1_folio_milestones"."acp_id",
coalesce("public"."sa_milestone_overrides"."team",
"public"."f1_folio_milestones"."team_responsible")
from "public"."f1_folio_milestones"
left outer join
"public"."sa_milestone_overrides" on (
"public"."f1_folio_milestones"."milestone" = "public"."sa_milestone_overrides"."milestone"
and "public"."f1_folio_milestones"."view" = "public"."sa_milestone_overrides"."view"
and "public"."f1_folio_milestones"."acp_id" = "public"."sa_milestone_overrides"."acp_id"
)
where "public"."f1_folio_milestones"."acp_id" in (
select "all_acp_ids"."acp_id" from "all_acp_ids"
)
_
_temp_table_de3398bacb6c4e8ca8b37be227eac089
_が単一列のテーブルである場合、_f1_folio_milestones
_(1700万エントリ)および_sa_milestone_overrides
_(約100万エントリ)は、_LEFT OUTER JOIN
_。
_temp_table_de3398bacb6c4e8ca8b37be227eac089
_には、最大5000のエントリを含めることができます。
通常のPostgreSQLデータベースで実行すると、次のクエリプランが生成されます。
_Unique (cost=4802622.20..4868822.51 rows=8826708 width=43) (actual time=483.928..483.930 rows=1 loops=1)
CTE all_acp_ids
-> Seq Scan on temp_table_de3398bacb6c4e8ca8b37be227eac089 (cost=0.00..23.60 rows=1360 width=32) (actual time=0.004..0.005 rows=1 loops=1)
-> Sort (cost=4802598.60..4824665.37 rows=8826708 width=43) (actual time=483.927..483.927 rows=4 loops=1)
Sort Key: f1_folio_milestones.acp_id, (COALESCE(sa_milestone_overrides.team, f1_folio_milestones.team_responsible))
Sort Method: quicksort Memory: 25kB
-> Hash Left Join (cost=46051.06..3590338.34 rows=8826708 width=43) (actual time=483.905..483.917 rows=4 loops=1)
Hash Cond: ((f1_folio_milestones.milestone = sa_milestone_overrides.milestone) AND (f1_folio_milestones.view = (sa_milestone_overrides.view)::text) AND (f1_folio_milestones.acp_id = (sa_milestone_overrides.acp_id)::text))
-> Nested Loop (cost=31.16..2572.60 rows=8826708 width=37) (actual time=0.029..0.038 rows=4 loops=1)
-> HashAggregate (cost=30.60..32.60 rows=200 width=32) (actual time=0.009..0.010 rows=1 loops=1)
Group Key: all_acp_ids.acp_id
-> CTE Scan on all_acp_ids (cost=0.00..27.20 rows=1360 width=32) (actual time=0.006..0.007 rows=1 loops=1)
-> Index Scan using f1_folio_milestones_acp_id_idx on f1_folio_milestones (cost=0.56..12.65 rows=5 width=37) (actual time=0.018..0.025 rows=4 loops=1)
Index Cond: (acp_id = all_acp_ids.acp_id)
-> Hash (cost=28726.78..28726.78 rows=988178 width=34) (actual time=480.423..480.423 rows=987355 loops=1)
Buckets: 1048576 Batches: 1 Memory Usage: 72580kB
-> Seq Scan on sa_milestone_overrides (cost=0.00..28726.78 rows=988178 width=34) (actual time=0.004..189.641 rows=987355 loops=1)
Planning time: 3.561 ms
Execution time: 489.223 ms
_
ご覧のとおり、クエリは1秒未満でかなりスムーズに進んでいます。しかし、Auroraインスタンスでは、これが発生します。
_Unique (cost=2632927.29..2699194.83 rows=8835672 width=43) (actual time=4577.348..4577.350 rows=1 loops=1)
CTE all_acp_ids
-> Seq Scan on temp_table_de3398bacb6c4e8ca8b37be227eac089 (cost=0.00..23.60 rows=1360 width=32) (actual time=0.001..0.001 rows=1 loops=1)
-> Sort (cost=2632903.69..2654992.87 rows=8835672 width=43) (actual time=4577.348..4577.348 rows=4 loops=1)
Sort Key: f1_folio_milestones.acp_id, (COALESCE(sa_milestone_overrides.team, f1_folio_milestones.team_responsible))
Sort Method: quicksort Memory: 25kB
-> Merge Left Join (cost=1321097.58..1419347.08 rows=8835672 width=43) (actual time=4488.369..4577.330 rows=4 loops=1)
Merge Cond: ((f1_folio_milestones.view = (sa_milestone_overrides.view)::text) AND (f1_folio_milestones.milestone = sa_milestone_overrides.milestone) AND (f1_folio_milestones.acp_id = (sa_milestone_overrides.acp_id)::text))
-> Sort (cost=1194151.06..1216240.24 rows=8835672 width=37) (actual time=0.039..0.040 rows=4 loops=1)
Sort Key: f1_folio_milestones.view, f1_folio_milestones.milestone, f1_folio_milestones.acp_id
Sort Method: quicksort Memory: 25kB
-> Nested Loop (cost=31.16..2166.95 rows=8835672 width=37) (actual time=0.022..0.028 rows=4 loops=1)
-> HashAggregate (cost=30.60..32.60 rows=200 width=32) (actual time=0.006..0.006 rows=1 loops=1)
Group Key: all_acp_ids.acp_id
-> CTE Scan on all_acp_ids (cost=0.00..27.20 rows=1360 width=32) (actual time=0.003..0.004 rows=1 loops=1)
-> Index Scan using f1_folio_milestones_acp_id_idx on f1_folio_milestones (cost=0.56..10.63 rows=4 width=37) (actual time=0.011..0.015 rows=4 loops=1)
Index Cond: (acp_id = all_acp_ids.acp_id)
-> Sort (cost=126946.52..129413.75 rows=986892 width=34) (actual time=4462.727..4526.822 rows=448136 loops=1)
Sort Key: sa_milestone_overrides.view, sa_milestone_overrides.milestone, sa_milestone_overrides.acp_id
Sort Method: quicksort Memory: 106092kB
-> Seq Scan on sa_milestone_overrides (cost=0.00..28688.92 rows=986892 width=34) (actual time=0.003..164.348 rows=986867 loops=1)
Planning time: 1.394 ms
Execution time: 4583.295 ms
_
グローバルコストは効果的に低くなりますが、以前よりも約10倍の時間がかかります。
マージ結合を無効にすると、Auroraはハッシュ結合に戻ります。これにより、実行時間が予測されますが、永続的に無効にすることはできません。不思議なことに、ネストされたループを無効にすると、マージ結合を使用しながらさらに良い結果が得られます...
_Unique (cost=3610230.74..3676431.05 rows=8826708 width=43) (actual time=2.465..2.466 rows=1 loops=1)
CTE all_acp_ids
-> Seq Scan on temp_table_de3398bacb6c4e8ca8b37be227eac089 (cost=0.00..23.60 rows=1360 width=32) (actual time=0.004..0.004 rows=1 loops=1)
-> Sort (cost=3610207.14..3632273.91 rows=8826708 width=43) (actual time=2.464..2.464 rows=4 loops=1)
Sort Key: f1_folio_milestones.acp_id, (COALESCE(sa_milestone_overrides.team, f1_folio_milestones.team_responsible))
Sort Method: quicksort Memory: 25kB
-> Merge Left Join (cost=59.48..2397946.87 rows=8826708 width=43) (actual time=2.450..2.455 rows=4 loops=1)
Merge Cond: (f1_folio_milestones.acp_id = (sa_milestone_overrides.acp_id)::text)
Join Filter: ((f1_folio_milestones.milestone = sa_milestone_overrides.milestone) AND (f1_folio_milestones.view = (sa_milestone_overrides.view)::text))
-> Merge Join (cost=40.81..2267461.88 rows=8826708 width=37) (actual time=2.312..2.317 rows=4 loops=1)
Merge Cond: (f1_folio_milestones.acp_id = all_acp_ids.acp_id)
-> Index Scan using f1_folio_milestones_acp_id_idx on f1_folio_milestones (cost=0.56..2223273.29 rows=17653416 width=37) (actual time=0.020..2.020 rows=1952 loops=1)
-> Sort (cost=40.24..40.74 rows=200 width=32) (actual time=0.011..0.012 rows=1 loops=1)
Sort Key: all_acp_ids.acp_id
Sort Method: quicksort Memory: 25kB
-> HashAggregate (cost=30.60..32.60 rows=200 width=32) (actual time=0.008..0.008 rows=1 loops=1)
Group Key: all_acp_ids.acp_id
-> CTE Scan on all_acp_ids (cost=0.00..27.20 rows=1360 width=32) (actual time=0.005..0.005 rows=1 loops=1)
-> Materialize (cost=0.42..62167.38 rows=987968 width=34) (actual time=0.021..0.101 rows=199 loops=1)
-> Index Scan using sa_milestone_overrides_acp_id_index on sa_milestone_overrides (cost=0.42..59697.46 rows=987968 width=34) (actual time=0.019..0.078 rows=199 loops=1)
Planning time: 5.500 ms
Execution time: 2.516 ms
_
AWSサポートチームに問い合わせましたが、彼らはまだ問題を調査していますが、その問題が発生する原因は何なのかと考えています。そのような行動の違いを説明できるものは何ですか?
データベースのドキュメントのいくつかを調べている間、Auroraは時間とともにコストを優先することを読みました。したがって、コストが最も低いクエリプランを使用しています。
しかし、ご覧のように、その応答時間を考えると、最適とはほど遠い...データベースのクエリプランをより高価(ただし高速)に使用できるしきい値または設定はありますか?
1つの問題が(すべてのクエリプランで)目立ち、簡単に修正できます。
Temp_table_de3398bacb6c4e8ca8b37be227eac089のシーケンススキャン(コスト= 0.00..23.60 rows = 136幅= 32)(実際の時間= 0.004..0.005 rows = 1ループ= 1)
大胆な強調鉱山。 Postgresはそのテーブルの1360行を期待していますが、1のみを見つけますと言います。
あなたはコメントしました:
それはすべてが行われた後に削除される通常のテーブルです。 [...]クエリプランはテーブル内の単一の値を使用して実行されましたが、合計で最大5000のエントリを持つことができ、すべてが個別です。
..完全に誤解を招くと予想される行数を説明できます。統計は autovacuum によって最新に保たれます。ただし、開始するまでに少し時間がかかります。そのようなテーブルにデータを入力した(または大幅に変更した)直後に複雑なクエリを実行する場合、少なくともANALYZE
を手動で実行するのが賢明です。
ANALYZE temp_table_de3398bacb6c4e8ca8b37be227eac089;
多分VACUUM ANALYZE
、しかしそれはトランザクション内では実行できません。
実際の 一時テーブル(CREATE TEMP TABLE ...
) は、ユースケースに適しているように思われます。 (他のセッションではテーブルの同じ状態を表示する必要はありません。)全体的にパフォーマンスが向上します。しかし、それらはautovacuumによる分析からまったく免除されていることに言及する価値があります。見る:
それが失敗した場合、Postgresはコアテーブルの完全に誤解を招く行推定に基づいて、あらゆる種類の不適切なクエリプランを試行します。 Postgresが異なるクエリプラン(コストの見積もりを変更するもの)を選択する理由は数多くあります。しかしthatを修正すれば、当面の問題は解消されるでしょう。
Postgresクエリプランの「コスト」は、推定時間(任意の単位)です。
"実際の時間"は、事後に測定された時間です。
Postgresalwaysは低コストを優先します。それが、どの計画を選ぶかを決定する方法です。オーロラとは一切関係ありません。主な問題は、誤解を招く統計->誤解を招く行の見積もり->誤解を招くコストの見積もりです。マニュアルの詳細。開始 ここ および ここ
とはいえ、クエリを簡略化します。 CTEは、具体化されているかどうかにかかわらず、クエリに役立つものは何も追加しません。それを除く。
SELECT DISTINCT m.acp_id,
COALESCE(o.team, m.team_responsible)
FROM temp_table_de3398bacb6c4e8ca8b37be227eac089 t
JOIN public.f1_folio_milestones m USING (acp_id)
LEFT JOIN public.sa_milestone_overrides o USING (milestone, view, acp_id);
USING
は、タイピングを節約するのに便利です。パフォーマンスへの影響はありません。該当する場合にのみ使用してください。
あなたのカーディナリティ(t
は最大5000行、m
は1,700万行)を考えると、この代替クエリをラテラルサブクエリで試してみます。
SELECT t.acp_id, om.team
FROM temp_table_de3398bacb6c4e8ca8b37be227eac089 t
CROSS JOIN LATERAL (
SELECT COALESCE(o.team, m.team_responsible) AS team
FROM public.f1_folio_milestones m
LEFT JOIN public.sa_milestone_overrides o USING (milestone, view, acp_id)
WHERE m.acp_id = t.acp_id
) om;
有利なデータ分布およびフィッティングインデックスを使用すると、はるかに高速になる可能性があります。さらに読む(最適化したい場合に推奨!):