PostgreSQLLa base de données la plus sophistiquée au monde.

F.18. pgbench

pgbench est un programme simple qui exécute un test de performances (benchmark en anglais) sur PostgreSQL™. Il exécute la même séquence de commandes SQL à plusieurs reprises, si possible dans plusieurs sessions en parallèle, puis calcule le taux de transaction moyen (transactions par seconde). Par défaut, pgbench teste un scénario vaguement basé sur TPC-B, impliquant cinq SELECT, UPDATE et INSERT par transaction. Néanmoins il est facile de tester d'autres cas en écrivant ses propres fichiers de transaction.

Voici un exemple d'affichage de pgbench :

transaction type: TPC-B (sort of)
scaling factor: 10
number of clients: 10
number of transactions per client: 1000
number of transactions actually processed: 10000/10000
tps = 85.184871 (including connections establishing)
tps = 85.296346 (excluding connections establishing)
 

Les quatre premières lignes indiquent simplement certains des paramètres les plus importants. La ligne suivante rapporte le nombre de transactions terminées et souhaitées (le dernier étant juste le produit du nombre de clients avec le nombre de transactions) ; ils doivent être égaux sauf si un échec est arrivé avant la fin. Les deux dernières lignes précisent le taux de transactions, avec et sans le temps de lancement de la session.

F.18.1. Aperçu

Le test de transaction par défaut, ressemblant à TPC-B, nécessite des tables particulières qu'il faut créer. pgbench doit être appelé avec l'option -i (initialisation) pour les créer et les peupler. (Lors du test d'un script personnalisé, cette étape n'est pas nécessaire, mais la base doit être configurée en fonction des tests.) L'initialisation ressemble à :

pgbench -i [ autres-options ] nom_base
   

dbname est le nom d'une base de données déjà créée et à utiliser pour les tests. (Les options -h, -p, et/ou -U peuvent être utilisées pour indiquer la façon de se connecter au serveur de bases de données.)

[Attention]

Attention

pgbench -i crée quatre tables : accounts, branches, history et tellers, détruisant toute table existante de ce nom. Il faut y être attentif s'il existe des tables de même nom !

Avec un « facteur d'échelle » de 1, les tables contiennent initialement ce nombre de lignes :

table           # de lignes
---------------------------
branches        1
tellers         10
accounts        100000
history         0
  

Le nombre de lignes peut être augmenter avec l'option -s (facteur d'échelle). L'option -F (facteur de remplissage) peut aussi être utilisée à ce moment.

Une fois que vous avez exécuté la configuration, vous pouvez lancer votre test de performance avec une commande qui n'inclut pas l'option -i, c'est-à-dire :

pgbench [ options ] nom_base
   

Dans la plupart des cas, vous aurez besoin de quelques options pour que ce test soit réellement intéressant. Les options les plus importantes sont -c (nombre de clients), -t (nombre de transactions) et -f (pour spécifier un script personnalisé). Voir ci-dessous pour une liste complète.

Tableau F.14, « Options d'initialisation de pgbench » affiche les options qui sont utiles lors de l'initialisation de la base de données, alors que Tableau F.15, « Options pour les tests de pgbench » affiche celles qui sont utiles lors de l'exécution des tests de performance et Tableau F.16, « Options communes de pgbench » affiches les options utiles dans les deux cas.

Tableau F.14. Options d'initialisation de pgbench

Option Description
-i Requis pour le mode initialisation.
-s facteur_echelle Multiplie le nombre de lignes générées par le facteur d'échelle. Par exemple, -s 100 ajoute 10 millions de lignes dans la table accounts. La valeur par défaut est 1.
-F fecteur_remplissage Crée les tables accounts, tellers et branches avec le facteur de remplissage indiqué. La valeur par défaut est 100.

Tableau F.15. Options pour les tests de pgbench

