Mam stosunkowo proste zapytanie dotyczące tabeli z 1,5 mln wierszy:
SELECT mtid FROM publication
WHERE mtid IN (9762715) OR last_modifier=21321
LIMIT 5000;
EXPLAIN ANALYZE
wynik:
Limit (cost=8.84..12.86 rows=1 width=8) (actual time=0.985..0.986 rows=1 loops=1) -> Bitmap Heap Scan on publication (cost=8.84..12.86 rows=1 width=8) (actual time=0.984..0.985 rows=1 loops=1) Recheck Cond: ((mtid = 9762715) OR (last_modifier = 21321)) -> BitmapOr (cost=8.84..8.84 rows=1 width=0) (actual time=0.971..0.971 rows=0 loops=1) -> Bitmap Index Scan on publication_pkey (cost=0.00..4.42 rows=1 width=0) (actual time=0.295..0.295 rows=1 loops=1) Index Cond: (mtid = 9762715) -> Bitmap Index Scan on publication_last_modifier_btree (cost=0.00..4.42 rows=1 width=0) (actual time=0.674..0.674 rows=0 loops=1) Index Cond: (last_modifier = 21321) Total runtime: 1.027 ms
Jak dotąd dobry, szybki i wykorzystuje dostępne indeksy.
Teraz, jeśli nieco zmodyfikuję zapytanie, wynikiem będzie:
SELECT mtid FROM publication
WHERE mtid IN (SELECT 9762715) OR last_modifier=21321
LIMIT 5000;
Dane EXPLAIN ANALYZE
wyjściowe to:
Limit (cost=0.01..2347.74 rows=5000 width=8) (actual time=2735.891..2841.398 rows=1 loops=1) -> Seq Scan on publication (cost=0.01..349652.84 rows=744661 width=8) (actual time=2735.888..2841.393 rows=1 loops=1) Filter: ((hashed SubPlan 1) OR (last_modifier = 21321)) SubPlan 1 -> Result (cost=0.00..0.01 rows=1 width=0) (actual time=0.001..0.001 rows=1 loops=1) Total runtime: 2841.442 ms
Nie tak szybko i przy użyciu skanowania sekwencyjnego ...
Oczywiście oryginalne zapytanie uruchamiane przez aplikację jest nieco bardziej złożone, a nawet wolniejsze, i oczywiście nie jest generowany hibernacja (SELECT 9762715)
, ale powolność jest nawet do tego (SELECT 9762715)
! Zapytanie jest generowane przez hibernację, więc ich zmiana jest dość trudna, a niektóre funkcje nie są dostępne (np. UNION
Nie są dostępne, co byłoby szybkie).
Pytania
- Dlaczego nie można użyć indeksu w drugim przypadku? Jak można je wykorzystać?
- Czy mogę poprawić wydajność zapytań w inny sposób?
Dodatkowe przemyślenia
Wydaje się, że moglibyśmy użyć pierwszego przypadku, ręcznie wykonując WYBÓR, a następnie umieszczając wynikową listę w zapytaniu. Nawet przy 5000 liczb na liście IN () jest czterokrotnie szybszy niż drugie rozwiązanie. Wydaje się to jednak NIEPRAWIDŁOWE (może być 100 razy szybsze :)). Jest całkowicie niezrozumiałe, dlaczego planista zapytań stosuje zupełnie inną metodę dla tych dwóch zapytań, dlatego chciałbym znaleźć lepsze rozwiązanie tego problemu.
(SELECT 9762715)
.
(SELECT 9762715)
. Na pytanie o hibernację: można to zrobić, ale wymaga poważnego przepisania kodu, ponieważ mamy zdefiniowane przez użytkownika zapytania o hibernację, które są tłumaczone w locie. Zasadniczo zmodyfikowalibyśmy hibernację, która jest ogromnym przedsięwzięciem z wieloma możliwymi skutkami ubocznymi.
JOIN
zamiast niegoIN ()
? Ponadtopublication
został niedawno przeanalizowany?