web-dev-qa-db-fra.com

Variations de performance de requête PostgreSQL LIKE

Les temps de réponse des requêtes LIKE à une table particulière de ma base de données varient considérablement. Parfois, j'obtiens des résultats dans les 200 à 400 ms (très acceptable), mais dans d'autres cas, cela peut prendre jusqu'à 30 secondes pour obtenir des résultats.

Je comprends que les requêtes LIKE nécessitent beaucoup de ressources, mais je ne comprends tout simplement pas pourquoi il y aurait une telle différence dans les temps de réponse. J'ai construit un index btree sur le champ owner1 mais je ne pense pas que cela aide les requêtes LIKE. Quelqu'un a des idées?

Exemple de code SQL:

SELECT gid, owner1 FORM parcels
WHERE owner1 ILIKE '%someones name%' LIMIT 10

J'ai aussi essayé:

SELECT gid, owner1 FROM parcels
WHERE lower(owner1) LIKE lower('%someones name%') LIMIT 10

Et:

SELECT gid, owner1 FROM parcels
WHERE lower(owner1) LIKE lower('someones name%') LIMIT 10

Avec des résultats similaires.
Nombre de lignes de la table: environ 95 000.

86
Jason

FTS ne prend pas en charge LIKE

Le réponse précédemment acceptée était incorrect. Recherche en texte intégral avec ses index en texte intégral est pas pour l'opérateur LIKE du tout, il a le sien opérateurs et ne fonctionne pas pour des chaînes arbitraires. Il fonctionne sur mots sur la base de dictionnaires et dérivés. Il fait ​​prend en charge le préfixe correspondant aux mots, mais pas avec l'opérateur LIKE:

Trigramme index pour LIKE

Installez le module supplémentaire pg_trgm qui fournit des classes d'opérateurs pour index de trigrammes GIN et Gist = prendre en charge tous les modèles LIKE et ILIKE, pas uniquement ceux à gauche:

Exemple d'index:

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

Ou:

CREATE INDEX tbl_col_Gist_trgm_idx ON tbl USING Gist (col Gist_trgm_ops);

Exemple de requête:

_SELECT * FROM tbl WHERE col LIKE '%foo%';   -- leading wildcard
SELECT * FROM tbl WHERE col ILIKE '%foo%';  -- works case insensitively as well_

Des trigrammes? Qu'en est-il des chaînes plus courtes?

Les mots avec moins de 3 lettres dans les valeurs indexées fonctionnent toujours. Le manuel:

Chaque mot est considéré comme ayant deux espaces préfixés et un espace suffixé lors de la détermination de l'ensemble des trigrammes contenus dans la chaîne.

Et les modèles de recherche avec moins de 3 lettres? Le manuel:

Pour les recherches à la fois LIKE et par expression régulière, n'oubliez pas qu'un modèle sans trigrammes extractibles dégénérera en analyse complète.

Cela signifie que les analyses d'index bitmap/index fonctionnent toujours (les plans de requête pour une instruction préparée ne se briseront pas), ils ne vous achèteront pas de meilleures performances. Généralement pas de grosse perte, car les chaînes de 1 ou 2 lettres ne sont guère sélectives (plus de quelques pour cent des correspondances de table sous-jacentes) et la prise en charge des index n'améliorerait pas les performances pour commencer, car une analyse complète de table est plus rapide.


_text_pattern_ops_ pour la correspondance de préfixe

Pour seulement modèles ancrés à gauche (aucun caractère générique de tête), vous obtenez l'optimum avec un classe d'opérateur approprié pour un index btree: _text_pattern_ops_ ou _varchar_pattern_ops_. Les deux fonctionnalités intégrées de Postgres standard, aucun module supplémentaire n'est nécessaire. Des performances similaires, mais un index beaucoup plus petit.

Exemple d'index:

CREATE INDEX tbl_col_text_pattern_ops_idx ON tbl(col text_pattern_ops);

