Eu tenho uma consulta relativamente simples em uma tabela com 1,5 milhões de linhas:
SELECT mtid FROM publication
WHERE mtid IN (9762715) OR last_modifier=21321
LIMIT 5000;
EXPLAIN ANALYZE
resultado:
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
Até aí tudo bem, rápido e usa os índices disponíveis.
Agora, se eu modificar um pouco uma consulta, o resultado será:
SELECT mtid FROM publication
WHERE mtid IN (SELECT 9762715) OR last_modifier=21321
LIMIT 5000;
A EXPLAIN ANALYZE
saída é:
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
Não tão rápido, e usando o seq scan...
Claro que a consulta original executada pelo aplicativo é um pouco mais complexa, e até mais lenta, e claro que a original gerada pelo hibernate não é (SELECT 9762715)
, mas a lentidão existe até por isso (SELECT 9762715)
! A consulta é gerada pelo hibernate, por isso é um grande desafio alterá-los, e alguns recursos não estão disponíveis (por exemplo UNION
, não estão disponíveis, o que seria rápido).
As questões
- Por que o índice não pode ser usado no segundo caso? Como eles poderiam ser usados?
- Posso melhorar o desempenho da consulta de outra maneira?
Pensamentos adicionais
Parece que poderíamos usar o primeiro caso fazendo manualmente um SELECT e, em seguida, colocando a lista resultante na consulta. Mesmo com 5.000 números na lista IN(), é quatro vezes mais rápido que a segunda solução. No entanto, parece ERRADO (além disso, poderia ser 100 vezes mais rápido :)). É completamente incompreensível porque o planejador de consulta usa um método completamente diferente para essas duas consultas, então gostaria de encontrar uma solução melhor para esse problema.