Performance sur des fonctions plpgsql

From: philippe(dot)beaudoin(at)bull(dot)net
To: pgsql-fr-generale(at)postgresql(dot)org
Subject: Performance sur des fonctions plpgsql
Date: 2008-09-02 17:10:16
Message-ID: OFEF11C552.BC5FDE14-ONC12574B8.005E2764@frcl.bull.fr
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-fr-generale

Bonjour à tous,

Je travaille actuellement à la réalisation d'un outil de migration de
données pour le compte d'un de mes clients. Je dois transférer des données
d'un SGBDR mainframe (sous l'OS Bull GCOS 8) vers une base de données
PostgreSQL (8.3.3) sous Linux. La liaison canal entre les 2 serveurs
(infiniband) et la solution DBSP de Bull permet à des programmes Cobol (oui
c'est encore utilisé ;-)) sous GCOS 8 d'accéder à une base PostgreSQL avec
un bon niveau de performance. Pour PostgreSQL le client est vu sous la
forme d'un simple programme C local.

Pour cette migration de données, et après quelques essais, j'ai préféré
utiliser cette liaison canal plutôt que de décharger les lignes dans un
fichier plat, transférer ce fichier puis le recharger.

Je génère donc des programmes Cobol, 2 par table à traiter : l'un lit la
base d'origine (SELECT), l'autre écrit sur PostgreSQL, par INSERT.
Globalement, pour l'insertion, il me faut entre 0,5 et 0,8 ms par ligne,
suivant les caractéristiques de la table (taille de la ligne, nombre de
colonnes, nombre d'index,...). Le temps passé dans la communication
inter-serveur doit-être de l'ordre de 0,4 ms.

Pour minimiser le temps passé dans ce tuyau, je souhaite insérer plusieurs
lignes en un seul échange de message. J'ai donc suivi le conseil de la
documentation PostgreSQL en écrivant des fonctions d'insertion en plpgsql.
Concrètement, chaque programme PostgreSQL :
? crée un buffer contenant plusieurs lignes (à concurrence de 32Ko, taille
maximum du message physique échangeable entre les 2 systèmes) par
juxtaposition du contenu des colonnes, éventuellement précédé de la
longueur effective pour les données de longueur variable,
? appelle une fonction traitant ce buffer en réalisant ces insertions
multiples.

Une fois la mécanique mise au point, j'ai sorti mon chronomètre et
j'obtiens des temps d'insertion entre 25 et 40 fois plus longs qu'avec les
inserts simples !!!
Sur le serveur Linux, top montre des process PostgreSQL entre 96 et 100% de
cpu, (au lieu des 27% pour les INSERT). Visiblement mes fonctions sont très
cpu-vores ! Pourtant, d'après la documentation, j'ai compris qu'à la
première exécution, un plan est créé pour la fonction, permettant ensuite
des exécutions performantes.

Je me demande donc si :
? soit la technologie plpgsql est intrinsèquement coûteuse en cpu,
? soit j'ai raté quelque chose dans l'utilisation que j'en fait.

Voici un exemple pour une table créée par :

CREATE TABLE CDAC0 (
NDOMAT NUMERIC(7) NOT NULL,
CDNER001 BYTEA NOT NULL,
DIMPCO NUMERIC(9) NOT NULL,
AC0RT0 BYTEA NOT NULL,
HCOMME NUMERIC(7) NOT NULL,
NETIMA NUMERIC(1) NOT NULL,
NAGCAF CHARACTER(5) NOT NULL,
LICOM1 BYTEA NOT NULL,
LICOM2 BYTEA NOT NULL,
LICOM3 BYTEA NOT NULL,
CPRICO CHARACTER(1) NOT NULL)
TABLESPACE TSPD
;

et la fonction (qui reçoit en entrée le nombre de lignes à insérer et le
buffer contenant les données et qui retourne en sortie le nombre de lignes
réellement insérées) :

CREATE OR REPLACE FUNCTION INSCDAC0
(NBLIG SMALLINT, ENTREE CHARACTER(32000))
RETURNS INTEGER AS $$
DECLARE
OFS INTEGER;
RET INTEGER;
LG INTEGER;
HV_NDOMAT NUMERIC(7) ;
HV_CDNER001 BYTEA ;
HV_DIMPCO NUMERIC(9) ;
HV_AC0RT0 BYTEA ;
HV_HCOMME NUMERIC(7) ;
HV_NETIMA NUMERIC(1) ;
HV_NAGCAF TEXT ;
HV_LICOM1 BYTEA ;
HV_LICOM2 BYTEA ;
HV_LICOM3 BYTEA ;
HV_CPRICO TEXT ;
BEGIN
OFS=1; RET=0;
FOR I IN 1 .. NBLIG LOOP
HV_NDOMAT = TO_NUMBER(SUBSTR(ENTREE,OFS,8),'S9999999');
OFS=OFS + 8;
LG = TO_NUMBER(SUBSTR(ENTREE,OFS,5),'99999');
OFS=OFS + 5;
HV_CDNER001 = SUBSTR(ENTREE,OFS,LG);
OFS=OFS + LG;
HV_DIMPCO = TO_NUMBER(SUBSTR(ENTREE,OFS,10),'S999999999');
OFS=OFS + 10;
LG = TO_NUMBER(SUBSTR(ENTREE,OFS,5),'99999');
OFS=OFS + 5;
HV_AC0RT0 = SUBSTR(ENTREE,OFS,LG);
OFS=OFS + LG;
HV_HCOMME = TO_NUMBER(SUBSTR(ENTREE,OFS,8),'S9999999');
OFS=OFS + 8;
HV_NETIMA = TO_NUMBER(SUBSTR(ENTREE,OFS,2),'S9');
OFS=OFS + 2;
HV_NAGCAF = SUBSTR(ENTREE,OFS,5);
OFS=OFS + 5;
LG = TO_NUMBER(SUBSTR(ENTREE,OFS,5),'99999');
OFS=OFS + 5;
HV_LICOM1 = SUBSTR(ENTREE,OFS,LG);
OFS=OFS + LG;
LG = TO_NUMBER(SUBSTR(ENTREE,OFS,5),'99999');
OFS=OFS + 5;
HV_LICOM2 = SUBSTR(ENTREE,OFS,LG);
OFS=OFS + LG;
LG = TO_NUMBER(SUBSTR(ENTREE,OFS,5),'99999');
OFS=OFS + 5;
HV_LICOM3 = SUBSTR(ENTREE,OFS,LG);
OFS=OFS + LG;
HV_CPRICO = SUBSTR(ENTREE,OFS,1);
OFS=OFS + 1;
INSERT INTO CDAC0 VALUES (
HV_NDOMAT,
HV_CDNER001,
HV_DIMPCO,
HV_AC0RT0,
HV_HCOMME,
HV_NETIMA,
HV_NAGCAF,
HV_LICOM1,
HV_LICOM2,
HV_LICOM3,
HV_CPRICO
);
RET=RET+1;
END LOOP;
RETURN RET;
END;
$$ LANGUAGE PLPGSQL;

La fonction est créée en début de programme puis est supprimée à la fin du
programme.

Toute info pour me permettre de comprendre sera la bienvenue...

Responses

Browse pgsql-fr-generale by date

  From Date Subject
Next Message Samuel ROZE 2008-09-02 17:42:35 pgplsql: Substitution de variables
Previous Message Dimitri Fontaine 2008-09-02 15:06:11 Re: plpgsql: Des paramètres facultatifs ?