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

#1 Re : Général » Scalabilité horizontale de PostgreSQL 9.1 pour bases > 2To » 15/11/2011 19:08:29

sco

Tout ceci se clarifie doucement. Je vais creuser la piste Slony.

Merci encore Guillaume !

#2 Re : Général » Scalabilité horizontale de PostgreSQL 9.1 pour bases > 2To » 15/11/2011 18:35:18

sco

Si vous utilisez BO, il a besoin de tables de travail. Donc, si vous voulez rester à la réplication interne de PostgreSQL, il vous faut un pgpool devant qui redirige les écritures sur le maître et les lectures sur l'esclave. Sinon, vous pouvez utiliser Slony, surtout sur un schéma aussi basique. Et vous pourrez utiliser l'esclave bien mieux pour votre outil de BI, Slony n'interdisant pas les modifications de schéma sur les tables non répliquées et autorisant l'ajout d'index sur les tables répliquées. Dans votre cadre spécifique, je vous conseillerai plutôt Slony si votre outil de BI a besoin d'écrire.

Oula... là je suis perdu ! smile

Pourquoi le fait que Slony n'interdise pas les modifications de schéma sur les tables non répliquées va aider mon outil de BI ?
On ne peut pas ajouter d'index à une table répliquée par Postgres lui-même ?
C'est ces deux points qui vont font préférer Slony à Postgres+Pgpool dans mon cas ?

Concernant Bucardo, pourquoi pas. Attention que vous aurez à gérer les conflits en écriture. Pour Postgres XC, à ma connaissance, ce n'est pas à installer en prod actuellement.

Complexe à mettre en oeuvre, moins mature.. je passe mon chemin !

#3 Re : Général » Scalabilité horizontale de PostgreSQL 9.1 pour bases > 2To » 14/11/2011 11:24:09

sco

Très clair pour la question 1. On peut rêver à imaginer du parallélisme à l'avenir pour une même requête wink

Concernant la rétention, elle est intégré dans l'approximation de volume. L'idée est de retenir 5 ans maximum (on part sur 500Go/an minimum au départ).

Question 3 : je suis parti du principe que non, mais effectivement, la question mérite d'être posée à l'éditeur, ce que je viens de faire. C'est embêtant car ça mine toute possibilitié de faire du scaling, sauf à utiliser une architecture maitre-maitre c'est ça ? (et dans ce cas les choix pourraient par ex. être Bucardo, PostgresXC )

Question 4 : Oui, je pense que le plus raisonnable reste de prendre le temps de maquetter la chose.

Merci pour ces éclaircissements !

#4 Re : Général » Scalabilité horizontale de PostgreSQL 9.1 pour bases > 2To » 13/11/2011 23:54:22

sco

Bonsoir Guillaume,

Question 1 : dimensionnement dans le cas d'un noeud unique ?

Oui j'avoue avoir été léger en détails. La vérité est que la volumétrie est encore hypothétique.

