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

#1 31/05/2011 17:42:02

sbouchard
Membre

Durée d'exécution d'une requête

Bonjour,

Contexte : 
Etude de faisabilité pour une migration SQLServer 2005 vers PostgreSQL 8.4.3 d'un projet de Datawarehouse (modeste)

Environnement :
PostgreSQL 8.4.3 sur WindowsServer2008
Client pgAdmin sur un poste de travail WindowXP
Client psql sur un poste de travail WindowXP

Problème : Durée d'exécution d'une requête SQL dans les traitements ETL sous PostgreSQL

La requête :

DELETE FROM HISTO_PersoHoraires 
WHERE Numero NOT IN 
    (SELECT Numero_TEC FROM KEEP_PersoHoraires)
  AND DateArchivage IS NULL

Volume des données pour tests :
Table HISTO_PersoHoraires : 1'383'508 lignes
Table KEEP_PersoHoraires  : 1'383'508 lignes
Nombre de lignes satisfaisant le critère DateArchivage IS NULL : 1'379'998

Durée d'exécution : 
Dans SQLServer  - Environ 10 minutes
Dans PostgreSQL - Après 6h30 la requête est toujours en cours d'exécution

Structure des tables : 

spagobi_stagingarea=> \d histo_persoHoraires
                  Table "spagobi.histo_persohoraires"
          Column           |              Type              | Modifiers
---------------------------+--------------------------------+-----------
 numero                    | numeric(18,0)                  | not null
 num_formations            | numeric(18,0)                  |
 num_lieuxdeformations     | numeric(18,0)                  |
 num_persomodules          | numeric(18,0)                  |
 num_justifications        | numeric(18,0)                  |
 num_periodesenseignements | numeric(18,0)                  |
 ladate                    | timestamp(6) without time zone |
 estabsent                 | numeric(18,0)                  | not null
 estenvacances             | numeric(18,0)                  |
 datearchivage             | timestamp(6) without time zone |
 luetarchive               | numeric(18,0)                  |
 ajuser                    | character varying(120)         |
 ajdate                    | timestamp(6) without time zone |
 mouser                    | character varying(120)         |
 modate                    | timestamp(6) without time zone |
 mocount                   | numeric(18,0)                  |
Indexes:
    "histo_persohoraires_pkey" PRIMARY KEY, btree (numero)
    "xu_ladate" btree (ladate)
    "xu_num_lieuxdeformations" btree (num_lieuxdeformations)
    "xu_num_persomodules" btree (num_persomodules)


spagobi_stagingarea=> \d keep_persoHoraires
                            Table "spagobi.keep_persohoraires"
   Column   |     Type      |                          Modifiers
------------+---------------+--------------------------------------------------------------
 numero     | numeric(18,0) | not null default nextval('seq_keep_persohoraires'::regclass)
 numero_tec | numeric(18,0) | not null
Indexes:
    "pk_keep_persohoraires" PRIMARY KEY, btree (numero)

Plan d'exécution de la requête : 

spagobi_stagingarea=> EXPLAIN DELETE FROM HISTO_PersoHoraires
spagobi_stagingarea-> WHERE Numero NOT IN
spagobi_stagingarea->     (SELECT Numero_TEC FROM KEEP_PersoHoraires)
spagobi_stagingarea->   AND DateArchivage IS NULL;
                                        QUERY PLAN
------------------------------------------------------------------------------------------
 Seq Scan on histo_persohoraires  (cost=29136.00..14526900364.85 rows=689978 width=6)
   Filter: ((datearchivage IS NULL) AND (NOT (SubPlan 1)))
   SubPlan 1
     ->  Materialize  (cost=29136.00..46936.00 rows=1280000 width=8)
           ->  Seq Scan on keep_persohoraires  (cost=0.00..22856.00 rows=1280000 width=8)

Mes questions :
Ce problème provient-il d'un mauvais paramétrage de mon serveur base de données?
Devrais-je indexer différemment certaines colonnes?
Avez-vous une piste de recherche à m'indiquer?

Cela m'ennuie un peu de retravailler sur la requête car pour une première étude nous
voulions faire une migration 1-1, c'est-à-dire réécrire les traitements tels quels afin d'avoir un
comparatif des 2 environnements.

D'avance merci pour vos avis

Sylvie

Hors ligne

#2 31/05/2011 17:54:47

flo
Membre

Re : Durée d'exécution d'une requête

Déjà il ne peut utiliser aucun index... est-ce que c'est normal?
Il n'y a en particulier pas d'index sur keep_persohoraires.numero_tec. Peut-être qu'avec cet index il pourrait utiliser un plan différent?

Dernière modification par flo (31/05/2011 17:56:37)

Hors ligne

#3 31/05/2011 17:58:11

Marc Cousin
Membre

Re : Durée d'exécution d'une requête

Vous avez probablement un work_mem trop bas pour une requête de ce type. Avec la syntaxe 'IN', postgresql ne va pas savoir faire d'antijoin, et va donc partir sur ce genre de plan.

Non, aucun intérêt de passer par un index pour aller rechercher tous les enregistrements de keep_persohoraires. On ramène 100% des enregistrements.

Essayez, spécifiquement pour cette requête, d'augmenter le work_mem. Il est, je présume, à sa valeur par défaut de 1Mo ?
Par exemple, dans une session: set work_mem to '128MB'.

