Eu tenho uma consulta relativamente simples em uma tabela com linhas de 1,5 milhão:
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 uma consulta um pouco, 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 ...
Obviamente, a consulta original executada pelo aplicativo é um pouco mais complexa e ainda mais lenta, e é claro que o original gerado por hibernação não é (SELECT 9762715)
, mas a lentidão existe mesmo para 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 executando manualmente um SELECT e colocando a lista resultante na consulta. Mesmo com 5000 números na lista IN (), é quatro vezes mais rápido que a segunda solução. No entanto, parece errado (também, pode ser 100 vezes mais rápido :)). É completamente incompreensível o motivo pelo qual o planejador de consultas usa um método completamente diferente para essas duas consultas. Por isso, gostaria de encontrar uma solução melhor para esse problema.
(SELECT 9762715)
.
(SELECT 9762715)
. Para a pergunta de hibernação: isso pode ser feito, mas requer uma reescrita séria de código, pois temos consultas de critérios de hibernação definidas pelo usuário que são traduzidas on-the-fly. Então, basicamente estaríamos modificando o hibernate, o que é uma tarefa enorme, com muitos efeitos colaterais possíveis.
JOIN
vez deIN ()
? Além disso,publication
foi analisado recentemente?