Option Description
-c clients Nombre de clients simultanés, autrement dit le nombre de sessions en parallèle. Par défaut, 1.
-t transactions Nombre de transactions exécutées par chaque client. Par défaut, 10.
-N Ne met pas à jour les tables tellers et branches. Ceci évitera les contentions des mises à jour sur ces tables mais du coup pgbench ne supporte pas les transactions du style TPC-B.
-S Réalise uniquement des transactions de sélection au lieu du TPC-B.
-f nom_fichier Lit le script de transaction à partir de nom_fichier. Des explications détaillées apparaîtront plus tard. -N, -S et -f ne sont pas utilisables ensemble.
-n Aucun VACUUM n'est réalisé pendant l'exécution du test. Cette option est nécessaire si vous exécutez un scénario de tests personnalisé qui n'inclut pas les tables standards accounts, branches, history et tellers.
-v Exécute un VACUUM sur les quatres tables avant le test. Sans -n et -v, pgbench exécute un VACUUM sur les seules tables tellers et branches, et supprimera toutes les entrées de history.
-D varname=value Définit une variable utilisée par un script personnalisé (voir plus bas). Vous pouvez utiliser plusieurs fois l'option -D.
-C Établie une connexion pour chaque transaction, plutôt que de se connecter une fois par client du thread. Ceci est utile pour mesurer la charge occasionnée par la connexion.
-l Écrit le temps pris par chaque transaction dans un journal applicatif. Voir ci-dessous pour les détails.
-s facteur_echelle Indique le facteur d'échelle spécifié en sortie de pgbench. Avec les tests internes, ce n'est pas nécessaire ; le bon facteur d'échelle sera détecté en comptant le nombre de lignes dans la table branches. Néanmoins, lors de tests de performance personnalisés (option -f), le facteur d'échelle sera rapporté valant 1 sauf si cette option est utilisée.
-d Affiche des informations de débogage.

Tableau F.16. Options communes de pgbench

Option Description
-h hostname Hôte du serveur de bases de données
-p port Numéro de port
-U login Nom utilisateur pour la connexion

F.18.2. Quelle transaction est réalisée dans pgbench ?

Le script de transaction par défaut exécute sept commandes :

  1. BEGIN;

  2. UPDATE accounts SET abalance = abalance + :delta WHERE aid = :aid;

  3. SELECT abalance FROM accounts WHERE aid = :aid;

  4. UPDATE tellers SET tbalance = tbalance + :delta WHERE tid = :tid;

  5. UPDATE branches SET bbalance = bbalance + :delta WHERE bid = :bid;

  6. INSERT INTO history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP);

  7. END;

Si vous indiquez -N, les étapes 4 et 5 ne sont pas inclus dans la transaction. Si vous indiquez -S, seul le SELECT est exécuté.

F.18.3. Scripts personnalisés

pgbench sait exécuter des scénarios personnalisés en remplaçant le script de transaction par défaut (décrit ci-dessus) avec un script de transaction lu à partir d'un fichier (option -f). Dans ce cas, une « transaction » compte en tant qu'une exécution du fichier. Vous pouvez même indiquer plusieurs scripts (avec plusieurs options -f), auquel cas un script est choisi au hasard à chaque fois qu'une session client exécute une nouvelle transaction.

Le fichier doit contenir une commande par ligne ; les commandes SQL multi-lignes ne sont pas acceptées. Les lignes vides et les lignes commençant par -- sont ignorées. Les lignes du fichier peuvent aussi contenir des « meta commandes », qui sont interprétées par pgbench lui-même, comme décrit ci-dessous.

Il existe une fonctionnalité de substitution de variables pour les fichiers. Les variables sont configurables par l'option -D en ligne de commande, comme expliqué ci-dessus, ou par les méta-commandes expliquées ci-dessous. En plus des variables pré-initialisées par les options -D, la variable scale est pré-initialisée avec le facteur d'échelle actuel. Une fois configurée, la valeur d'une variable peut être insérée dans une commande SQL en écrivant :nom_variable. Lors de l'exécution de plusieurs sessions clients, chaque session a son propre ensemble de variables.

Les méta-commandes du script commencent par un antislash (\). Les arguments d'une méta-commande sont séparés par des espaces blancs. Voici la liste des méta-commandes acceptées :

\set nom_variable operande1 [ operateur operande2 ]

Initialise la variable varname avec une valeur entière calculée. Chaque operande est soit une constante entière soit une référence :nom_variable à une variable entière. L'operateur peut être +, -, * ou /.

Exemple :

\set ntellers 10 * :scale
      
\setrandom nom_variable min max