Hormis parcours de la table, pour une requête de ce type (avec à peu près la même volumétrie) et avec un select à la place du delete, je suis à 3 secondes d'exécution avec mon simple ordinateur portable (pour la partie filtrage bien sûr, je n'ai pas recréé toutes vos données…)

Par contre, je ne peux pas vous conseiller comme ça de passer le work_mem à 128Mo globalement dans le postgresql.conf: c'est un paramètre qui affecte toutes les sessions, ce qui fait que les sessions peuvent tous consommer beaucoup de mémoire avec ce paramètre à une valeur si élevée. C'est à réserver à des requêtes manipulant beaucoup de données en première approche. Sauf si vous êtes sure d'avoir peu de sessions en parallèle (ce qui est assez probable avec un datawarehouse…).

Pas la peine de réexécuter la requête, contentez vous d'un explain.

Donc :
set work_mem to '128MB';
explain…


Marc.

Hors ligne

#4 01/06/2011 09:16:38

sbouchard
Membre

Re : Durée d'exécution d'une requête

Bonjour,

Merci pour votre réponse rapide, claire et précise! Vous êtes toujours aussi efficace!

Effectivement, en augmentant la valeur de ce paramètre pour la session, l'exécution de la requête dure 5 secondes. 

Cela me montre que j'ai encore bien des compétences à acquérir dans l'administration d'un serveur PostgreSQL!

Bonne journée!

Sylvie

Voici à titre documentaire le résultat de mon test avec le même jeu de données que précédemment :

spagobi_stagingarea=> set work_mem to '128MB';
SET

spagobi_stagingarea=> EXPLAIN DELETE FROM HISTO_PersoHoraires
spagobi_stagingarea-> WHERE Numero NOT IN
spagobi_stagingarea->   (SELECT Numero_TEC FROM KEEP_PersoHoraires)
spagobi_stagingarea-> AND DateArchivage IS NULL;
                                     QUERY PLAN
------------------------------------------------------------------------------------
 Seq Scan on histo_persohoraires  (cost=26056.00..63284.85 rows=689978 width=6)
   Filter: ((datearchivage IS NULL) AND (NOT (hashed SubPlan 1)))
   SubPlan 1
     ->  Seq Scan on keep_persohoraires  (cost=0.00..22856.00 rows=1280000 width=8)
(4 rows)


spagobi_stagingarea=> EXPLAIN ANALYSE DELETE FROM HISTO_PersoHoraires
spagobi_stagingarea-> WHERE Numero NOT IN
spagobi_stagingarea->   (SELECT Numero_TEC FROM KEEP_PersoHoraires)
spagobi_stagingarea-> AND DateArchivage IS NULL ;
                                                              QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------------------
 Seq Scan on histo_persohoraires  (cost=26056.00..63284.85 rows=689978 width=6) (actual time=5004.884..5004.884 rows=0 loops=1)
   Filter: ((datearchivage IS NULL) AND (NOT (hashed SubPlan 1)))
   SubPlan 1
     ->  Seq Scan on keep_persohoraires  (cost=0.00..22856.00 rows=1280000 width=8) (actual time=0.047..1700.607 rows=1383508 loops=1)
 Total runtime: 5010.056 ms
(5 rows)

Hors ligne

#5 01/06/2011 09:37:39

Marc Cousin
Membre

Re : Durée d'exécution d'une requête

Ça me semble plus raisonnable. Il ne s'agit que de hacher et comparer 1 million d'enregistrements entiers, après tout smile

Mais on est dans un cas où le meilleur plan a besoin de beaucoup de mémoire. Ce qu'on ne pourrait pas autoriser globalement sur une base transactionnelle avec 500 sessions, par exemple…

Si c'est un infocentre, et que vous connaissez approximativement le nombre de sessions, vous pouvez compter, pour ne pas prendre de risque, 3 à 5 tris/hachages simultanés par session en première approximation. Chaque session peut prendre (au besoin, elle ne va pas allouer 128Mo ici si elle n'en a pas besoin) autant de fois work_mem qu'elle effectue de tris ou hachages. Donc assurez vous simplement que vous n'aurez pas 50 sessions faisant simultanément 5 tris à 128Mo de work_mem: ça voudrait dire 50 X 5 X 128 Mo, soit 25 Go de ram.

Bref, le réglage de ce paramètre dépend vraiment du nombre de sessions effectuant des requêtes en même temps, de la complexité de ces requêtes, et de la mémoire sur le serveur.

Dernier point: si je comprends bien, c'est un nouveau projet. Pourquoi ne pas partir sur une 9.0 ? Il y a encore pas mal d'améliorations de l'optimiseur dans cette version, et vraiment peu de choses qui pourraient poser pb en termes de compatibilité. Il y en a encore quelques unes qui arrivent en 9.1 d'ailleurs (surtout si vous aviez besoin de recourir au partitionnement).


Marc.

Hors ligne

#6 01/06/2011 10:19:57

sbouchard
Membre

Re : Durée d'exécution d'une requête

La migration vers la version 9.0.4 de mon serveur PostgreSQL est planifiée pour la semaine prochaine!

Merci pour ces conseils dans l'ajustement de ce paramètre work_mem.

Sylvie

Hors ligne

Pied de page des forums