postgresql に奇妙な問題があります。プランナーは、外部キー インデックスを介したデータへのアクセスが非常に遅いと判断し、シーケンシャル スキャンを使用します。
単純化されたテーブル構造があります。
create table flight
(
id bigint not null constraint flight_pkey primary key,
parent_id bigint not null constraint flight_parent_fkey references flight
);
create table passenger
(
id bigint not null constraint passenger_pkey primary key,
flight_id bigint not null constraint pax_flight_fkey references flight
);
create index pax_flight_id on passenger (flight_id);
テーブルpassenger
には約 1,400 万行、テーブル フライトには約 6 万行あります。
問題は、多くのオプション条件を含む QueryDSL クエリがあり、そのうちの 1 つが乗客をフライトでフィルタリングすることです。
QPassenger qPassenger = QPassenger.passenger;
Long flightId = 123456;
...
BooleanBuilder predicate = new BooleanBuilder();
predicate.and(qPassenger.flight.id.eq(flightId));
...
そして、これらのオプションの条件に適合するすべての乗客を取得しようとすると、以下のようなクエリが生成され、30 秒間実行されます。それは恐ろしいです。passenger
どういうわけか、すべてのテーブルに対して順次スキャンとハッシュ結合を使用します。
select
passenger0_.id as id1_13_,
passenger0_.flight_id as flight_14_13_
from
passenger passenger0_ cross join flights flight1_
where
passenger0_.flight_id = 123456
or flight1_.parent_id = 123456
ただし、解決策を探して 1 日を過ごした後、flight
テーブルの主キーを使用すると、postgres が主キー インデックスを使用することがわかりました。
select
passenger0_.id as id1_13_,
passenger0_.flight_id as flight_14_13_
from
passenger passenger0_ cross join flights flight1_
where
flight1_.id = 123456 -- ←this line!
or flight1_.parent_id = 123456
残念ながら、手動で受け取った行をフィルター処理することはできません。これは、 が設定されている場合、1 フライトあたり約 300 人の乗客に対して、約 1,300 万行になるためflightId
です。
➥ それで、私の質問は次のとおりです: この条件に特定の列を使用するように QueryDSL/Hibernate に指示する方法はありますか? つまりflight.id
、そうではありませんpassenger.flight_id
。
または、別の質問: PostgreSQL プランナーの何が問題なのですか?どうすれば修正できますか?
UPDプランナーの計画:
- WHERE 条件で主キーを使用する適切なクエリ:
EXPLAIN ANALYZE
SELECT *
FROM passenger p JOIN flights f ON p.flight_id = f.id
WHERE (f.id = 123456
OR f.parent_id = 123456);
QUERY PLAN
Nested Loop (cost=3.66..1759.26 rows=310 width=951) (actual time=0.044..0.242 rows=184 loops=1)
-> Bitmap Heap Scan on flights f (cost=3.10..9.78 rows=6 width=240) (actual time=0.024..0.029 rows=4 loops=1)
Recheck Cond: ((id = 123456) OR (parent_id = 123456))
Heap Blocks: exact=4
-> BitmapOr (cost=3.10..3.10 rows=6 width=0) (actual time=0.018..0.018 rows=0 loops=1)
-> Bitmap Index Scan on flights_pkey (cost=0.00..1.53 rows=1 width=0) (actual time=0.008..0.008 rows=1 loops=1)
Index Cond: (id = 123456)
-> Bitmap Index Scan on flt_parent_id_index (cost=0.00..1.56 rows=5 width=0) (actual time=0.009..0.009 rows=3 loops=1)
Index Cond: (parent_id = 123456)
-> Index Scan using passenger_flight_id on passenger p (cost=0.56..286.73 rows=485 width=711) (actual time=0.005..0.023 rows=46 loops=4)
Index Cond: (flight_id = f.id)
Planning Time: 0.566 ms
Execution Time: 0.321 ms
- WHERE 条件で外部キーを使用する不適切なクエリ:
EXPLAIN ANALYZE
SELECT *
FROM passenger p JOIN flights f ON p.flight_id = f.id
WHERE (p.flight_id = 123456
OR f.parent_id = 123456);
QUERY PLAN
Gather (cost=34194.96..3461993.92 rows=734 width=951) (actual time=79878.815..80711.129 rows=184 loops=1)
Workers Planned: 2
Workers Launched: 2
-> Parallel Hash Join (cost=33194.96..3460920.52 rows=306 width=951) (actual time=71883.434..72044.345 rows=61 loops=3)
Hash Cond: (p.flight_id = f.id)
Join Filter: ((p.flight_id = 123456) OR (f.parent_id = 123456))
Rows Removed by Join Filter: 11206038
-> Parallel Seq Scan on passenger p (cost=0.00..827052.82 rows=14216282 width=711) (actual time=20.021..27298.757 rows=11206100 loops=3)
-> Parallel Hash (cost=20891.65..20891.65 rows=275065 width=240) (actual time=1284.916..1284.917 rows=219796 loops=3)
Buckets: 8192 Batches: 128 Memory Usage: 1248kB
-> Parallel Seq Scan on flights f (cost=0.00..20891.65 rows=275065 width=240) (actual time=2.134..966.560 rows=219796 loops=3)
Planning Time: 0.605 ms
Execution Time: 80711.774 ms