У меня есть относительно простой запрос к таблице с 1,5M строк:
SELECT mtid FROM publication
WHERE mtid IN (9762715) OR last_modifier=21321
LIMIT 5000;
EXPLAIN ANALYZE
вывод:
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
Пока все хорошо, быстро и использует доступные индексы.
Теперь, если я немного изменю запрос, результат будет:
SELECT mtid FROM publication
WHERE mtid IN (SELECT 9762715) OR last_modifier=21321
LIMIT 5000;
EXPLAIN ANALYZE
Выход:
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
Не так быстро, и с помощью seq scan ...
Конечно, исходный запрос, выполняемый приложением, немного сложнее и даже медленнее, и, конечно, генерируемый в спящем режиме нет (SELECT 9762715)
, но медлительность есть даже для этого (SELECT 9762715)
! Запрос генерируется hibernate, поэтому изменить их довольно сложно, а некоторые функции недоступны (например, UNION
недоступны, что было бы быстро).
Вопросы
- Почему индекс не может быть использован во втором случае? Как они могли быть использованы?
- Могу ли я улучшить производительность запросов другим способом?
Дополнительные мысли
Кажется, что мы могли бы использовать первый случай, вручную выполнив SELECT, а затем поместив полученный список в запрос. Даже с 5000 числами в списке IN () это в четыре раза быстрее, чем второе решение. Однако, это только кажется НЕПРАВИЛЬНЫМ (также, это могло бы быть в 100 раз быстрее :)). Совершенно непонятно, почему планировщик запросов использует совершенно разные методы для этих двух запросов, поэтому я хотел бы найти более удачное решение этой проблемы.
(SELECT 9762715)
.
(SELECT 9762715)
. На вопрос гибернации: это можно сделать, но требует серьезного переписывания кода, поскольку у нас есть пользовательские критерии запроса гибернации, которые транслируются на лету. Поэтому, по сути, мы будем модифицировать Hibernate, что является огромным предприятием с множеством возможных побочных эффектов.
JOIN
вместоIN ()
? Кроме того,publication
был недавно проанализирован?