ORDER BY lento com LIMIT


11

Eu tenho esta consulta:

SELECT * 
FROM location 
WHERE to_tsvector('simple',unaccent2("city"))
   @@ to_tsquery('simple',unaccent2('wroclaw')) 
order by displaycount

Estou feliz com isso:

"Sort  (cost=3842.56..3847.12 rows=1826 width=123) (actual time=1.915..2.084 rows=1307 loops=1)"
"  Sort Key: displaycount"
"  Sort Method: quicksort  Memory: 206kB"
"  ->  Bitmap Heap Scan on location  (cost=34.40..3743.64 rows=1826 width=123) (actual time=0.788..1.208 rows=1307 loops=1)"
"        Recheck Cond: (to_tsvector('simple'::regconfig, unaccent2((city)::text)) @@ '''wroclaw'''::tsquery)"
"        ->  Bitmap Index Scan on location_lower_idx  (cost=0.00..33.95 rows=1826 width=0) (actual time=0.760..0.760 rows=1307 loops=1)"
"              Index Cond: (to_tsvector('simple'::regconfig, unaccent2((city)::text)) @@ '''wroclaw'''::tsquery)"
"Total runtime: 2.412 ms"

Mas quando adiciono LIMIT, a execução leva mais de 2 segundos:

SELECT * 
FROM location 
WHERE to_tsvector('simple',unaccent2("city"))
   @@ to_tsquery('simple',unaccent2('wroclaw')) 
order by displaycount 
limit 20

Explicar:

"Limit  (cost=0.00..1167.59 rows=20 width=123) (actual time=2775.452..2775.643 rows=20 loops=1)"
"  ->  Index Scan using location_displaycount_index on location  (cost=0.00..106601.25 rows=1826 width=123) (actual time=2775.448..2775.637 rows=20 loops=1)"
"        Filter: (to_tsvector('simple'::regconfig, unaccent2((city)::text)) @@ '''wroclaw'''::tsquery)"
"Total runtime: 2775.693 ms"

Eu acho que é algum problema com ORDER BY e LIMIT. Como forçar o PostgreSQL a usar o índice e fazer os pedidos no final?

Subconsulta não ajuda:

SELECT * 
FROM (
    SELECT * 
    FROM location 
    WHERE to_tsvector('simple',unaccent2("city"))
       @@ to_tsquery('simple',unaccent2('wroclaw')) 
    order by displaycount
) t 
LIMIT 20;

ou:

SELECT * 
FROM (
    SELECT * 
    FROM location 
    WHERE to_tsvector('simple',unaccent2("city"))
       @@ to_tsquery('simple',unaccent2('wroclaw'))
) t 
order by displaycount 
LIMIT 20;

Respostas:


12

Meu palpite é que isso resolveria sua consulta:

SELECT * 
FROM   location 
WHERE     to_tsvector('simple',unaccent2(city))
       @@ to_tsquery('simple',unaccent2('wroclaw')) 
ORDER  BY to_tsvector('simple',unaccent2(city))
       @@ to_tsquery('simple',unaccent2('wroclaw')) DESC
         ,displaycount 
LIMIT  20;

Repito a WHEREcondição como primeiro elemento da ORDER BYcláusula - que é logicamente redundante, mas deve impedir o planejador de consultas de assumir que seria melhor processar as linhas de acordo com o índice location_displaycount_index- o que acaba sendo muito mais caro.

O problema subjacente é que, obviamente, o planejador de consultas julga de maneira grosseira a seletividade e / ou o custo de sua WHEREcondição. Só posso especular por que isso acontece.

Você tem autovacuum correndo - o que também deve cuidar de correr ANALYZEem suas mesas? Assim, suas estatísticas de tabela estão atualizadas? Qualquer efeito se você executar:

ANALYZE location;

E tente novamente?

Também pode ser que a seletividade do @@operador esteja sendo julgada incorretamente. Eu imaginaria que é muito difícil estimar por razões lógicas.


Se minha consulta não resolver o problema e, geralmente, para verificar a teoria subjacente, faça uma destas duas coisas:

O último é menos invasivo e afeta apenas a sessão atual. Deixa os métodos bitmap heap scane bitmap index scanabre, que são usados ​​pelo plano mais rápido.
Em seguida, execute novamente a consulta.

BTW: Se a teoria for sólida, sua consulta (como você a possui agora) será muito mais rápida com um termo de pesquisa menos seletivo na condição do STF - ao contrário do que você poderia esperar. Tente.


11
A consulta funciona. Desligar o indexscan também funciona. ANALYZE não funciona. Muito obrigado pela resposta abrangente.
Ziri

0

Ao usar um ajuste LIMIT do postgresql, seu plano é ideal para recuperar apenas o subconjunto da linha. Infelizmente, de alguma forma, faz uma escolha errada no seu caso. Isso pode ocorrer porque as estatísticas da tabela são muito antigas. Tente atualizar a estatística emitindo o local VACUUM ANALYZE;

Forçar o uso de índices normalmente é feito impedindo o uso de varreduras sequenciais (configure enable_seqscan = false). No entanto, no seu caso, não está fazendo uma varredura seqüencial, apenas alterna para um índice diferente para a consulta com o LIMIT.

Caso a análise não ajude, você poderia dizer qual versão do postgresql está usando? Também quantas linhas existem na tabela?


Analisar não ajudou. A tabela possui cerca de 36000 linhas e estou usando o postgresql 9.1.
Ziri
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.