Partons sur une table unique, une trentaine de champs pour 1000 octets au total (taille des données brutes) : un timestamp, une dizaines de compteurs (qu'on veut grapher), quelques chaines de caractères sur lesquelles on va filtrer. 5 index sur les colonnes de filtre.

En écriture :
- une centaine de clients faisant des INSERT, disons un toutes les dix secondes, en permanence, 24/7
En lecture :
- un client unique (l'outil de BI) qui viendrait faire ses requêtes deux à trois fois par jour pour générer un dashboard. Je n'ai pas regardé la complexité des requêtes générés par Tableau.

J'ai constaté que Postgres n'utilisait qu'un seul processeur, ce qui me semble sous-optimal. Donc finalement ca rend ma question stupide en réalité, non ?

Question 3 :
cf la question 1 wink

Question 4 : GreenPlum & Co
D'expérience, savez vous à quelle volumétrie on peut s'y intéresser (plutot 100Go, 1Po et +) ? Grossièrement bien sûr.

J'espère avoir été plus précis cette fois. Encore merci de votre aide !

#5 Général » Scalabilité horizontale de PostgreSQL 9.1 pour bases > 2To » 13/11/2011 23:15:22

sco
Réponses : 8

Bonjour,

Je travaille à la conception d'un entrepot de données de l'ordre de 2 à 3 To. Il est rempli par des applications métiers, et requeté par un outil BI du type Tableau à des fins d'analytics assez simples.

Question 1 : Quel serait le dimensionnement necessaire pour un noeud unique en termes de RAM/CPU ?

Je pensais partir sur une approche maitre-esclaves.  Depuis la version 9.1 de Postgres, j'ai vu qu'une fonctionnalité de réplication synchrone est disponible. J'imaginais pouvoir faire du load balancing, mais la documentation n'est pas très claire sur ce point. Dans mon idée : un master gérant lectures et ecritures, et pouvant déléguer les lectures sur d'autres esclaves.

Question 2 : PostgreSQL 9.1 est-il autonome sur le sujet, ou dois-je passer par Pgpool ?

Question 3 : Combien d'esclaves pourraient être nécessaires pour avoir des performances acceptables ? Peut-on ajouter des esclaves à chaud ?

Question 4 : Un passage à Greenplum/AsterDate/GridSQL est-il indispensable ?

Ca fait beaucoup de questions mais mon expérience PostgreSQL passée se résumé à des bases de 10Go tournant sur QuadCore/24Go de RAM... donc autant dire sans (mauvaise!) surprise.

Merci de vos lumières !

#6 Re : Général » Transaction commitée, mais nouvelle ligne pas vue ! » 30/06/2011 23:00:54

sco

Je vois .. si effectivement en lisant la séquence BEGIN/COMMIT/BEGIN/COMMIT on peut envisager que la séquence réelle était BEGIN/BEGIN/COMMIT/COMMIT ca change tout ! Dommage que l'ordre ne soit pas fiable...

Je vais essayer ceci : http://www.postgresql.org/docs/current/ … RT-EXAMPLE, plutôt confiant !

Merci Guillaume !

#7 Re : Général » Transaction commitée, mais nouvelle ligne pas vue ! » 30/06/2011 14:33:28

sco

Ce qui me tracasse c'est le log postgresql, il n'y a vraiment rien qui puisse expliquer ce comportement !

#8 Général » Transaction commitée, mais nouvelle ligne pas vue ! » 30/06/2011 00:19:40

sco
Réponses : 4

Bonsoir,

Alors là, je suis pantois devant ceci :

[15074] 2011-06-29 23:29:17 CEST 4/88492009 0  LOG:  instruction : begin transaction
[15074] 2011-06-29 23:29:17 CEST 4/88492009 0  LOG:  exécute <unnamed>: select count_application_hit from application_hit where id_account_application_hit = $1  and appfm_application_hit = $2  and appver_application_hit = $3  and appname_application_hit = $4  for update
[15074] 2011-06-29 23:29:17 CEST 4/88492009 0  DÃTAIL:  paramètres : $1 = '105739', $2 = 'ios', $3 = '305', $4 = 'NomAppli'
[15074] 2011-06-29 23:29:17 CEST 4/88492009 0  LOG:  exécute <unnamed>: insert into application_hit ( id_account_application_hit , apppfm_application_hit , appver_applcation_hit , appname_application_hit , count_application_hit ) values ( $1  , $2  , $3  , $4  , $5  )
[15074] 2011-06-29 23:29:17 CEST 4/88492009 0  DÃTAIL:  paramètres : $1 = '105739', $2 = 'ios', $3 = '305', $4 = 'NomAppli', $5 = '1'
[15074] 2011-06-29 23:29:17 CEST 4/88492009 124647794  LOG:  instruction : commit
[15075] 2011-06-29 23:29:17 CEST 5/53691771 0  LOG:  instruction : begin transaction
[15075] 2011-06-29 23:29:17 CEST 5/53691771 0  LOG:  exécute <unnamed>: select count_application_hit from application_hit where id_account_application_hit = $1  and appfm_application_hit = $2  and appver_application_hit = $3  and appname_application_hit = $4  for update
[15075] 2011-06-29 23:29:17 CEST 5/53691771 0  DÃTAIL:  paramètres : $1 = '105739', $2 = 'ios', $3 = '305', $4 = 'NomAppli'
[15075] 2011-06-29 23:29:17 CEST 5/53691771 0  LOG:  exécute <unnamed>: insert into application_hit ( id_account_application_hit , apppfm_application_hit , appver_applcation_hit , appname_application_hit , count_application_hit ) values ( $1  , $2  , $3  , $4  , $5  )
[15075] 2011-06-29 23:29:17 CEST 5/53691771 0  DÃTAIL:  paramètres : $1 = '105739', $2 = 'ios', $3 = '305', $4 = 'NomAppli', $5 = '1'
[15075] 2011-06-29 23:29:17 CEST 5/53691771 124647795  ERREUR:  la valeur d'une clé dupliquée rompt la contrainte unique « application_hit_pkey »
[15075] 2011-06-29 23:29:17 CEST 5/53691771 124647795  INSTRUCTION :  insert into application_hit ( id_account_application_hit , apppfm_application_hit , appver_application_hit , appname_application_hit , count_application_hit ) values ( $1  , $2  , $3  , $4  , $5  )

En clair : j'ouvre une transaction(4/88492009), je regarde si j'ai une ligne contenant (105739,ios,305,NomAppli), et comme elle n'existe pas, je l'insère et commite la transaction. Un pouième plus tard, une nouvelle requete se pointe via une autre transaction (5/53691771), refait le select et bam!, postgres semble ne pas avoir trouvé la ligne commitée précedemment. Là j'avoue ne pas comprendre. Le fait de la commiter ne la rend pas visible aux futures transactions ?

Pour etre exhaustif, voici le code ECPG que j'utilise :

EXEC SQL    SELECT count_application_hit
  INTO    :pg_count
  FROM application_hit
  WHERE id_account_application_hit = :pg_id_account
          AND apppfm_application_hit = :pg_apppfm
          AND appver_application_hit = :pg_appver
          AND appname_application_hit = :pg_appname
  FOR UPDATE ;

if ( sqlca.sqlcode == ECPG_NOT_FOUND ) {
    EXEC SQL    INSERT INTO  application_hit ( id_account_application_hit , apppfm_application_hit , appver_application_hit , appname_application_hit , count_application_hit )
                VALUES     ( :pg_id_account , :pg_apppfm , :pg_appver , :pg_appname , :pg_count );

    if ( sqlca.sqlcode != ECPG_NO_ERROR ) {
        return ( sqlca.sqlcode );
    }
}
else {
    EXEC SQL    UPDATE  application_hit
                SET     count_application_hit = count_application_hit + 1
                WHERE id_account_application_hit = :pg_id_account
                    AND apppfm_application_hit = :pg_apppfm
                    AND appver_application_hit = :pg_appver
                    AND appname_application_hit = :pg_appname;

    if ( sqlca.sqlcode != ECPG_NO_ERROR ) {
        return ( sqlca.sqlcode );
    }
}

(d'ailleurs à me relire je pourrais tester que le SELECT s'est passé sans erreur avant de faire l'UPDATE...).

Merci de vos lumières !

SCO

#9 C et C++ » Procedures stockees et PREPARE/EXECUTE en ECPG » 21/06/2011 14:24:16

sco
Réponses : 1

Bonjour à tous,

Suite à des essais avec une version 8.4 de la base, j'ai quelques impressions, certains d'entre-vous pourraient-ils confirmer ou infirmer :

- pas possible de faire du PREPARE/EXECUTE dans de l'ECPG. J'ai constamment du "-202:too few arguments on line...". Mais j'ai le bon nombre d'arguments
- pas possible d'appeler une procedure stockée depuis du code embarqué. J'ai constamment du "-202:too few arguments on line..." alors que la meme ligne passe dans psql sans probleme :

CREATE OR REPLACE FUNCTION insert_objet(text , text )
RETURNS void AS
$delimiter$
INSERT INTO objet ( sn_objet , mac_address_objet )
VALUES ( $1 , $2 );
$delimiter$
LANGUAGE SQL;

Depuis ECPG (FAIL) :
EXEC SQL SELECT insert_objet('sdfd','a5:55:44:88:78:04');

Depuis psql (OK) :
LOG:  statement: SELECT insert_objet('sdqqfd','aa:55:44:88:78:04');
LOG:  duration: 3.505 ms
insert_objet
--------------------

(1 ligne)

Mon cas d'usage c'est 100000 à 200000 INSERT à faire dans une table. De base, il faut compter pres de 5 minutes, ce qui semblait beaucoup. Je voulais tenter un PREPARE, ou une procedure stockée. Sans succès...

Merci !

SCO

#10 Re : C et C++ » [ECPG] Performances du SELECT/FETCH » 09/12/2010 18:05:42

sco

Oh mais je commence à mettre les choses ensembles... J'ignorais l'existence de la syntaxe FETCH FORWARD, et donc j'utilisais un offset (celui sur lequel porte votre question!) pour récupérer "les 1000 suivantes", puis "encore les 1000 suivantes".
Si je comprends bien, le fait de passer une tableau de 1000 ne suffit pas à indiquer à ECPG qu'il faut fetcher par plus que 1 à chaque FETCH ?

L'utilisation du FORWARD dans le FETCH et l'élimination de l'offset devenu superflu divisent par 6 le temps d'exécution. C'est déjà plus acceptable. A l'occasion je refais un benchmark avec psql pour savoir plus précisemment où on en est.

Marc, encore mille mercis pour votre aide et votre patience !

SC

#11 Re : C et C++ » [ECPG] Performances du SELECT/FETCH » 07/12/2010 16:15:29

sco

Le code présenté précédemment est un fetch avec curseur explicite, ce qui suit est une tentative de FETCH 1000 par 1000 avec un curseur implicite, avec passage de tableaux :

/*
*
*/
int DBGet_AllItem ( long id_customer , long item_type , long item_id , long endDate , long startDate ) {

#define MAX_PGROWS_SIZE                 1000

EXEC SQL BEGIN DECLARE SECTION;
long            pg_id_item_grp [ MAX_PGROWS_SIZE ];
long            pg_id_item [ MAX_PGROWS_SIZE ];
long            pg_id_customer [ MAX_PGROWS_SIZE ];
long            pg_item_type [ MAX_PGROWS_SIZE ];
long            pg_startDate;
long            pg_endDate;
EXEC SQL END DECLARE SECTION;

int             sqlcode                 = 0;
int             idx                        = 0;

        /* A few inits */
        pg_id_customer = id_customer;
        pg_endDate = endDate;
        pg_startDate = startDate;
        pg_item_type = item_type;
        pg_id_item = item_id;

        pg_max_rows = MAX_PGROWS_SIZE;
        pg_offset_rows = 0;

        do {
                EXEC SQL        SELECT                  item_groups.id_item_grp,
                                                        item_groups.id_customer_item_grp,
                                                        id_item,
                                                        type_item
                                INTO    :pg_id_item_grp, :pg_id_customer , :pg_id_item , :pg_type_item;
                                FROM    ( SELECT id_item_grp, id_customer_item_grp
                                                        FROM item_grp
                                                        WHERE   date_item_grp >= :pg_startDate
                                                        AND date_item_grp <= :pg_endDate
                                                        AND ( id_customer_item_grp = :pg_id_customer )
                                                        AND ( id_item_grp = :pg_id_item_grp OR :pg_id_item_grp = -1 )
                                                        ORDER BY        date_item_grp DESC , id_item_grp DESC
                                        ) AS item_groups
                                INNER JOIN item ON
                                                        ( id_item_grp = id_item_grp_item
                                                        AND ( type_item = :pg_item_type OR :pg_item_type = -1 ) )
                                ORDER BY        id_item_grp DESC , id_item DESC , type_item ASC
                                OFFSET :pg_offset_rows
                                LIMIT :pg_max_rows;

                if ( sqlca.sqlcode == ECPG_NO_ERROR ) {
                        for ( idx=0 ; idx<sqlca.sqlerrd[2] ; idx++ ) {
                                /* Ici on ne fait rien du résultat récu, afin de tester les perfs */
                        }
                        pg_offset_rows += idx;
                }
        } while ( sqlca.sqlcode == ECPG_NO_ERROR );

        return ( ECPG_NO_ERROR );
}

#12 Re : C et C++ » [ECPG] Performances du SELECT/FETCH » 06/12/2010 23:38:50

sco

Non, j'avais un doute sur le traitement des données après réception, donc j'ai chronométrer, puis fini par supprimer les traitements autres que la pure récupération. Il n'y a guere plus que du code touchant de près à Postgres.
Le code résultant ressemble à celui indiqué ci-dessous.


/*
*
*/
int DBGet_AllItem ( long id_customer , long item_type , long item_id , long endDate , long startDate ) {

EXEC SQL BEGIN DECLARE SECTION;
long            pg_id_item_grp;
long            pg_id_item;
long            pg_id_customer;
long            pg_item_type;
long            pg_startDate;
long            pg_endDate;
EXEC SQL END DECLARE SECTION;

int             sqlcode                 = 0;

        /* A few inits */
        pg_id_customer = id_customer;
        pg_endDate = endDate;
        pg_startDate = startDate;
        pg_item_type = item_type;
        pg_id_item = item_id;
        
        EXEC SQL DECLARE cur_allitem CURSOR FOR
                                SELECT                  item_groups.id_item_grp,
                                                        item_groups.id_customer_item_grp,
                                                        id_item,
                                                        type_item
                                FROM    ( SELECT id_item_grp, id_customer_item_grp
                                                        FROM item_grp
                                                        WHERE   date_item_grp >= :pg_startDate
                                                        AND date_item_grp <= :pg_endDate
                                                        AND ( id_customer_item_grp = :pg_id_customer )
                                                        AND ( id_item_grp = :pg_id_item_grp OR :pg_id_item_grp = -1 )
                                                        ORDER BY        date_item_grp DESC , id_item_grp DESC
                                        ) AS item_groups
                                INNER JOIN item ON
                                                        ( id_item_grp = id_item_grp_item
                                                        AND ( type_item = :pg_item_type OR :pg_item_type = -1 ) )
                                ORDER BY        id_item_grp DESC , id_item DESC , type_item ASC;

        EXEC SQL        OPEN cur_allitem;
        if ( sqlca.sqlcode != ECPG_NO_ERROR ) {
                return ( sqlca.sqlcode );
        }

        while ( sqlca.sqlcode == ECPG_NO_ERROR ) {
                EXEC SQL        FETCH   cur_allitem
                                        INTO        :pg_id_item_grp, :pg_id_customer , :pg_id_item , :pg_type_item;
                if ( sqlca.sqlcode == ECPG_NO_ERROR ) {
                    // Aucun traitement ici en vue de ces tests de perf
                }
                else if ( sqlca.sqlcode == ECPG_NOT_FOUND ) {
                        break;
                }
                else {
                        sqlcode = sqlca.sqlcode;
                        EXEC SQL CLOSE cur_allitem;
                        if ( sqlca.sqlcode != ECPG_NO_ERROR ) {
                              return ( sqlca.sqlcode );
                        }
                        return ( sqlcode );
                }
        }

        EXEC SQL CLOSE cur_allitem;
        if ( sqlca.sqlcode != ECPG_NO_ERROR ) {
                return ( sqlca.sqlcode );
        }

        return ( ECPG_NO_ERROR );
}

J'ai quelque peu simplifié le code pour le rendre plus lisible.

#13 Re : C et C++ » [ECPG] Performances du SELECT/FETCH » 02/12/2010 11:54:15

sco

Pour faire des fetchs dans mon code, j'ouvre systématiquement un curseur. Je 'fetch' ce curseur jusqu'a ce qu'il ne retourne plus de résultats.

Donc à ce point mes interrogations demeurent. D'autres idées ? wink

#14 Re : C et C++ » [ECPG] Performances du SELECT/FETCH » 01/12/2010 17:50:51

sco

Bonjour Marc,

Il me manque une info pour pouvoir répondre à cette question : j'imagine que l'appel a \set dans psql a une portée qui se limite uniquement au process psql en question, et n'est pas un paramètre global de postgresql. La preuve, je relance psql, et la variable n'est pas settée.

Si je comprends bien votre question, je devrais passer l'équivalent d'une telle commande en SQL embarqué dans mon code. Comment faire ? Je sèche !

Merci encore !

#15 Re : C et C++ » [ECPG] Performances du SELECT/FETCH » 26/11/2010 10:03:26

sco

Bonjour Marc,

Alors je reprend, j'avoue que c'était confus. Voici les manipulations faites sur la base de test, à froid, en arrivant le matin :

Step #1 :
\set cursor_tuple_fraction 1
SELECT <....> retourne en 1400 ms
SELECT <....> retourne en 44 ms
SELECT <....> retourne en 34 ms
SELECT <....> retourne en 40 ms

Step #2
\unset cursor_tuple_fraction
SELECT <....> retourne en 42 ms
SELECT <....> retourne en 37 ms
SELECT <....> retourne en 41 ms

Step #3 :
\set cursor_tuple_fraction 1
SELECT <....> retourne en 41 ms
SELECT <....> retourne en 44 ms
SELECT <....> retourne en 34 ms

Voilà, je pense que c'est deja plus clair en effet !

#16 Re : C et C++ » [ECPG] Performances du SELECT/FETCH » 25/11/2010 10:24:25

sco

Oui effectivement, je suis en 8.4.

Apres passage de cursor_tuple_fraction à 1, le tout premier SELECT a pris environ 1400ms (peut etre parce que j'ai pris la base un peu à froid), tous les essais suivants seulement environ 40ms.
J'ai essayé ensuite d'unset cursor_tuple_fraction, les essais suivants ne sont pas spécialement plus longs que 40ms.
J'ai repassé cursor_tuple_fraction à 1, les temps de requetage oscillent tous autour de 40 ms.

D'autres idées ? wink

#17 Re : C et C++ » [ECPG] Performances du SELECT/FETCH » 24/11/2010 12:19:34

sco

Bon, je n'ai pas pu m'empêcher de faire quelques petits essais avec libpq...
Le verdict est sans appel : j'ai des logs qui sortent sur la sortie standard, ils montrent une moyenne approchant plutôt les 50ms. J'ai du mal à rester assis !

A-t'on suffisamment de preuves pour commencer a envisager un probleme au niveau du SQL embarqué ?

#18 Re : C et C++ » [ECPG] Performances du SELECT/FETCH » 24/11/2010 11:44:13

sco

Bonjour Marc,

Marc Cousin a écrit :

Testez toujours en demandant à psql de faire aussi des fetch 1000 par 1000:
\set FETCH_COUNT 1000
Et tentez la requête. Il fera exactement comme vous: ouverture d'un curseur et récupération des résultats 1000 par 1000.

Avec psql, un fetch ligne par ligne (avec \set FETCH_COUNT 1) en moyenne coute 68ms pour fetcher l'ensemble des 1740 lignes, avec un fetch 1000 par 1000, la moyenne redescend a 60ms.

Avec mon code (tenez-vous bien), avec un fetch ligne par ligne (donc sans utilisation de tableaux alloués statiquement), je m'en sors avec 1400ms en moyenne. Et grosso modo 600ms avec des tableaux alloués statiquement et sans FETCH mais des SELECT avec un offset +1000 pour chaque SELECT.

Très étrange aussi : si je me contente d'un COUNT sur la requete plutot que de fetcher le résultat, j'en suis deja a 78ms en moyenne... Le meme count sur psql tourne autour de 50ms. Le cout du COUNT ne me choque pas (j'en ai absolument besoin, et d'une manière précise), mais c'est le fait qu'il soit presque 50% plus élevé que par psql alors que le volume de données transférées est j'imagine très faible.

La durée indiquée par psql (eg. "LOG:  duration: 50.639 ms") indique-t'il le temps complet de récupération de l'intégralité des lignes résultat, ou le temps jusqu'à la completion du premier FETCH partiel ( de FETCH_COUNT lignes donc).

psql utilise la lib libpq non ? Ca vaudrait le coup que je compare avec ou pas ?

Merci encore !

#19 Re : Réplication » [PG 9.0] Streaming replication sans log-shipping » 23/11/2010 19:53:08

sco
Marc Cousin a écrit :

Ils ne doivent pas 'nécessairement' faire 16Mo. C'est juste que la commande d'archivage cp se contente de recopier le WAL. On peut en réduire la taille avec pg_lesslog. Mais ça reste un contrib pour le moment. Que vaut checkpoint_timeout et checkpoint_segments ? Cela aussi peut avoir un gros impact (parce qu'après chaque checkpoint PostgreSQL doit à nouveau écrire des images complètes de bloc, ce qui est beaucoup plus volumineux).

Hmmm, je n'avais jamais entendu parler de pg_lesslog, très intéressant ! Combiné avec l'ouverture permise par le 'archive_command' s'est vrai que c'est un excellent compromis temps de restauration/espace disque.
J'ai dis des bêtises, j'avais configuré le archive_timeout a 5 minutes, ceci expliquant cela. Bon 2,3Go ca reste énorme, mais couplé a pg_lesslog, "it does the job!".

Merci encore !

#20 Re : C et C++ » [ECPG] Performances du SELECT/FETCH » 23/11/2010 19:34:22

sco
Marc Cousin a écrit :

- Avez vous vérifié après avoir modifié le code C pour faire ces fetchs de tableau qu'il n'y a effectivement plus autant de dialogues client/serveur ?

Oh que oui, c'est flagrant. C'est infiniment moins verbeux en fetchant 1000 par 1000.

Marc Cousin a écrit :

- Êtes vous sûr d'avoir le même plan d'exécution pour la requête que celui de psql (vous faites une requête préparée ?)

Non, il ne s'agit pas d'une requête préparée, je la passe avec des variables hote directement comme ceci :

EXEC SQL
SELECT <les colonnes qui vont bien>
INTO                   <mes variables :pg_ qui vont bien>
FROM    ( SELECT <ce qu'il faut> FROM <la table>
                                WHERE  <les clauses qui vont bien>
                                ORDER BY        <comme il faut>
                                OFFSET         <le bon>
                        ) AS alias_de_tout_ca
INNER JOIN <l'autre table> ON 
                        <la bonne jointure>
ORDER BY        <comme il faut>
OFFSET <ce qu'il faut>
LIMIT <la bonne>

Je n'ai rien d'autre qui pourrait consommer dans ce bout de code (pas de log, pas d'écriture fichier, pas d'affichage, pas de calcul), il s'agit d'une récupération de base. Je suis à sec !

#21 C et C++ » [ECPG] Performances du SELECT/FETCH » 23/11/2010 18:15:48

sco
Réponses : 20

Bonjour,

Je viens m'enquérir ici du retour d'expérience sur les performances du SELECT/FETCH sur des gros set de résultats en SQL embarqué en C.

J'ai des interrogations sérieuses sur l'écart flagrant de performances entre une requête exécutée avec psql, et cette même requete exécutée dans mon code en C.
Je constate un facteur 10 (psql étant, vous vous en douterez le plus rapide), ce qui me semble simplement énorme !

La requete SELECT est une jointure entre deux tables (25000 lignes et 50000 lignes respectivement chacune), renvoyant environ 2000 lignes. psql retourne les résultat en 80ms, mon application en 800ms.

Sur le coup, j'ai fait un tour sur le channel IRC pg et on m'a suggéré de faire un strace. Il s'est averé que vraisemblablement le FETCH ligne par ligne me faisait perdre énormément de temps. Au vu de la documentation 9.1devel, j'ai tenté de passer un tableau alloué statiquement pour par exemple 1000 lignes, et donc au final mon code C itère deux fois sur le FETCH, je fais deux FETCH au lieu de 2000. Le gain obtenu est de l'ordre de 50%, puisque je récupère l'ensemble des 2000 lignes maintenant en 400 ms environ. Fetcher davantage que 1000 lignes à chaque fois n'a plus d'impact sur les performances.
C'est mieux, mais encore loin de mon objectif qui est de m'approcher de psql.

Avez-vous déjà constaté un tel différentiel ? Comment vous en êtes-vous sorti ?

Merci d'avance pour vos retours !

#22 Re : Réplication » [PG 9.0] Streaming replication sans log-shipping » 23/11/2010 17:59:24

sco
Marc Cousin a écrit :

- Normal qu'il n'ait pas renommé recovery.conf en recovery.done: la récupération est toujours en cours, vous streamez toujours des journaux du maître vers l'esclave. Vous n'aurez un recovery.done que le jour où vous ouvrirez la base secondaire en lecture écriture (en créant le fichier trigger par exemple)

Effectivement, vu sous cet angle là je comprends mieux. Dans mon esprit, la récupération ne concernait que la relecture des fichiers WAL locaux, pas le streaming via socket.

Marc Cousin a écrit :

- Les fichiers WAL sont toujours transférés: le maître ne sait pas où en est l'esclave. Seul l'esclave sait ce qu'il a à faire. Le maître continue donc à archiver, en utilisant votre script d'archivage (qui, je le présume, recopie les fichiers). Ce mécanisme a un autre intérêt: si vous aviez une déconnexion de la réplication pendant 'trop longtemps', il se pourrait que la streaming replication ne puisse plus être utilisée: les journaux seraient archivés, et donc plus accessibles par le maître, pour être mis à disposition de l'esclave, via SR. Vous serez donc très content, ce jour là, d'avoir encore les fichiers archivés: il suffira de les rejouer jusqu'au dernier, puis reprendre la SR (tout cela est automatique).

Assurément, mais cela représente un coût de stockage énorme : avec un archive_timeout a 1800, pour une base de 300Mo, dans ma configuration actuelle en trois jours de tests, et pour une base avec un volume d'activité plutot faible j'en suis deja a plus de 13 Go de WAL. Je pense baisser la taille des segments de 16Mo à par exemple 2Mo. D'ailleurs pourquoi les segments WAL dont on force le flush avec archive_timeout doivent ils nécessairement faire 16 Mo (sauf si configuré autrement bien sur) ? La prix a payer pour pouvoir se dire "je peux restaurer ma base au pire telle qu'elle etait a t-1800secondes" n'est-il pas un peu cher ?

Marc Cousin a écrit :

- Comment il se récupère : répondu au dessus: l'algo est expliqué dans la doc, mais grosso modo c'est : essayer d'appliquer les archives aussi loin que possible, puis se connecter en SR au maître et appliquer les journaux via SR, puis si échec repasser en mode 'application' d'archive, etc… en boucle (avec une tempo quand même). Il vous faut donc le log shipping, si vous voulez pouvoir vous sortir de ce cas de figure.

Et oui, il me faut le log shipping !

Marc Cousin a écrit :

- Le slave sait reprendre le streaming où il en était, SI LE WAL EST ENCORE EN LIGNE. Sinon, il faut qu'il aille le chercher dans l'«archive», via sa restore_command. Ça peut très bien être cette 'restore_command' qui va chercher le fichier sur le maître, si vous ne voulez transférer qu'au moment où ça devient nécessaire. Vous pouvez aussi limiter ce cas de figure en augmentant le paramètre wal_keep_segments, qui demande au maître de garder davantage de fichiers WAL en ligne, justement dans l'optique de ce cas de figure.

Ah, je n'avais pas bien compris, au temps pour moi ! Pour moi, le slave commençait par relire ses WAL en local (ceux archivés par le maitre), puis redémarrait le streaming, mais que le streaming ne pouvait pas "revenir en arrière" en relisant les journaux du maître. C'est bien plus clair maintenant !

Merci beaucoup Marc pour toutes ces précisions qui couplées à une doc somme toute vraiment très bien m'ont bien éclairci l'esprit.

#23 Réplication » [PG 9.0] Streaming replication sans log-shipping » 22/11/2010 12:48:11

sco
Réponses : 5

Bonjour,

J'ai monté une maquette PG 9 pour évaluer la nouvelle feature de streaming replication. Je souhaite disposer d'un moyen de remonter très rapidement une base, avec un minimum de pertes de transactions, donc pour ça, la réplication par flux me semble toute indiquée. J'ai donc monté un master sur lequel vient se connecter un slave en hot standby. J'ai bootstrappé le slave et faisant un backup comme indiqué en §24.3.2. La réplication se fait correctement (par contre, bizarrement sur le slave j'ai l'impression que le recovery.conf n'a pas été renommé en recovery.done...)

J'ai deux questions.

Je constate dans un premier temps qu'à l'issue du restore, les fichiers WAL sont toujours shippés par le primary vers mon slave. Mis à part pour "faire le PITR", êtes-vous d'accord avec moi pour dire que faire du log-shipping ne me sert (dans mon cas) plus a rien puisque le streaming fait deja jouer les transactions sur le slave via une connexion TCP/IP (donc pas besoin d'avoir ces WAL en backup non ?) ? L'inconvénient est qu'on va arriver rapidement à saturer le disque à conserver des WAL non ? J'imagine que l'utilisation standard est de faire un backup tous les x jours et donc ne ne conserver que x jours de WAL, c'est ça ?

D'autre part, je me pose une question sur le fonctionnement attendu lorsque le serveur slave tombe, et que le log-shipping n'est pas utilisé (uniquement le streaming l'étant).
Je me demandais comment se récupère le standby quand il revient :
- il tente de rejouer ses WAL en local (il n'y en a pas dans mon cas, étant donné que je ne fais pas de log shipping)
- il tente de rejouter  le pg_xlog (éventuellement il y a des trucs, qui sont des transactions streamées depuis le master mais pas encore commitées dans la base slave au moment de l'interruption)
- il se reconnecte au master et remet en place la réplication par streaming. C'est là où intervient ma question : le slave sait-il reprendre le streaming là où il en était [dans ce cas, on tombe bien dans le cas du keep_wal_segments a configurer proprement pour s'assurer un historique suffisant ?) ou bien a-t'on forcément un gap irrécupérable ? On est d'accord que je n'ai pas besoin du 'restore_command' dans le recovery.conf ?

Merci pour vos lumières !

Pied de page des forums

Propulsé par FluxBB