Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

[PostgreSQL] optimisation jointures

6 réponses
Avatar
Lionel
Bonjour,

J'ai un problème de performances sur certaines requetes sur postgresSQL 8.0,
je voudrais savoir s'il était possible de les améliorer avec de nouveaux
index.

Voici la situation:
- une table DONNEES contenant 2 million de lignes
- 5 colonnes de cette table sont des FK vers d'autres tables de plusieurs
dizaines de milliers de lignes, toutes avec un index (appelons les FK_X)
- certaines de ces tables possèdent une colonne étant une FK vers une autre
table, indexée également (appelons les FK_FK_X), possédant plusieurs
dizaines de lignes

Je fais un moteur de recherche multicriteres dynamique (au niveau du select,
des group by et des where, toutes les combinaisons sont possibles) qui
génère des requetes type:
select FK_FK_2, sum(donnee1), sum(donnee2)
from DONNEES
inner join FK_TABLE inner join FK_FK_TABLE
where FK_1=truc and FK_FK_3=chose
ORDER BY FK_FK2

Comment faire pour optimiser les perf dans ce genre de cas ?
Le problème ne vient pas du nombre de lignes remontée (une dizaine
généralement) mais des jointures qui forcent un parcours complet de la table
DONNEES.
C'est le disque qui mouline, le CPU est à 20%.

Autre souci très proche, si je fais une requete à plat sur les données avec
jointure vers 4 ou 5 tables avec un where remontant 10000 lignes et un order
by, la requete prend plusieurs minutes.
Ajouter un limit 50 ne change strictement rien (normal il est logiquement
appliqué après le order by)
Ce sont les multiples jointures qui coutent cher, mais je peux difficilement
m'en passer.

Passer à la v8.2 pourrait améliorer ce genre de requêtes assez gourmandes en
permettant d'éviter un parcours total de la table données ?

Merci pour vos conseils

6 réponses

Avatar
Patrick Mevzek
Le Mon, 26 Nov 2007 18:20:15 +0100, Lionel a écrit:
Je fais un moteur de recherche multicriteres dynamique (au niveau du select,
des group by et des where, toutes les combinaisons sont possibles) qui
génère des requetes type:
select FK_FK_2, sum(donnee1), sum(donnee2)
from DONNEES
inner join FK_TABLE inner join FK_FK_TABLE
where FK_1=truc and FK_FK_3=chose
ORDER BY FK_FK2



Votre syntaxe est incorrecte, il manque un ON
toto INNER JOIN tata ON (toto.x=tata.y)
etc...

Et sur cet exemple, il faut, en général, avoir des index sur toto.x et
tata.y si on veut des performances correctes.
Est-ce votre situation ?

Comment faire pour optimiser les perf dans ce genre de cas ?



Que dit EXPLAIN ?

C'est le disque qui mouline, le CPU est à 20%.



Peut-être est-il au maximum de ses performances votre disque ?
Alternativement, plus PostgreSQL a de RAM disponible plus, via le noyau,
peut-il avoir une grosse partie de la base de données en RAM.

Autre souci très proche, si je fais une requete à plat sur les données
avec jointure vers 4 ou 5 tables avec un where remontant 10000 lignes et
un order by, la requete prend plusieurs minutes. Ajouter un limit 50 ne
change strictement rien (normal il est logiquement appliqué après le
order by)
Ce sont les multiples jointures qui coutent cher, mais je peux
difficilement m'en passer.



Idem, voir ce que dit EXPLAIN.

Passer à la v8.2 pourrait améliorer ce genre de requêtes assez
gourmandes en permettant d'éviter un parcours total de la table données
?



Peut-être, peut-être pas.

Et l'accès séquentiel n'est *PAS* nécessairement la solution la plus
lente. Cela dépend. Le but n'est pas d'éliminer l'accès séquentiel, le but
est d'avoir la réponse la plus rapide possible, peu importe comment.

