Comment créer un index pour accélérer une requête LIKE agrégée sur une expression?

20

Je pose peut-être la mauvaise question dans le titre. Voici les faits:

Mes gens du service client se sont plaints de la lenteur des temps de réponse lors de la recherche de clients sur l'interface d'administration de notre site basé à Django.

Nous utilisons Postgres 8.4.6. J'ai commencé à enregistrer des requêtes lentes et j'ai découvert ce coupable:

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

Cette requête prend plus de 32 secondes pour s'exécuter. Voici le plan de requête fourni par 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)

Comme il s'agit d'une requête générée par l'ORM Django à partir d'un ensemble de requêtes Django généré par l'application d'administration Django, je n'ai aucun contrôle sur la requête elle-même. Un index semble être la solution logique. J'ai essayé de créer un index pour accélérer cela, mais cela n'a fait aucune différence:

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

Qu'est-ce que je fais mal? Comment puis-je accélérer cette requête?

David Eyk
la source

Réponses:

21

Il n'y a pas de prise en charge d'index pour LIKE/ ILIKEdans PostgreSQL 8.4 - à l'exception des termes de recherche ancrés à gauche .

Depuis PostgreSQL 9.1, le module supplémentaire pg_trgmfournit des classes d'opérateurs pour les index de trigrammes GIN et GiST supportant LIKE/ ILIKEou des expressions régulières (opérateurs ~et amis). Installez une fois par base de données:

CREATE EXTENSION pg_trgm;

Exemple d'index GIN:

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

En relation:

Erwin Brandstetter
la source
2
C'est en fait la bonne réponse.
vonPetrushev
9

Cet index ne va pas aider à cause du '%' au début de votre correspondance - un index BTREE ne peut correspondre qu'aux préfixes et le caractère générique au début de votre requête signifie qu'il n'y a pas de préfixe fixe à rechercher.

C'est pourquoi il effectue un balayage de table et fait correspondre chaque enregistrement tour à tour avec la chaîne de requête.

Vous devez probablement envisager d'utiliser un index de texte intégral et les opérateurs de correspondance de texte plutôt que de faire la recherche de sous-chaîne avec LIKE que vous êtes en ce moment. Vous pouvez trouver plus d'informations sur la recherche en texte intégral dans la documentation:

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

En fait, je remarque sur cette page que LIKE n'utilise apparemment jamais d'index, ce qui me semble étrange car il devrait être capable de résoudre les préfixes non génériques à l'aide d'un index BTREE. Quelques tests rapides suggèrent cependant que la documentation est probablement correcte, auquel cas aucune quantité d'indexation ne vous aidera pendant que vous utilisez LIKE pour résoudre la requête.

TomH
la source
Voilà ce que j'avais peur. Existe-t-il un autre type d'index qui vous aidera? Comme je l'ai dit, je suis un peu limité dans ma capacité à affecter la requête elle-même.
David Eyk
En outre, le début %est une fonctionnalité nécessaire: les représentants du service client en ont besoin pour trouver des comptes clients, en particulier lorsqu'il y a une faute de frappe dans l'adresse e-mail.
David Eyk
Eh bien, après un peu de recherche sur LIKE et l'indexation de texte intégral, et je commence à voir votre point.
David Eyk
Pour l'instant, j'ai trouvé un moyen de supprimer le caractère générique principal. Il s'avère que vous pouvez utiliser un index avec LIKE, si vous créez l'index avec une classe d'opérateur appropriée . Les documents sont ici: postgresql.org/docs/8.4/static/indexes-opclass.html
David Eyk
Vérifiez également votre base de données pour les ballonnements. Si vous avez beaucoup de ballonnement dans ce tableau, cela va prendre beaucoup de temps pour le scanner. Si vous avez un temps d'arrêt, regroupez-le simplement sur la clé primaire et voyez si cela devient plus rapide. Si vous voulez vérifier le ballonnement, vous pouvez exécuter l'analyse puis lancer la requête ici: wiki.postgresql.org/wiki/Show_database_bloat . Pour des valeurs plus précises, consultez le bas de cette page.
Scott Marlowe