Vous n'êtes pas identifié(e).

#1 06/04/2011 15:19:33

mafia49
Membre

Optimisation d'une requete SQL

Bonjour à tous.

Je vous écrit car je suis confronté a une requête plutôt longue dans le temps d'exécution.

C'est une requete qui cherche dans une colonne de type tsvector, et qui doit renvoyer les 48 premières lignes contenant 'coca'

Il y a un index sur la colonne tsv.


SELECT *
FROM PRODUCT
WHERE  product.tsv @@ plainto_tsquery('coca')
LIMIT 48 OFFSET 0


Temps d'éxécution : 245Ms, donc plutot bon.

Mais la ou cela se complique, c'est lorsque j'essaie de trier par num_product desc:

C'est un champ de type bigint, ou il y a un index mis en place

SELECT *
FROM PRODUCT
WHERE  product.tsv @@ plainto_tsquery('coca')
ORDER BY num_product DESC
LIMIT 48 OFFSET 0

Temps d'éxecution : 2500Ms, et la cela devient trop long pour l'utilisateur..

avez vous des idées d'optimisation possible?

Cordialement

Hors ligne

#2 06/04/2011 15:44:12

Marc Cousin
Membre

Re : Optimisation d'une requete SQL

Malheureusement, l'index pour les recherches plein-texte n'est pas un btree, mais un gin ou un gist. Ils ne sont pas «triés», et vous ne pouvez donc pas les utiliser pour optimiser ce genre d'accès. Le moteur est bien obligé d'aller chercher tous les mots contenant coca, récupérer le num_product qui va avec, et trier. Ou bien prendre les enregistrements de la table product par num_product descendant, et vérifier pour chaque enregistrement la condition sur le fulltext.

En tout cas, en ce qui me concerne, pas d'idée pour faire mieux là dessus.


Marc.

Hors ligne

#3 06/04/2011 17:41:34

mafia49
Membre

Re : Optimisation d'une requete SQL

Bonjour,

Merci pour votre réponse.

Celle-ci me parait étrange car ce sur quoi je trie est bien un entier, et non du full text?

Hors ligne

#4 06/04/2011 17:44:58

Marc Cousin
Membre

Re : Optimisation d'une requete SQL

Vous triez sur un entier. Mais vous filtrez sur un full text. Donc soit vous triez puis vous filtrez (et donc vous devez récupérer trop d'enregistrements parce que dans vos valeurs triées, il y aura des enregistrements ne correspondant pas à votre filtre), soit vous filtrez puis triez (mais dans ce cas, vous devez trier par un sort, et non par un parcours d'index, et vous devez avoir TOUS les enregistrements avant de les passer).

Pour trier et filtrer en même temps, on peut y arriver avec un index composé. Mais ça ne marche qu'en btree. Et le full text n'est pas indexable en btree.


Marc.

Hors ligne

#5 07/04/2011 16:49:56

mafia49
Membre

Re : Optimisation d'une requete SQL

OK.

Merci

Hors ligne

#6 20/05/2011 16:32:03

mafia49
Membre

Re : Optimisation d'une requete SQL

Vous me croirez ou non, la meme recherche full text sur SQL SERVER 2008 prends entre 6 et 50 ms grace à l'utilisation de la clause CONTAINS().

autrement dit, pour de la recherche full text, postgreSQL à encore du boulot à faire.

CDT.

Hors ligne

#7 20/05/2011 16:39:19

Marc Cousin
Membre

Re : Optimisation d'une requete SQL

Je vous crois. Mais sans comparaison, sans plus d'information, etc… c'est difficile. Si il met 50ms pour faire ça, très bien, mais j'aimerais savoir comment, pas me contenter de si peu.


Marc.

Hors ligne

#8 23/05/2011 16:56:26

mafia49
Membre

Re : Optimisation d'une requete SQL

En fait, sous Postgres, j'avais un champ de type tsvector, qui était la concaténation de 3 autres champs de la même table.
Sous SQL Server, ce champ était indéxé en Recherche de texte intégral (Menu Création..).

Une même requête avec jointure,  filtre sur un mot, puis tri pour obtenir les 48 premiers, prend 2500Ms sous PG, et entre 6 et 50ms sous SQL Server (meme nombre de ligne, meme schema de base).

Je ne saurais me l'expliquer à moi même ..

Hors ligne

#9 23/05/2011 17:04:45

Marc Cousin
Membre

Re : Optimisation d'une requete SQL

Les 48 premiers sur quel critère ? C'est toute la question… smile

Si c'est sur la proximité entre le texte recherché et le texte du document, on peut faire pas trop mal avec PostgreSQL (surtout avec la 9.1 qui arrive) aussi.