--
Patrick Mevzek . . . . . . . . . . . . . . Dot and Co
<http://www.dotandco.net/> <http://www.dotandco.com/>
Dépêches sur le nommage <news://news.dotandco.net/dotandco.info.news>
Avatar
Lionel
Patrick Mevzek wrote:
select FK_FK_2, sum(donnee1), sum(donnee2)
from DONNEES
inner join FK_TABLE inner join FK_FK_TABLE
where FK_1=truc and FK_FK_3=chose
ORDER BY FK_FK2



Votre syntaxe est incorrecte, il manque un ON
toto INNER JOIN tata ON (toto.x=tata.y)
etc...



Je sais c'était un exemple pour illustrer rapidement la problèmatique.

Et sur cet exemple, il faut, en général, avoir des index sur toto.x et
tata.y si on veut des performances correctes.
Est-ce votre situation ?



oui. x a un index et y est la PK de la table.

Comment faire pour optimiser les perf dans ce genre de cas ?


Que dit EXPLAIN ?



j'essaye de poster un exemple de plan demain.

Et l'accès séquentiel n'est *PAS* nécessairement la solution la plus
lente. Cela dépend. Le but n'est pas d'éliminer l'accès séquentiel,
le but est d'avoir la réponse la plus rapide possible, peu importe
comment.



Je sais. Actuellement les temps sont acceptables (30s la requete, c'est long
mais vue la quantité de données à traiter personne ne s'en plaindra, et je
leur fais économiser des heures de manip sordides sous excel). Je voudrais
juste etre certain que cette durée ne croit pas de facon linéaire ou
exponentielle lorsque la volumétrie augmente.
Avatar
Patrick Mevzek
Le Mon, 26 Nov 2007 19:43:28 +0100, Lionel a écrit:
Je voudrais
juste etre certain que cette durée ne croit pas de facon linéaire ou
exponentielle lorsque la volumétrie augmente.



Cela dépend de plein de choses aussi, en particulier
- le schéma des tables : vos clefs de jointure sont de quels types ?
(il faut que ce soit bien exactement le même, et de préférence un entier)
- la RAM disponible pour PostgreSQL (les tables tiennent-elles en RAM,
regardez dans la table pg_statio_user_tables)
- sa configuration (shared_buffers, work_mem, etc...)

Il n'y a pas non plus 36 façons de faire : remplissez vos tables de
données (pas complétement aléatoires, il faut assurer une répartition
statistique semblable avec vos vraies données), et vérifiez comment ca se
passe.
Cela vous permettra de régler votre système et de vérifier qu'il passe
bien à l'échelle dans 6 mois ou dans un an, selon les évolutions
volumétriques que vous estimerez.

--
Patrick Mevzek . . . . . . . . . . . . . . Dot and Co
<http://www.dotandco.net/> <http://www.dotandco.com/>
Dépêches sur le nommage <news://news.dotandco.net/dotandco.info.news>
Avatar
Lionel
Patrick Mevzek wrote:
Idem, voir ce que dit EXPLAIN.



Exemple de plan qui dure très longtemps....
Les nested loop sont dus aux IN ?
Il y a un code alpha que je devrais finalement pouvoir passer en int mais ca
m'étonnerait que cela change grand chose.

Sort ( rows=1 widthQ)
Sort Key: JOINTURE1.LIBELLE
-> HashAggregate ( rows=1 widthQ)
-> Nested Loop (cost=0.00..40116.72 rows=1 widthQ)
-> Nested Loop (cost=0.00..40110.87 rows=1 width)
-> Nested Loop (cost=0.00..40093.10 rows=3 widthU)
-> Index Scan using i_fk_JOINTURE2_fk1 on
JOINTURE2 (cost=0.00..46.31 rows=1 width‚)
Index Cond: (id2 = 51)
Filter: (((code)::text = '183'::text) OR
((code)::text = '184'::text))
-> Index Scan using i_fk_DONNEES_JOINTURE2 on
DONNEES (cost=0.00..40046.46 rows& width7)
Index Cond: ((DONNEES.id)::text =
("outer".id)::text)
Filter: ((un_boolean = false) AND (maDate >=
(('now'::text)::date - 500)) AND (maDate <= ('now'::text)::date))
-> Index Scan using pk_JOINTURE3 on JOINTURE3
(cost=0.00..5.91 rows=1 widthC)
Index Cond: (("outer".code)::text =
(JOINTURE3.code)::text)
Filter: ((ss_id = 82) OR (ss_id = 83) OR (ss_id =
84) OR (ss_id = 89))
-> Index Scan using pk on JOINTURE1 (cost=0.00..5.82 rows=1
widthG)
Index Cond: ("outer".id = JOINTURE1.id)
Avatar
Patrick Mevzek
Le Wed, 28 Nov 2007 19:05:28 +0100, Lionel a écrit:
Idem, voir ce que dit EXPLAIN.