Initialise la variable nom_variable à une valeur entière prise au hasard entre les limites min et max. Chaque limite peut être soit une constante entière soit une référence :nom_variable à une valeur entière.

Exemple :

\setrandom aid 1 :naccounts
      
\sleep nombre [ us | ms | s ]

Provoque un endormissement de l'exécution du script pour la durée indiquée en microsecondes (us), millisecondes (ms) ou secondes (s). Si l'unité est omise, alors ce seront par défaut des secondes. nombre peut être soit un entier soit une référence :nom_variable à un entier.

Exemple :

\sleep 10 ms
      

En exemple, voici la définition complète d'une transaction style TPC-B :

\set nbranches :scale
\set ntellers 10 * :scale
\set naccounts 100000 * :scale
\setrandom aid 1 :naccounts
\setrandom bid 1 :nbranches
\setrandom tid 1 :ntellers
\setrandom delta -5000 5000
BEGIN;
UPDATE accounts SET abalance = abalance + :delta WHERE aid = :aid;
SELECT abalance FROM accounts WHERE aid = :aid;
UPDATE tellers SET tbalance = tbalance + :delta WHERE tid = :tid;
UPDATE branches SET bbalance = bbalance + :delta WHERE bid = :bid;
INSERT INTO history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP);
END;
   

Ce script permet à chaque itération de la transaction de référencer des lignes différentes, prises au hasard. (Cet exemple montre aussi pourquoi il est imporant que chaque session client ait ses propres variables -- sinon elles ne traiteraient pas des lignes différentes.)

F.18.4. Journalisation par transaction

Avec l'option -l, pgbench écrit le temps pris par chaque transaction dans un journal applicatif. Le journal sera nommé pgbench_log.nnn, où nnn est le PID du processus pgbench. Le format de ce journal est :

    client_id transaction_no time file_no time_epoch time_us
   

time est le durée de la transaction en microsecondes, file_no identifie le script qui a été utilisé (utile quand plusieurs scripts sont indiqués avec -f) et time_epoch/time_us sont une date/heure au format epoch Unix et un décalage en microsecondes (convenable pour la création d'un horodatage ISO 8601 avec des secondes en fration) indiquant la date et heure de la fin de la transaction.

Voici un exemple de journal :

 0 199 2241 0 1175850568 995598
 0 200 2465 0 1175850568 998079
 0 201 2513 0 1175850569 608
 0 202 2038 0 1175850569 2663
   

F.18.5. Bonnes practiques

Il est très facile d'utiliser pgbench pour produire des nombres sans signification. Voici quelques lignes de conduite pour vous aider à obtenir des résultats intéressants.

En premier lieu, ne jamais croire tout test qui ne s'exécute que pendant quelques secondes. Augmentez suffisamment le paramètre -t pour que le test dure plusieurs minutes pour rendre le bruit insignifiant. Dans certains cas, nous avez besoin de quelques heures pour obtenir des chiffres reproductibles. Exécuter le test plusieurs fois est une bonne idée pour savoir si vos résultats sont reproductibles.

Pour le scénario par défaut, style TPC-B, le facteur d'échelle à l'initialisation (-s) doit être au moins aussi important que le plus grand nombre de clients que vous souhaitez supporter (-c) ; sinon vous ne ferez que mesurer la contention des mises à jour. Il n'y a que -s lignes dans la table branches, et chaque transaction veut en mettre une à jour, donc les valeurs -c supérieures à -s résulteront sans doute en beaucoup de transactions bloquées, en attente d'autres transactions.

Le scénario test par défaut est aussi assez sensible du moment où les tables ont été initialisées : une accumulation de lignes morts et d'espace vide dans les tables modifient les résultats. Pour comprendre les résultats, vous devez garder trace de nombre total de mises à jour et des moments où un VACUUM est exécuté. Si l'autovacuum est activé, cela peut causer des modifications non prévisibles dans les performances mesurées.

Une limite de pgbench est qu'il peut devenir lui-même le goulot d'étranglement lors de tentative de tests d'un grand nombre de sessions clients. Ceci peut se voir allégé en exécutant pgbench sur une autre machine que le serveur de bases de données, bien que la latence du réseau est essentielle. Il pourrait même être utile d'exécuter plusieurs instances pgbench en parallèle sur plusieurs machines client, pour le même serveur de bases de données.