Como criar um índice para acelerar uma consulta LIKE agregada em uma expressão?

20

Eu posso estar fazendo a pergunta errada no título. Aqui estão os fatos:

Meu pessoal de atendimento ao cliente está reclamando de tempos de resposta lentos ao fazer pesquisas de clientes na interface de administração do nosso site baseado em Django.

Estamos usando o Postgres 8.4.6. Comecei a registrar consultas lentas e descobri o culpado:

SELECT COUNT(*) FROM "auth_user" WHERE UPPER("auth_user"."email"::text) LIKE UPPER(E'%deyk%')

Esta consulta está demorando mais de 32 segundos para ser executada. Aqui está o plano de consulta fornecido pelo EXPLAIN:

QUERY PLAN
Aggregate  (cost=205171.71..205171.72 rows=1 width=0)
  ->  Seq Scan on auth_user  (cost=0.00..205166.46 rows=2096 width=0)
        Filter: (upper((email)::text) ~~ '%DEYK%'::text)

Como esta é uma consulta gerada pelo Django ORM a partir de um Django QuerySet gerado pelo aplicativo Admin do Django, não tenho controle sobre a própria consulta. Um índice parece ser a solução lógica. Tentei criar um índice para acelerar isso, mas não fez diferença:

CREATE INDEX auth_user_email_upper ON auth_user USING btree (upper(email::text))

O que estou fazendo errado? Como posso acelerar esta consulta?

David Eyk
fonte

Respostas:

21

Não há suporte para índice no LIKE/ ILIKEno PostgreSQL 8.4 - exceto nos termos de pesquisa ancorados à esquerda .

Desde o PostgreSQL 9.1, o módulo adicional pg_trgmfornece classes de operadores para índices trigramas GIN e GiST que suportam LIKE/ ILIKEou expressões regulares (operadores ~e amigos). Instale uma vez por banco de dados:

CREATE EXTENSION pg_trgm;

Exemplo de índice GIN:

CREATE INDEX tbl_col_gin_trgm_idx ON tbl USING gin (col gin_trgm_ops);

Relacionado:

Erwin Brandstetter
fonte
2
Esta é realmente a resposta correta.
vonPetrushev 13/07/2013
9

Esse índice não ajudará por causa do '%' no início da sua correspondência - um índice BTREE pode corresponder apenas a prefixos e o curinga no início da sua consulta significa que não há prefixo fixo para procurar.

É por isso que está fazendo uma varredura de tabela e combinando todos os registros, por sua vez, com a string de consulta.

Provavelmente, você precisa usar um índice de texto completo e os operadores de correspondência de texto, em vez de fazer a pesquisa de substring com LIKE que você está no momento. Você pode encontrar mais informações sobre a pesquisa de texto completo na documentação:

http://www.postgresql.org/docs/8.4/static/textsearch-intro.html

Na verdade, percebo nessa página que LIKE aparentemente nunca usa índices, o que me parece estranho, pois deveria ser capaz de resolver prefixos não curinga usando um índice BTREE. No entanto, alguns testes rápidos sugerem que a documentação provavelmente está correta, caso em que nenhuma quantidade de indexação ajudará enquanto você estiver usando o LIKE para resolver a consulta.

TomH
fonte
Era disso que eu tinha medo. Existe outro tipo de índice que ajudará? Como eu disse, estou um pouco limitado em minha capacidade de afetar a própria consulta.
David Eyk
Além disso, o principal %é um recurso necessário: os representantes de atendimento ao cliente precisam dele para encontrar contas de clientes, especialmente quando há um erro de digitação no endereço de e-mail.
David Eyk
Bem, depois de um pouco de pesquisa sobre o LIKE e a indexação de texto completo, estou começando a entender o seu ponto.
David Eyk
Por enquanto, encontrei uma maneira de suprimir o curinga principal. Acontece que você pode usar um índice com LIKE, se você criar o índice com uma classe de operador apropriada . Os documentos estão aqui: postgresql.org/docs/8.4/static/indexes-opclass.html
David Eyk
Além disso, verifique se o seu banco de dados está inchado. Se você tiver muito inchaço nessa tabela, levará muito tempo para verificar seq. Se você tiver algum tempo de inatividade, basta agrupá-lo na chave primária e ver se fica mais rápido. Se você deseja verificar o inchaço, pode executar o analyse e, em seguida, execute a consulta aqui: wiki.postgresql.org/wiki/Show_database_bloat . Para valores mais precisos, consulte o final da página.
Scott Marlowe