Exemple de plan qui dure très longtemps....
Les nested loop sont dus aux IN ?



Difficile à dire, vous ne devez pas la requête SQL en question, et cela ne
semble pas être compatible avec celle donnée initialement dans ce fil.

Donc je ne vois pas quoi vous dire à part :
- vous avez bien fait un ANALYZE juste avant ?
- pourquoi ne pas tester avec la version 8.2 de PostgreSQL, voire la 8.3
quasi terminée, les performances vont plutôt en s'améliorant de version en
version (y compris pour les versions « mineures »).

Un EXPLAIN ANALYZE serait utile aussi cela permettrait de comparer ce que
le planificateur croit récupérer comme nombre de lignes par rapport à ce
qu'il récupère réellement.

Il y a un code alpha que je devrais finalement pouvoir passer en int mais ca
m'étonnerait que cela change grand chose.



Manipuler des entiers c'est nécessairement toujours plus rapide que de
manipuler des chaînes de caractères, même sans les problèmes d'encodage.
Surtout si au final vous avez des nombres dans vos champs alphanumériques :

Filter: (((code)::text = '183'::text) OR
((code)::text = '184'::text))



et

-> Index Scan using i_fk_DONNEES_JOINTURE2 on
DONNEES (cost=0.00..40046.46 rows& width7)
Index Cond: ((DONNEES.id)::text =
("outer".id)::text)



Vous avez une clef qui est un champ alphanumérique ?

Filter: ((un_boolean = false) AND (maDate >=
(('now'::text)::date - 500)) AND (maDate <= ('now'::text)::date))



Vous avez essayé avec des index sur un_boolean et/ou maDate ?

Index Cond: (("outer".code)::text =
(JOINTURE3.code)::text)



Idem plus haut, clef comme champ alphanumérique ?

--
Patrick Mevzek . . . . . . . . . . . . . . Dot and Co
<http://www.dotandco.net/> <http://www.dotandco.com/>
Dépêches sur le nommage <news://news.dotandco.net/dotandco.info.news>
Avatar
Patrick Mevzek
Le Wed, 12 Dec 2007 19:57:07 +0100, Lionel a écrit:
Vous avez une clef qui est un champ alphanumérique ?


cela arrive ,mais sur celle là c'est une erreur en réécrivant le plan...la
PK de la table des donnees est une sequence.
J'ai quelques clés alfa (code produit,..) pour améliorer les perfs lorsque
la clé est fréquemment utilisée comme critère de recherche ou affichée dans
un tableau, cela m'évite une jointure qui peut etre couteuse.



Vous avez *testé* ca et vérifié la différence de performances ou c'est ce
que vous pensez ?
Parce que justement plus haut vous dites le contraire (index sur entiers
plus petit donc meilleures performances).

Idem plus haut, clef comme champ alphanumérique ?



oui, parfois c'est mieux ainsi.

J'essaierai de mettre qq plans sans erreur + les requetes la semaine
prochaine, le sujet est très intéressant et je trouve très peu de doc sur la
facon d'interpréter un plan et en tirer les bonnes conclusions.



La meilleure doc, ce sont les listes de discussion PostgreSQL mais elles
sont en anglais, et vous trouverez là-bas des experts de la lecture d'un
EXPLAIN (je n'en suis pas).

--
Patrick Mevzek . . . . . . . . . . . . . . Dot and Co
<http://www.dotandco.net/> <http://www.dotandco.com/>
Dépêches sur le nommage <news://news.dotandco.net/dotandco.info.news>