Exemple de requête:

_SELECT * FROM tbl WHERE col LIKE 'foo%';  -- no leading wildcard_

O, si vous utilisez votre base de données avec la locale 'C' (effectivement no locale), tout est trié selon l'ordre des octets. de toute façon et un index Btree simple avec une classe d'opérateur par défaut fait le travail.

Plus de détails, explications, exemples et liens dans ces réponses connexes sur dba.SE:

260
Erwin Brandstetter

Peut-être que les plus rapides sont des modèles ancrés avec des caractères sensibles à la casse comme ceux qui peuvent utiliser des index. c'est-à-dire qu'il n'y a pas de caractère générique au début de la chaîne de correspondance, de sorte que l'exécuteur peut utiliser une analyse de plage d'index. ( le commentaire pertinent dans la documentation est ici ) Lower et ilike perdront également votre capacité à utiliser l'index, sauf si vous créez spécifiquement un index à cette fin (voir index fonctionnels ).

Si vous souhaitez rechercher une chaîne au milieu du champ, vous devez rechercher dans le texte intégral ou les indices de trigrammes . Le premier d'entre eux est dans le noyau Postgres, l'autre est disponible dans les modules contrib.

7
Ants Aasma

Vous pouvez installer Wildspeed , un type d'index différent dans PostgreSQL. Wildspeed fonctionne avec les caractères génériques% Word%, pas de problème. L'inconvénient est la taille de l'indice, il peut être grand, très gros.

4
Frank Heikens

Veuillez exécuter la requête mentionnée ci-dessous pour améliorer les performances de la requête LIKE dans postgresql . Créez un index comme celui-ci pour les tables plus volumineuses:

CREATE INDEX <indexname> ON <tablename> USING btree (<fieldname> text_pattern_ops)
2
Noyal

J'ai récemment eu un problème similaire avec une table contenant 200 000 enregistrements et je dois faire des requêtes répétées LIKE. Dans mon cas, la chaîne en cours de recherche a été corrigée. Autres domaines variés. C'est pour ça que j'ai pu réécrire:

SELECT owner1 FROM parcels
WHERE lower(owner1) LIKE lower('%someones name%');

comme

CREATE INDEX ix_parcels ON parcels(position(lower('someones name') in lower(owner1)));

SELECT owner1 FROM parcels
WHERE position(lower('someones name') in lower(owner1)) > 0;

J'ai été ravi lorsque les requêtes sont revenues rapidement et j'ai vérifié que l'index est utilisé avec EXPLAIN ANALYZE:

 Bitmap Heap Scan on parcels  (cost=7.66..25.59 rows=453 width=32) (actual time=0.006..0.006 rows=0 loops=1)
   Recheck Cond: ("position"(lower(owner1), 'someones name'::text) > 0)
   ->  Bitmap Index Scan on ix_parcels  (cost=0.00..7.55 rows=453 width=0) (actual time=0.004..0.004 rows=0 loops=1)
         Index Cond: ("position"(lower(owner1), 'someones name'::text) > 0)
 Planning time: 0.075 ms
 Execution time: 0.025 ms
1
Stephen Quan

Vos requêtes similaires ne peuvent probablement pas utiliser les index que vous avez créés pour les raisons suivantes:

1) vos critères LIKE commencent par un caractère générique.

2) vous avez utilisé une fonction avec vos critères LIKE.

0
Asaph

pour ce que cela vaut, Django ORM a tendance à utiliser UPPER(text) pour toutes les requêtes LIKE afin de le rendre insensible à la casse, 

L'ajout d'un index sur UPPER(column::text) a considérablement accéléré mon système, contrairement à tout autre chose.

En ce qui concerne%, oui, cela n’utilisera pas d’indice. Voir ce blog pour une bonne explication:

https://use-the-index-luke.com/sql/where-clause/searching-for-ranges/like-performance-tuning

0
MrE