Sinon, je ne vois vraiment pas comment sql server pourrait faire (et j'aimerais bien le savoir…).

Dans les derniers points que je n'ai pas creusé côté PostgreSQL: sans le LIMIT, il y a combien d'enregistrements retournés ? Et l'index sur le texte, c'est du GIST ou du GIN ?


Marc.

Hors ligne

#10 23/05/2011 17:13:34

mafia49
Membre

Re : Optimisation d'une requete SQL

Que ce soit les 48, ou la totalités, le temps d'exec est quasiment le même.

Les 48, c'est un besoin de pagination seulement, ce pourrait etre les 1000, 500 etc.. Et c'est sur un critere de date (les 48 contenant le texte, ordonnés par date DESC).

C'est une recherche qui varie, sur 2 millions de lignes au total, il y a en moyenne 500 a 1000 retours positifs sur le filtre, et je n'affiche que les premiers.

L'index est un BTREE

Hors ligne

#11 23/05/2011 17:16:31

Marc Cousin
Membre

Re : Optimisation d'une requete SQL

Les tsvectors ne sont pas indexables par btree. Je crois que c'est là qu'on a un problème…

C'est quoi la définition de l'index sur la colonne tsv exactement ?


Marc.

Hors ligne

#12 23/05/2011 17:51:52

mafia49
Membre

Re : Optimisation d'une requete SQL

CREATE INDEX product_tsv_idx
  ON product
  USING btree
  (tsv);

Hors ligne

#13 23/05/2011 17:56:25

Marc Cousin
Membre

Re : Optimisation d'une requete SQL

Je ne pensais même pas que c'était possible smile (j'aurais du essayer une fois…)

Essayez de supprimer cet index, puis recréez le avec:

CREATE INDEX product_tsv_idx
ON product
USING gin
(tsv);

Refaites un ANALYZE, puis retestez.

L'index btree n'est pas utilisable pour l'opérateur @@… donc à l'heure actuelle, vous lisez toute la table à chaque requête.

Dernière modification par Marc Cousin (23/05/2011 19:24:40)


Marc.

Hors ligne

#14 24/05/2011 08:39:15

mafia49
Membre

Re : Optimisation d'une requete SQL

Les temps de réponse restent similaires a l'index BTREE..

Hors ligne

#15 24/05/2011 08:50:03

Marc Cousin
Membre

Re : Optimisation d'une requete SQL

Très étrange. On peut avoir le plan ? (même requête, mais avec un EXPLAIN ANALYZE devant)


Marc.

Hors ligne

#16 24/05/2011 10:44:13

mafia49
Membre

Re : Optimisation d'une requete SQL

"Limit  (cost=161978.97..161978.99 rows=10 width=59) (actual time=7892.944..7892.945 rows=10 loops=1)"
"  ->  Sort  (cost=161978.97..161979.68 rows=284 width=59) (actual time=7892.942..7892.943 rows=10 loops=1)"
"        Sort Key: (min(price.price))"
"        Sort Method:  top-N heapsort  Memory: 18kB"
"        ->  HashAggregate  (cost=161969.28..161972.83 rows=284 width=59) (actual time=7892.705..7892.808 rows=414 loops=1)"
"              ->  Nested Loop  (cost=0.00..161647.46 rows=42910 width=59) (actual time=42.308..7881.205 rows=24334 loops=1)"
"                    ->  Seq Scan on product  (cost=0.00..21700.56 rows=284 width=55) (actual time=42.281..7222.542 rows=489 loops=1)"
"                          Filter: (search @@ plainto_tsquery('coca'::text))"
"                    ->  Index Scan using price_num_product_idx on price  (cost=0.00..490.88 rows=151 width=12) (actual time=0.199..1.336 rows=50 loops=489)"
"                          Index Cond: (price.num_product = product.num_product)"
"Total runtime: 7893.049 ms"

Hors ligne

#17 24/05/2011 10:55:01

Marc Cousin
Membre

Re : Optimisation d'une requete SQL

Bon, on a donc un scan séquentiel sur la table product, qui représente toujours le plus gros du travail. Et c'est évidemment là que les bactéries se déposent. Il devrait évidemment utiliser l'index. Il doit donc y avoir un truc pas net quelque part. Vous devriez avoir quelque chose comme ça pour la recherche du mot:

EXPLAIN ANALYZE SELECT * from test where to_tsquery('mot_a_rechercher')@@tsv;
                                                   QUERY PLAN                                                   
----------------------------------------------------------------------------------------------------------------
Bitmap Heap Scan on test  (cost=4.26..8.28 rows=1 width=32) (actual time=0.042..0.042 rows=0 loops=1)
   Recheck Cond: (to_tsquery('mot_a_rechercher'::text) @@ tsv)
   ->  Bitmap Index Scan on tst_tsv  (cost=0.00..4.26 rows=1 width=0) (actual time=0.037..0.037 rows=0 loops=1)
         Index Cond: (to_tsquery('mot_a_rechercher'::text) @@ tsv)
Total runtime: 0.072 ms

Plusieurs points à voir :

- Qu'est ce que l'explain analyze donne si vous exécutez:
«SET enable_seqscan TO off;» dans la session avant de l'exécuter ? => C'est pour savoir si il ne veut pas utiliser l'index, ou si c'est qu'il ne peut pas
- Est-ce que vous pouvez poster la définition des tables (\d nom_table dans psql)
- Est-ce que vous me confirmez que ce n'est pas la même requête que celle que vous avez posté ? Le champ a l'air de s'appeller search et plus tsv, et vous avez 2 tables jointes et plus une ? Dans ce cas, donnez moi le code de la nouvelle requête smile
- Quelle version de PG ? J'aurais du vous la poser il y a un certain temps smile


Marc.

Hors ligne

#18 26/05/2011 13:42:49

Marc Cousin
Membre

Re : Optimisation d'une requete SQL

Plus de nouvelles ?


Marc.

Hors ligne

Pied de page des forums