pgbench — Réalise un test de benchmark pour PostgreSQL
pgbench
-i
[option
...] [nom_base
]
pgbench
[option
...] [nom_base
]
pgbench est un programme pour réaliser simplement des
tests de performance (benchmark)
sur PostgreSQL. Il
exécute la même séquence de commandes SQL en continu, potentiellement
avec plusieurs sessions concurrentes puis calcule le taux de
transactions moyen (en transactions par secondes).
Par défaut, pgbench teste un scénario
vaguement basé sur TPC-B, impliquant cinq commandes SELECT
,
UPDATE
et INSERT
par
transaction.
Toutefois, il est facile de tester d'autres scénarios en écrivant
vos propres scripts de transactions.
Une sortie classique de pgbench ressemble à ceci :
transaction type: <builtin: TPC-B (sort of)> scaling factor: 10 query mode: simple number of clients: 10 number of threads: 1 maximum number of tries: 1 number of transactions per client: 1000 number of transactions actually processed: 10000/10000 number of failed transactions: 0 (0.000%) latency average = 11.013 ms latency stddev = 7.351 ms initial connection time = 45.758 ms tps = 896.967014 (without initial connection time)
Les sept premières lignes rapportent certains des plus importants paramètres.
La sixième ligne affiche le nombre maximum d'essais pour les transactions
avec des erreurs de sérialisation ou des deadlocks (pour plus
d'informations, voir Failures and Serialization/Deadlock Retries).
La huitième ligne remonte les nombres de transactions réalisées et
prévues (ce dernier étant juste le produit du nombre de
clients et du nombre de transactions par client) ;
ils seront égaux à moins que l'exécution échoue avant la fin
ou que des commandes SQL échouent.
(Avec le mode -T
, seul le nombre réel de transactions est affiché.)
La ligne suivante affiche le nombre de transactions échouées
à cause d'erreurs de sérialisation ou de deadlock (pour plus
d'informations, voir Failures and Serialization/Deadlock Retries).
La dernière ligne indique le nombre de transactions par seconde.
Les transactions de ce test, proche de TPC-B, nécessitent d'avoir défini
au préalable quelques tables spécifiques.
Il faut utiliser l'option -i
(initialisation)
de pgbench pour créer et remplir ces tables.
(Si vous faites vos tests avec un script personnalisé,
vous n'aurez pas besoin de cette étape, mais devrez mettre en place
tout ce dont votre script aura besoin.)
Une initialisation ressemble à cela :
pgbench -i [autres-options
]nom_base
où nom_base
est le nom de la base de données
pré-existante sur laquelle on conduit les tests. (Vous aurez aussi
probablement besoin des options -h
,
-p
et/ou -U
pour spécifier comment
se connecter au serveur de base de données.)
pgbench -i
crée quatre tables nommées
pgbench_accounts
,
pgbench_branches
,
pgbench_history
et
pgbench_tellers
,
détruisant toute table qui porterait l'un de ces noms.
Attention à utiliser une autre base de données si vous avez
des tables qui portent ces noms !
Par défaut, avec un facteur d'échelle de 1, les tables contiennent initialement les nombres de lignes suivants :
table # de lignes --------------------------------- pgbench_branches 1 pgbench_tellers 10 pgbench_accounts 100000 pgbench_history 0
Vous pouvez (et, dans la plupart des cas, devriez) augmenter le nombre de
lignes en utilisant l'option -s
. Le facteur de remplissage
-F
peut aussi être utilisée à cet effet.
Une fois la mise en place terminée, vous pouvez
lancer vos benchmarks sans inclure l'option -i
,
c'est-à-dire :
pgbench [options
]nom_base
Dans presque tous les cas, vous allez avoir besoin de certaines
options pour rendre vos tests plus pertinents.
Les options les plus importantes sont :
-c
(le nombre de clients),
-t
(le nombre de transactions),
-T
(l'intervalle de temps)
et -f
(le script à lancer).
Vous trouverez ci-dessous toutes les options disponibles.
La partie suivante est divisée en trois sous-parties : des options différentes sont utilisées pendant l'initialisation et pendant les tests ; certaines options sont utiles dans les deux cas.
Pour réaliser l'initialisation, pgbench accepte les arguments suivants en ligne de commande :
[-d] nom_base
[--dbname=]nom_base
#
Indique le nom de la base à tester. Si elle n'est pas spécifier, la
variable d'environnement PGDATABASE
est utilisée. Si
elle n'est pas configurée, le nom d'utilisateur indiqué pour la
connexion est utilisé.
-i
--initialize
#Nécessaire pour passer en mode initialisation.
-I init_steps
--init-steps=init_steps
#
N'effectue qu'une partie des étapes d'initialisation habituelles.
init_steps
spécifie les étapes
d'initialisation à exécuter, à raison d'un caractère par étape.
Chaque étape est appelée dans l'ordre indiqué.
La valeur par défaut est dtgvp
. Voici la liste
des différentes étapes disponibles :
d
(Détruit) #Supprime toutes les tables pgbench déjà présentes.
t
(crée Tables) #
Crée les tables utilisées par le scénario
pgbench standard, à savoir
pgbench_accounts
,
pgbench_branches
,
pgbench_history
et
pgbench_tellers
.
g
ou G
(génère des données, côté client ou côté serveur) #Génère des données et les charge dans les tables standards, remplaçant toutes les données déjà présentes.
Avec g
(génération de données côté client),
les données sont générées dans le client pgbench
,
puis envoyées au serveur. Cela utilise une commande COPY
et consomme beaucoup de bande passante entre le client et le serveur.
En version 14 ou supérieure, pgbench
utilise l'option
FREEZE
de PostgreSQL pour
accélérer les VACUUM
suivantes,
sauf sur la table pgbench_accounts
si les partitions sont
activées.
Avec g
, la trace affiche un message toutes les
100 000 lignes lors de la génération de toutes les tables.
Avec G
(génération côté serveur), seules de petites
requêtes sont envoyées depuis le client pgbench
,
et les données sont ensuite générées sur le serveur.
Aucune bande passante significative n'est nécessaire dans cette variante,
mais le serveur travaillera plus.
Avec G
, la trace n'affichera aucun message de
progression pendant la génération des données.
Par défaut, l'initialisation utilise la génération des données
côté client (soit g
).
v
(Vacuum) #
Appelle VACUUM
sur les tables standards.
p
(clés Primaires) #Crée les clés primaires sur les tables standards.
f
(Foreign keys
) #Crée les contraintes de clés étrangères entre les différentes tables standards. (Notez que cette étape n'est pas exécutée par défaut.)
-F
fillfactor
--fillfactor=
fillfactor
#
Crée les tables pgbench_accounts
,
pgbench_tellers
et
pgbench_branches
avec le facteur de
remplissage (fillfactor) spécifié.
La valeur par défaut est 100.
-n
--no-vacuum
#
Ne réalise pas de VACUUM après l'initialisation.
(Cette option supprime l'étape d'initialisation v
,
même si elle était précisée dans -I
.)
-q
--quiet
#Passe du mode verbeux au mode silencieux, en n'affichant qu'un message toutes les 5 secondes. Par défaut, on affiche un message toutes les 100 000 lignes, ce qui engendre souvent plusieurs lignes toutes les secondes (particulièrement sur du bon matériel)
Ce paramètre n'a pas d'effet si G
est spécifié
dans l'option -I
.
-s
scale_factor
--scale=
scale_factor
#
Multiplie le nombre de lignes générées par le facteur d'échelle
(scale factor). Par exemple,
-s 100
va créer 10 millions de
lignes dans la table pgbench_accounts
. La
valeur par défaut est 1. Lorsque l'échelle dépasse 20 000, les
colonnes utilisées pour contenir les identifiants de compte (colonnes
aid
) vont être converties en grands entiers
(bigint
), de manière à être suffisamment grandes pour
contenir l'espace des identifiants de compte.
--foreign-keys
#
Crée des contraintes de type clé étrangère entre les tables standards.
(Cette option ajoute l'étape d'initialisation f
,
si elle n'est pas déjà présente.)
--index-tablespace=index_tablespace
#Crée un index dans le tablespace spécifié plutôt que dans le tablespace par défaut.
--partition-method=NOM
#
Crée une table partitionnée pgbench_accounts
par la méthode NOM
.
Les valeurs attendues sont range
ou hash
.
Cette option nécessite que --partitions
soit différente
de zéro. Sans précision, le défaut est range
.
--partitions=NUM
#
Crée une table partitionnée pgbench_accounts
avec NUM
partitions de taille à peu près
égale pour le nombre de clients indiqué par l'échelle.
Le défaut est 0
, ce qui signifie qu'il n'y a pas de partitionnement.
--tablespace=tablespace
#Crée une table dans le tablespace spécifié plutôt que dans le tablespace par défaut.
--unlogged-tables
#Crée toutes les tables en tant que tables non journalisées (unlogged tables) plutôt qu'en tant que tables permanentes.
Pour réaliser un benchmark pgbench accepte les arguments suivants en ligne de commande :
-b
nom_script[@poids]
--builtin
=nom_script[@poids]
#
Ajoute le script interne spécifié à la liste des scripts à exécuter.
Les scripts internes disponibles sont tpcb-like
,
simple-update
et select-only
.
L'utilisation des préfixes non ambigus des noms de scripts internes
est acceptée. En utilisant le nom spécial list
, la
commande affiche la liste des scripts internes, puis quitte
immédiatement.
En option, il est possible d'écrire un poids en entier après
@
pour ajuster la probabilité de sélectionner ce
script plutôt que les autres. Le poids par défaut est de 1. Voir
ci-dessous pour les détails.
-c
clients
--client=
clients
#Nombre de clients simulés, c'est-à-dire le nombre de sessions concurrentes sur la base de données. La valeur par défaut est à 1.
-C
--connect
#Établit une nouvelle connexion pour chaque transaction, plutôt que de ne le faire qu'une seule fois par session cliente. C'est une option très utile pour mesurer la surcharge engendrée par la connexion.
-D
variable
=
value
--define=
variable
=
value
#
Définit une variable à utiliser pour un script personnalisé
Voir ci-dessous pour plus de détails.
Il est possible d'utiliser plusieurs fois l'option -D
.
-f
nom_fichier[@poids]
--file=
nom_fichier[@poids]
#
Ajoute un script de transactions nommé
nom_fichier
à la liste des scripts à
exécuter.
En option, il est possible d'écrire un poids sous la forme d'un entier
après le symbole @
pour ajuster la probabilité de
sélectionner ce script plutôt qu'unautre. Le poids par défaut est de
1. (Pour utiliser un nom de fichier incluant un caractère
@
, ajoutez un poids pour qu'il n'y ait pas
d'ambiguité, par exemple filen@me@1
.) Voir
ci-dessous pour les détails.
-j
threads
--jobs=
threads
#Nombre de processus utilisés dans pgbench. Utiliser plus d'un thread peut être utile sur des machines possédant plusieurs cœurs. Les clients sont distribués de la manière la plus uniforme possible parmi les threads. La valeur par défaut est 1.
-l
--log
#Rapporte les informations sur chaque transaction dans un fichier journal. Voir ci-dessous pour plus de détails.
-L
limite
--latency-limit=
limite
#
Les transactions durant plus de limite
millisecondes
sont comptabilisée et rapportées séparément en tant que
late.
Lorsqu'un bridage est spécifié (--rate=...
),
les transactions accusant un retard sur la planification supérieur à
limite
millisecondes, et donc sans aucune
chance de respecter la limite de latence,
ne sont pas du tout envoyées au serveur.
Elles sont comptabilisées et rapportées séparément en tant que
skipped (ignorées).
Avec l'option --max-tries
, une transaction
échouée à cause d'une anomalie de sérialisation ou d'un deadlock
ne sera pas retentée si le temps cumulé de ses tentatives dépasse
limit
ms.
Pour ne limiter que la durée des tentatives et non leur nombre,
utiliser --max-tries=0
.
Par défaut, --max-tries
vaut 1, et les transactions
en erreur à cause de la sérialisation ou d'un deadlock
ne sont pas retentées.
Voir Failures and Serialization/Deadlock Retries pour plus de détails
sur comment retenter de telles transactions.
-M
querymode
--protocol=
querymode
#Protocole à utiliser pour soumettre des requêtes au serveur :
simple
: utilisation du protocole
de requêtes standards.
extended
: utilisation du protocole
de requête étendu.
prepared
: utilisation du protocole
de requête étendu avec instructions préparées.
Comme dans le mode prepared
,
pgbench réutilise le résultat de l'analyse
pour la deuxième itération et les suivantes,
pgbench s'exécute plus rapidement dans le
mode prepared
que dans les autres modes.
Par défaut, le protocole de requête standard est utilisé (voir Chapitre 53 pour plus d'informations).
-n
--no-vacuum
#
Ne réalise pas de VACUUM avant de lancer le test.
Cette option est nécessaire si vous lancez
un scénario de test personnalisé qui n'utilise pas les tables
standards pgbench_accounts
,
pgbench_branches
,
pgbench_history
et pgbench_tellers
.
-N
--skip-some-updates
#
Exécute le script interne simple-update. Raccourci pour
-b simple-update
.
-P
sec
--progress=
sec
#
Affiche un rapport de progression toutes les
sec
secondes.
Ce rapport inclut la durée du test, le nombre de
transactions par seconde depuis le dernier rapport et la
latence moyenne des transactions, ainsi que la déviation
et le nombre de transactions échouées
depuis le dernier rapport.
Avec le bridage (option -R
), la latence est calculée
en fonction de la date de démarrage ordonnancée de la transaction et non
de son temps de démarrage réel, donc elle inclut aussi la latence
moyenne du temps d'ordonnancement.
Si --max-tries
est utilisée pour autoriser de nouvelles
tentatives après des erreurs de sérialisation ou des deadlocks,
le rapport inclut le nombre de transactions retentées et la somme
de tous les nouveaux essais.
-r
--report-per-command
#
Affiche les statistiques suivantes pour chaque commande après la fin
du benchmark : latence moyenne par requête (temps
d'exécution du point de vue du client), nombre d'échecs après des
erreurs de sérialisation ou des deadlocks, et le nombre de nouvelles
tentatives après cela.
Le rapport affiche les statistiques sur les nouveaux essais uniquement
si --max-tries
n'est pas égal à 1.
-R
rate
--rate=
rate
#Exécute les transactions en visant le débit spécifié, au lieu d'aller le plus vite possible (le défaut). Le débit est donné en transactions par seconde. Si le débit visé est supérieur au maximum possible, la limite de débit n'aura aucune influence sur le résultat.
Pour atteindre ce débit, les transactions sont ordonnancées avec une distribution suivant une loi de Poisson. La date de démarrage prévue se calcule depuis le moment où le client a démarré et pas depuis le moment où la dernière transaction s'est achevée. Cette approche signifie que, si une transaction dépasse sa date de fin prévue, un rattrapage est encore possible pour les suivantes.
Lorsque le bridage est actif, la latence de la transaction rapportée en fin de test est calculée à partir des dates de démarrage ordonnancées, c'est-à-dire qu'elle inclut le temps où chaque transaction attend que la précédente se termine. Le temps d'attente est appelé temps de latence d'ordonnancement, et ses valeurs moyenne et maximum sont rapportées séparément. La latence de transaction par rapport au temps de démarrage réel, c'est-à-dire le temps d'exécution de la transaction dans la base, peut être récupérée en soustrayant le temps de latence d'ordonnancement à la latence précisée dans les journaux.
Si l'option --latency-limit
est utilisée avec
l'option --rate
, une transaction peut avoir
une telle latence qu'elle serait déjà supérieure à limite de
latence lorsque la transaction précédente se termine, car la
latence est calculée au moment de la date de démarrage planifiée.
Les transactions concernées ne sont pas envoyées à l'instance,
elles sont complètement ignorées et comptabilisées séparément.
Une latence de planification élevée est une indication que le système n'arrive pas à traiter les transactions à la vitesse demandée, avec les nombres de clients et threads indiqués. Lorsque le temps moyen d'exécution est plus important que l'intervalle prévu entre chaque transaction, les transactions vont prendre du retard une-à-une, et la latence de planification va continuer de croître tout le long de la durée du test. Si cela se produit, vous devrez réduire le taux de transaction que vous avez spécifié.
-s
scale_factor
--scale=
scale_factor
#
Affiche le facteur d'échelle dans la sortie de pgbench.
Avec les tests internes, ce n'est pas nécessaire ; le facteur
d'échelle approprié sera détecté en comptant le nombre de lignes
dans la table pgbench_branches
.
Toutefois, lors de l'utilisation d'un benchmark avec un scénario
personnalisé (option -f
), le facteur
d'échelle sera affiché à 1 à moins que cette option soit utilisée.
-S
--select-only
#
Exécute le script interne select-only. Raccourci pour
-b select-only
.
-t
transactions
--transactions=
transactions
#Nombre de transactions lancées par chaque client. La valeur par défaut est 10.
-T
seconds
--time=
seconds
#
Lance le test pour la durée spécifiée en secondes, plutôt que
pour un nombre fixe de transactions par client.
Les options -t
et -T
ne sont
pas compatibles.
-v
--vacuum-all
#
Réalise un VACUUM sur les quatre tables standards avant de
lancer le test. Sans l'option -n
ou
-v
, pgbench lancera un VACUUM
sur les tables pgbench_tellers
et
pgbench_branches
, puis tronquera
pgbench_history
.
--aggregate-interval=
secondes
#Taille de l'intervalle d'agrégation (en secondes). Ne peut être utilisée qu'avec l'option -l. Avec cette option, le journal contiendra des résumés par intervalle, comme décrit ci-dessous.
--exit-on-abort
#
Quitte imméditament quand un client abandonne à cause d'une erreur.
Sans cette option, quand un client est abandonné, les autres clients
continuent leur exécution comme indiqué par les options
-t
ou -T
, et
pgbench affichera un résultat incomplet
dans ce cas.
Notez que les échecs de sérialisation et de deadlock ne provoquent pas l'abandon du client, donc ils ne sont pas affectés par cette option. Voir Failures and Serialization/Deadlock Retries pour plus d'informations.
--failures-detailed
#Dans les traces, par transaction et agrégées, et les rapports manuels et par script, affiche les erreurs regroupées selon les types suivants :
erreurs de sérialisation ;
deadlocks ;
Voir Failures and Serialization/Deadlock Retries pour plus d'informations.
--log-prefix=prefix
#
Définit le préfixe des fichiers logs créés par --log
.
Le défaut est pgbench_log
.
--max-tries=number_of_tries
#
Autorise de nouvelles tentatives dans les transactions avec des erreurs
de sérialisation ou de verrous mortels, et définit le nombre maximum
de ces tentatives.
Cette option peut être combinée avec --latency-limit
,
qui limite le temps total de toutes les tentatives de transactions ;
cependant vous ne pouvez définir un nombre illimité de tentatives
(--max-tries=0
) sans définir
--latency-limit
ou --time
.
La valeur par défaut est 1, et les transactions avec des erreurs de
sérialisation ou des deadlocks ne sont donc pas retentées.
Voir Failures and Serialization/Deadlock Retries
pour plus de détails sur les nouvelles tentatives sur de telles
transactions.
--progress-timestamp
#
Lorsque la progression est affichée (option -P
),
utilise un horodatage de type timestamp (epoch Unix) au lieu d'un
nombre de secondes depuis le début de l'exécution. L'unité est la
seconde avec une précision en millisecondes après le point. Ceci
aide à comparer les traces générées par différents outils.
--random-seed=
graine
#
Fournit la graine du générateur de nombres aléatoires, qui produira
alors une séquence d'états initiaux du générateur, un pour chaque
thread.
Les valeurs pour graine
peuvent être
time
(par défaut, la graine est basée sur l'heure en
cours), rand
(utilise une source fortement aléatoire,
et tombe en échec si aucune n'est disponible), ou une valeur entière
non signée.
Le générateur aléatoire est appelé depuis un script
pgbench explicitement (fonctions random...
) ou
implicitement (par exemple l'option --rate
l'utilise
pour planifier les transactions).
Si elle est mise en place explicitement, la valeur utilisée comme
graine est affichée sur le terminal.
N'importe quelle valeur autorisée pour graine
peut aussi être fournie par la variable d'environnement
PGBENCH_RANDOM_SEED
.
Pour garantir que la graine fournie couvre tous les cas d'usage
possibles, mettez cette fonction en premier ou utilisez la variable
d'environnement.
Placer cette variable explicitement permet de reproduire un run
pgbench
exactement identique, du moins en ce qui
concerne les nombres aléatoires.
Comme l'état du générateur aléatoire est géré par thread,
pgbench
s'exécutera à l'identique s'il y a un client
par thread et pas de dépendance externe ou par rapport aux données.
D'un point de vue statistique, reproduire des runs est une mauvaise
idée, car cela peut masquer la variabilité des performances ou
améliorer les performances excessivement, par exemple en appelant les
mêmes pages qu'un run précédent.
Cependant, ce peut être d'une grande aide pour déboguer, par exemple
pour reproduire un cas tordu provoquant une erreur. À utiliser
judicieusement.
--sampling-rate=
rate
#Taux d'échantillonnage utilisé lors de l'écriture des données dans les journaux, afin d'en réduire la quantité. Si cette option est utilisée, n'y sera écrite que la proportion indiquée des transactions. 1.0 signifie que toutes les transactions seront journalisées, 0.05 signifie que 5 % de toutes les transactions le seront.
Pensez à prendre le taux d'échantillonnage en compte en consultant le journal. Par exemple, lorsque vous évaluez le nombre de transactions par seconde, vous devrez multiplier les nombres en conséquence. (Par exemple, avec un taux d'échantillonnage de 0,01, vous n'obtiendrez que 1/100è du débit réel).
--show-script
scriptname
#
Affiche le code réel du script intégré scriptname
sur stderr, et quitte immédiatement.
--verbose-errors
#Affiche des messages sur toutes les erreurs et échecs (les erreurs sans nouvelles tentatives), et mentionne quelle limite a été dépassée, et de combien, pour les échecs lors d'erreurs de sérialisation ou des deadlocks. (Notez que dans ce cas le débit peut être significativement amélioré.) Voir Failures and Serialization/Deadlock Retries pour plus d'informations.
pgbench accepte aussi les arguments suivants en ligne de commande pour les paramètres de connexion et les autres paramètres communs :
--debug
#Affiche les informations de debug.
-h
hostname
--host=
hostname
#Le nom du serveur de base de données
-p
port
--port=
port
#Le port d'écoute de l'instance sur le serveur de base de données
-U
login
--username=
login
#Le nom de l'utilisateur avec lequel on se connecte
-V
--version
#Affiche la version de pgbench puis quitte.
-?
--help
#Affiche l'aide sur les arguments en ligne de commande de pgbench puis quitte.
Une exécution réussie se terminera avec un code d'erreur 0.
Une valeur 1 indique des problèmes comme des options de ligne de commande
invalides ou des erreurs internes supposée ne jamais arriver.
Des erreurs dès le démarrage du benchmark, comme des échecs aux premières
tentatives de connexion, échouent aussi avec le statut 1.
Des erreurs pendant l'exécution, comme des erreurs de base de données ou des
problèmes dans le script, provoquent une erreur de code 2.
Dans ce dernier cas, pgbench affichera des
résultats partiels si --exit-on-abort
n'est pas indiqué.
PGDATABASE
PGHOST
PGPORT
PGUSER
#Paramètres de connexion par défaut.
Cet outil, comme la plupart des autres outils PostgreSQL, utilise les variables d'environnement supportées par la libpq (voir Section 32.15).
La variable d'environnement PG_COLOR
précise si l'on
doit utiliser la couleur dans les messages de diagnostique.
Les valeurs possibles sont
always
, auto
et
never
.
pgbench exécute des scripts de tests choisis de
façon aléatoire à partir d'une sélection. Les scripts peuvent inclure
des scripts internes, indiqués avec l'option -b
, et des
scripts fournis par l'utilisateur, indiqués avec l'option
-f
. Chaque script peut se voir affecter un poids
relatif, à préciser après un caractère @
, pour modifier sa
probabilité de sélection. Le poids par défaut est de 1
.
Les scripts avec un poids de 0
sont ignorés.
Le script interne par défaut (aussi appelé avec -b tpcb-like
)
exécute sept commandes par transaction choisies de
façon aléatoire parmi aid
, tid
,
bid
et delta
. Le scénario s'inspire
du benchmark TPC-B, mais il ne s'agit pas réellement de TPC-B, d'où son nom.
BEGIN;
UPDATE pgbench_accounts SET abalance = abalance + :delta
WHERE aid = :aid;
SELECT abalance FROM pgbench_accounts
WHERE aid = :aid;
UPDATE pgbench_tellers
SET tbalance = tbalance + :delta
WHERE tid = :tid;
UPDATE pgbench_branches
SET bbalance = bbalance + :delta
WHERE bid = :bid;
INSERT
INTO pgbench_history (tid, bid, aid, delta, mtime)
VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP);
END;
Si vous sélectionnez le script interne simple-update
(ou
-N
), les étapes 4 et 5 ne sont pas incluses dans la
transaction. Cela évitera des contentions au niveau des mises à jour sur
ces tables, mais le test ressemblera encore moins à TPC-B.
Si vous sélectionnez le script interne select-only
(ou
-S
), alors seul le SELECT
est exécuté.
pgbench est capable d'utiliser des
scénarios de test de performance personnalisés, en remplaçant le
script de transactions par défaut (décrit ci-dessus) par un script de
transactions lu depuis un fichier spécifié avec l'option
(-f
).
Dans ce cas, une « transaction » est comptabilisée comme
une exécution du fichier script.
Un fichier script contient une ou plusieurs commandes SQL terminées par des
points-virgules. Les lignes vides et les lignes commençant par
--
sont ignorées. Les fichiers scripts peuvent aussi
contenir des « méta-commandes », qui seront interprétées par
pgbench comme indiqué plus bas.
Avant PostgreSQL 9.6, les commandes SQL comprises dans les fichiers scripts étaient terminées par un retour à la ligne. Elles ne pouvaient donc pas être écrites sur plusieurs lignes. Maintenant, un point-virgule est requis pour séparer des commandes SQL consécutives (bien qu'une commande SQL n'en a pas besoin si elle est suivie par une méta-commande). Si vous avez besoin de créer un fichier script qui fonctionne avec les anciennes et nouvelles versions de pgbench, assurez-vous d'écrire chaque commande SQL sur une seule ligne et en terminant avec un point-virgule.
On part du principe que les scripts pgbench ne contiennent pas de blocs incomplets de transactions SQL. Si à l'exécution le client atteint la fin du script sans terminer le dernier bloc de transaction, il sera arrêté.
Il est possible de procéder facilement à de la substitution de variables
dans les fichiers scripts.
Les noms de variables doivent consister en lettres (y compris des caractères
non latins), chiffres et soulignés (_
), mais le premier
caractère ne doit pas être un chiffre.
Les variables peuvent être instanciées via
l'option -D
de la ligne de commande comme décrit ci-dessus,
ou grâce aux méta-commandes décrites ci-dessous.
En plus des commandes pré-définies par l'option de la ligne de
commande -D
, quelques variables sont
automatiquement prédéfinies, listées sous
Tableau 298.
Une valeur de ces variables définie via l'option -D
aura priorité sur la valeur définie
automatiquement. Une fois définie, la valeur d'une variable peut
être insérée dans les commandes SQL en écrivant :
nom_variable
.
S'il y a plus d'une session par client, chaque session possède son
propre jeu de variables.
pgbench supports up to 255 variable uses in one
statement.
Tableau 298. Variables automatiques de pgbench
Variable | Description |
---|---|
client_id | nombre unique permettant d'identifier la session client (commence à zéro) |
default_seed | graine utilisée par défaut dans les fonctions de hachage et de permutation pseudo-aléatoire |
random_seed | graine du générateur aléatoire (si pas remplacée avec
-D ) |
scale | facteur d'échelle courant |
Dans les fichiers de scripts, les méta-commandes commencent avec un
anti-slash (\
) et s'étendent jusqu'à la fin de la ligne,
même si elles peuvent s'étendre sur plusieurs lignes en écrivant anti-slash
puis un retour chariot. Les
arguments d'une méta-commande sont séparés par des espaces vides. Les
méta-commandes suivantes sont supportées :
\gset [prefix
]
\aset [prefix
]
#
Cette commande peut être utilisée pour marquer la fin de requêtes SQL,
prenant ainsi la place du point-virgule final (;
).
Quand la commande \gset
est utilisée,
la requête SQL précédente doit renvoyer une ligne.
Les valeurs de ses colonnes sont enregistrées dans
des variables nommées d'après les noms de colonnes, préfixées avec
prefix
, si ce dernier est fourni.
Quand la commande \aset
est utilisée, toutes les requêtes
(séparées par \;
) voient leurs colonnes stockées dans
des variables nommées d'après elles, préfixées de prefix
s'il est fourni.
Si une requête ne retourne aucune ligne, aucune affectation n'est faite.
On peut tester l'existence de la variable pour détecter ce cas.
Si une requête retourne plus d'une ligne, la dernière valeur est conservée.
\gset
et \aset
ne peuvent être
utilisées en mode pipeline, puisque les résultats des requêtes ne sont pas
encore disponibles au moment où la commande en a besoin.
L'exemple suivant place la balance finale du compte provenant de la
première requête dans la variable abalance
,
et remplit les variables p_two
et
p_three
avec les entiers provenant de la
troisième requête. Le résultat de la deuxième requête est ignoré.
Les résultats des deux dernières requêtes combinées sont stockés dans
les variables
four
et five
.
UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid RETURNING abalance \gset -- compound of two queries SELECT 1 \; SELECT 2 AS two, 3 AS three \gset p_ SELECT 4 AS four \; SELECT 5 AS five \aset
\if
expression
\elif
expression
\else
\endif
#
Ce groupe de commandes implémente des blocs conditionnels imbriquables,
de manière similaire au \if
expression
de
psql
.
Les expressions conditionnelles sont identiques à celles avec
\set
, les valeurs autres que zéro valant
true
.
\set nom_variable
expression
#
Définit la variable nom_variable
à une valeur
définie par expression
.
L'expression peut contenir la constante NULL
,
les constantes booléennes TRUE
et
FALSE
, des constantes entières comme
5432
, des constantes double précision comme
3.14159
, des références à des variables
:
nomvariable
,
des opérateurs avec leur
priorité et leur associativité habituelles en SQL,
des appels de fonction,
des
expressions conditionnelles génériques SQL
avec CASE
et des parenthèses.
Les fonctions et la plupart des opérateurs retournent
NULL
en cas d'entrée à NULL
.
En ce qui concerne les conditions, les valeurs numériques différentes de
zéro valent TRUE
, les valeurs numériques à zéro et
NULL
sont FALSE
.
Des constantes entières ou à virgule flottante ainsi que des opérateurs
arithmétiques entiers (+
, -
,
*
et /
), trop larges ou trop
petites, renvoient des erreurs de dépassement.
Quand aucune clause finale ELSE
n'est fournie à un
CASE
, la valeur par défaut est NULL
.
Exemples :
\set ntellers 10 * :scale \set aid (1021 * random(1, 100000 * :scale)) % \ (100000 * :scale) + 1 \set divx CASE WHEN :x <> 0 THEN :y/:x ELSE NULL END
\sleep
nombre
[ us | ms | s ]
#
Entraîne la suspension de l'exécution du script pendant la durée
spécifiée en microsecondes (us
), millisecondes
(ms
) ou secondes (s
).
Si l'unité n'est pas définie, l'unité par défaut est la seconde.
Ce peut être soit un entier constant, soit une référence
:
nom_variable
vers
une variable retournant un entier.
Exemple :
\sleep 10 ms
\setshell nom_variable
commande
[
argument
... ]
#
Définit la variable nom_variable
comme le
résultat d'une commande shell nommée commande
aves le(s) argument
(s) donné(s). La commande
doit retourner un entier sur la sortie standard.
commande
et chaque
argument
peuvent être soit une constante de
type text, soit une référence
:
nom_variable
à une variable.
Si vous voulez utiliser un argument
commençant
avec un symbole deux-points, écrivez un deux-points supplémentaire au
début de l'argument
.
Exemple :
\setshell variable_à_utiliser commande argument_litéral :variable ::literal_commencant_avec_deux_points
\shell commande
[ argument
... ]
#
Identique à \setshell
, mais le résultat de la
commande sera ignoré.
Exemple :
\shell command literal_argument :variable ::literal_starting_with_colon
\startpipeline
\syncpipeline
\endpipeline
#
Ce groupe de commandes implémente un pipeline de requêtes SQL.
Un pipeline doit commencer avec \startpipeline
et terminer avec \endpipeline
. Entre les deux,
il peut y avoir plusieurs commandes \syncpipeline
qui envoie un message de
synchronisation sans terminer le pipeline en cours et sans
vide le buffer d'envoi. Dans le mode pipeline, les requêtes sont
envoyées au serveur sans attendre le résultat des requêtes
précédentes.
Voir Section 32.5 pour plus de détails.
Le mode pipeline impose l'utilisation du protocole de requête étendu.
Les opérateurs arithmétiques, de manipulation de bits, de comparaison et
logiques listés dans Tableau 299 sont intégrés
dans pgbench et peuvent être utilisés dans des
expressions apparaissant dans
\set
.
Les opérateurs sont listés par priorité croissante.
Sauf indication contraire, les opérateurs prenant deux paramètres
en entrée produiront un nombre en double précision, si une des entrées
est en double précision, sinon le résultat produit sera entier.
Tableau 299. Opérateurs pgbench
Opérateur Description Exemple(s) |
---|
OU logique
|
ET logique
|
NON logique
|
Tests de valeur booléenne
|
Tests de nullité
|
Est égal
|
N'est pas égal
|
N'est pas égal
|
Inférieur à
|
Inférieur ou égal à
|
Plus grand que
|
Plus grand ou égal à
|
OU binaire
|
XOR binaire
|
ET binaire
|
NON binaire
|
Décalage binaire vers la gauche
|
Décalage binaire vers la droite
|
Addition
|
Soustraction
|
Multiplication
|
Division (tronque le résultat vers zéro si les deux paramètres d'entrée sont des entiers)
|
Modulo (reste)
|
Opposé
|
Les fonctions listées dans Tableau 300 sont
internes à pgbench et peuvent être
utilisées dans des expressions apparaissant dans
\set
.
Tableau 300. pgbench Functions
Fonction Description Exemple(s) |
---|
Valeur absolue
|
Affiche l'argument dans stderr, puis le retourne.
|
Convertit en double précision.
|
Exponentielle (
|
Sélectionne la plus grande valeur parmi les arguments.
|
Alias pour
|
Calcule le hash FNV-1a.
|
Computes MurmurHash2 hash.
|
Convertit en entier.
|
Choisit la plus petite valeur parmi les arguments.
|
Logarithme naturel
|
Modulo (reste)
|
Valeur permutée de
|
Valeur approximative de pi
|
|
Calcule un entier aléatoire uniformément distribué dans
|
Calcule un entier aléatoire distribué exponentiellement dans
|
Calcule un entier aléatoire distribué de manière gaussienne dans
|
Calcule un entier aléatoire distribué selon la loi de Zipf dans
|
Racine carrée
|
La fonction random
génère des valeurs en utilisant une
distribution uniforme ; autrement dit toutes les valeurs sont dans
l'intervalle spécifiée avec une probabilité identique. Les fonctions
random_exponential
, random_gaussian
et random_zipfian
requièrent un paramètre
supplémentaire de type double qui détermine le contour précis de cette
distribution.
Pour une distribution exponentielle,
paramètre
contrôle la distribution en
tronquant une distribution exponentielle en décroissance rapide à
paramètre
, puis en projetant le résultant sur
des entiers entre les limites. Pour être précis :
f(x) = exp(-paramètre * (x - min) / (max - min + 1)) / (1 - exp(-paramètre))
Puis la valeur i
entre les valeurs
min
et max
, en les
incluant, est récupérée avec la probabilité :
f(i) - f(i + 1)
.
Intuitivement, plus paramètre
est grand, plus
les valeurs fréquentes proches de min
sont
accédées et moins les valeurs fréquentes proches de
max
sont accédées. Plus
paramètre
est proche de 0, plus la
distribution d'accès sera plate (uniforme). Une approximation grossière de
la distribution est que 1 % des valeurs les plus fréquentes de
l'intervalle, proches de min
, sont tirées
paramètre
% du temps. La valeur de
paramètre
doit être strictement positive.
Pour une distribution gaussienne, l'intervalle correspond à une
distribution normale standard (la courbe gaussienne classique en forme
de cloche) tronquée à -paramètre
à gauche et à
+paramètre
à droite.
Les valeurs au milieu de l'intervalle sont plus
susceptibles d'être sélectionnées. Pour être précis, si
PHI(x)
est la fonction de distribution cumulative de
la distribution normale standard, avec une moyenne mu
définie comme (max + min) / 2.0
, avec
f(x) = PHI(2.0 * paramètre * (x - mu) / (max - min + 1)) /
(2.0 * PHI(paramètre) - 1)
alors la valeur i
entre
min
et max
(inclus) est sélectionnée avec une probabilité :
f(i + 0.5) - f(i - 0.5)
. Intuitivement, plus
paramètre
est grand, et plus les valeurs
fréquentes proches du centre de l'intervalle sont sélectionnées, et
moins les valeurs fréquentes proches des bornes
min
et max
.
Environ 67 % des valeurs sont sélectionnées à partir du centre
1.0 / paramètre
, soit
0.5 / paramètre
autour de la moyenne, et 95 % dans le
centre 2.0 / paramètre
, soit
1.0 / paramètre
autour de la moyenne ; par
exemple, si paramètre
vaut 4.0, 67 % des
valeurs sont sélectionnées depuis le quart du milieu (1.0 / 4.0) de
l'intervalle (ou à partir de 3.0 / 8.0
jusqu'à
5.0 / 8.0
) et 95 % depuis la moitié du milieu
(2.0 / 4.0
) de l'intervalle (deuxième et troisième
quartiles). La valeur minimale autorisée pour paramètre
est 2.0.
random_zipfian
génère une distribution bornée
selon la loi de Zipf.
paramètre
définit à quel point la distribution est biaisée. Plus
paramètre
est grand, plus fréquemment les
valeurs du début de l'intervalle seront tirées.
La distribution est telle que, en supposant que l'intervalle commence à
1, le ratio de probabilité d'un jet k
contre
un jet k+1
est
((
.
Par exemple, k
+1)/k
)**parameter
random_zipfian(1, ..., 2.5)
produit
la valeur 1
à peu près (2/1)**2.5 =
5.66
fois plus fréquemment que 2
, qui
lui-même est produit (3/2)**2.5 = 2.76
fois plus
fréquemment que 3
, et ainsi de suite.
L'implémentation de pgbench est basée sur
« Non-Uniform Random Variate Generation », Luc Devroye, p. 550-551,
Springer 1986. À cause des limitations de cet algorithme, la valeur
paramètre
est restreinte à l'intervalle
[1.001, 1000].
Lors de la conception d'un benchmark qui sélectionne des lignes de manière non uniforme, soyez conscient que les lignes choisies peuvent être corrélées avec d'autres données, comme les ID d'une séquence ou l'ordre physique des lignes, ce qui peut biaiser les mesures de performance.
Pour éviter cela, pensez à la fonction permute
,
ou toute autre opération avec le même effet, pour mélanger les lignes
sélectionnées et détruire ces corrélations.
Les fonctions de hachage hash
,
hash_murmur2
et hash_fnv1a
acceptent
une valeur d'entrée et une graine optionnelle. Si la graine n'est pas
fournie, la valeur de :default_seed
est utilisée,
initialisée de façon aléatoire si elle n'est pas définie par l'option de
ligne de commande -D
..
permute
accepte en entrée une valeur, une taille,
et une graine optionnelle. Elle génère une permutation
pseudo-aléatoire des entiers dans la tranche [0, taille)
,
et retourne l'index de la valeur d'entrée dans les valeurs permutées.
La permutation choisie est paramétrée par la graine, soit par défaut
:default_seed
si elle n'est pas fournie.
Au contraire des fonctions de hachage, permute
garantit qu'il n'y aura ni collision ni trou dans les valeurs retournées.
Les valeurs d'entrée hors de l'intervalle sont interprétées modulo
la taille. La fonction lève une erreur si la taille n'est pas positive.
permute
peut être utilisée pour disperser
la distribution de fonctions aléatoires non uniformes comme
random_zipfian
ou random_exponential
,
afin que les valeurs les plus couramment tirées ne soient pas corrélées
de manière triviale. Par exemple, le script pgbench
suivant simule une charge réaliste typique des médias sociaux et
des plateformes de blogs, où quelques comptes génèrent une charge
excessive :
\set size 1000000 \set r random_zipfian(1, :size, 1.07) \set k 1 + permute(:r, :size)
Dans certains cas, plusieurs distributions distinctes non corrélées entre elles sont nécessaires, et c'est là que le paramètre graine optionnel est pratique :
\set k1 1 + permute(:r, :size, :default_seed + 123) \set k2 1 + permute(:r, :size, :default_seed + 321)
Un comportement similaire peut être approché avec hash
:
\set size 1000000 \set r random_zipfian(1, 100 * :size, 1.07) \set k 1 + abs(hash(:r)) % :size
Cependant, comme hash
génère des collisions,
certaines valeurs ne sont pas atteignables, et d'autres seront plus fréquentes
qu'attendues par rapport à la distribution originale.
À titre d'exemple, la définition complète de la transaction style TPC-B intégrée est :
\set aid random(1, 100000 * :scale) \set bid random(1, 1 * :scale) \set tid random(1, 10 * :scale) \set delta random(-5000, 5000) BEGIN; UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid; SELECT abalance FROM pgbench_accounts WHERE aid = :aid; UPDATE pgbench_tellers SET tbalance = tbalance + :delta WHERE tid = :tid; UPDATE pgbench_branches SET bbalance = bbalance + :delta WHERE bid = :bid; INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP); END;
Ce script autorise chaque itération de la transaction à référencer des lignes différentes, sélectionnées aléatoirement. (Cet exemple montre aussi pourquoi il est important que chaque session cliente ait ses propres variables -- sinon elles n'affecteront pas les différentes lignes de façon indépendante.
Avec l'option -l
(mais sans l'option
--aggregate-interval
),
pgbench va écrire des informations sur
chaque transaction dans un fichier journal. Il sera nommé
,
où prefix
.nnn
prefix
vaut par défaut
pgbench_log
, et nnn
est le PID
du processus pgbench.
Le préfixe peut être changé avec l'option --log-prefix
.
Si l'option -j
est positionnée à 2 ou plus,
créant plusieurs processus de travail (worker),
chacun aura son propre fichier journal.
Le premier worker utilisera le même nom pour son fichier journal
que dans le cas d'un seul processus.
Les fichiers journaux supplémentaires s'appelleront
,
où prefix
.nnn
.mmm
mmm
est un numéro de séquence,
identifiant chaque worker, commençant à 1.
Dans un fichier journal, chaque ligne décrit une transaction. Elle contient les champs suivants, séparés par des espaces :
client_id
identifie la session client qui a exécuté la transaction
transaction_no
décompte les transactions exécutées par cette session
time
temps déroulé pour la transaction, en microsecondes
script_no
identifie le fichier script utilisé par la transaction
(utile quand plusieurs scripts ont été fournis
avec -f
ou -b
)
time_epoch
horodatage de la fin de la transaction (époque Unix)
time_us
fraction de seconde de la durée de la transaction, en microsecondes
schedule_lag
délai au démarrage de de la transaction, c'est-à-dire la différence
entre le moment prévu du démarrage et le moment où elle a
réellement démarré, en microsecondes
(n'est présent que si --rate
est précisé)
retries
nombre des nouvelles tentatives après des erreurs de sérialisation
et des deadlocks pendant la transaction
(n'est présent que si --max-tries
n'est pas à 1)
Quand les options --rate
et --latency-limit
sont utilisées en même temps, le champ time
pour
une transaction ignorée sera rapporté en tant que skipped
.
Si la transaction se termine par une erreur, son
time
sera rapporté comme
failed
.
Avec l'option --failures-detailed
, le
time
de la transaction échouée
sera attribué à la serialization
ou à un
deadlock
en fonction du type d'erreur (voir
Failures and Serialization/Deadlock Retries pour plus d'information).
Ci-dessous un extrait du fichier journal généré avec un seul client :
0 199 2241 0 1175850568 995598 0 200 2465 0 1175850568 998079 0 201 2513 0 1175850569 608 0 202 2038 0 1175850569 2663
Autre exemple avec les options --rate=100
et
--latency-limit=5
(notez
la colonne supplémentaire schedule_lag
) :
0 81 4621 0 1412881037 912698 3005 0 82 6173 0 1412881037 914578 4304 0 83 skipped 0 1412881037 914578 5217 0 83 skipped 0 1412881037 914578 5099 0 83 4722 0 1412881037 916203 3108 0 84 4142 0 1412881037 918023 2333 0 85 2465 0 1412881037 919759 740
Dans cet exemple, la transaction 82 a été en retard, elle affiche une latence (6,173 ms) supérieure à la limite de 5 ms. Les deux transactions suivantes ont été ignorées, car elles avaient déjà en retard avant même d'avoir commencé.
L'exemple suivant montre un extrait d'un journal avec des erreurs
et de nouvelles tentatives, avec un nombre maximum d'essais à 10
(notez la colonne supplémentaire retries
:
3 0 47423 0 1499414498 34501 3 3 1 8333 0 1499414498 42848 0 3 2 8358 0 1499414498 51219 0 4 0 72345 0 1499414498 59433 6 1 3 41718 0 1499414498 67879 4 1 4 8416 0 1499414498 76311 0 3 3 33235 0 1499414498 84469 3 0 0 failed 0 1499414498 84905 9 2 0 failed 0 1499414498 86248 9 3 4 8307 0 1499414498 92788 0
Avec l'option --failures-detailed
, le type d'erreur
est rapporté dans le time
ainsi :
3 0 47423 0 1499414498 34501 3 3 1 8333 0 1499414498 42848 0 3 2 8358 0 1499414498 51219 0 4 0 72345 0 1499414498 59433 6 1 3 41718 0 1499414498 67879 4 1 4 8416 0 1499414498 76311 0 3 3 33235 0 1499414498 84469 3 0 0 serialization 0 1499414498 84905 9 2 0 serialization 0 1499414498 86248 9 3 4 8307 0 1499414498 92788 0
Dans le cas d'un test long sur du matériel qui peut
supporter un grand nombre de transactions, les journaux
peuvent devenir très volumineux.
L'option --sampling-rate
peut être utilisée pour
journaliser seulement un extrait aléatoire des transactions
effectuées.
Avec l'option --aggregate-interval
, les fichiers
journaux utilisent un format quelque peu différent.
Chaque ligne décrit un intervalle d'agrégation.
Elle contient les champs suivants séparés par des espaces :
interval_start
horodatage du démarrage de l'intervalle (époque Unix)
num_transactions
nombre de transactions au sein de l'intervalle
sum_latency
somme des latences de transaction
sum_latency_2
somme des carrés des latences de transaction
min_latency
minimum des latences de transaction
max_latency
maximum des latences de transaction
sum_lag
somme des délais de démarrage des transactions
(zéro, à moins que --rate
soit renseigné)
sum_lag_2
somme des carrés des délais de démarrage des transactions
(zéro, à moins que --rate
soit renseigné)
min_lag
minimum des délais de démarrage des transactions
(zéro, à moins que --rate
soit renseigné)
max_lag
maximum des délais de démarrage des transactions
(zéro, à moins que --rate
soit renseigné)
skipped
nombre de transactions passées car elles auraient démarré trop tard
(zéro, à moins que --rate
et
--latency-limit
soient renseignés)
retried
nombre de transactions retentées
(zéro, à moins que --max-tries
soit différent de 1)
retries
nombre de transactions retentées après des erreurs de
sérialisation ou des deadlocks
(zéro, à moins que --max-tries
soit différent de 1)
serialization_failures
nombre de transactions ayant obtenu une erreur de sérialisation
et non retentées ensuite
(zéro, à moins que --failures-detailed
soit précisé)
deadlock_failures
nombre de transactions ayant obtenu une erreur à cause d'un deadlock
et non retentées ensuite
(zéro, à moins que --failures-detailed
soit précisé)
Voici un exemple de sortie générée avec cette option :
pgbench --aggregate-interval=10 --time=20 --client=10 --log --rate=1000 --latency-limit=10 --failures-detailed --max-tries=10 test
1650260552 5178 26171317 177284491527 1136 44462 2647617 7321113867 0 9866 64 7564 28340 4148 0
1650260562 4808 25573984 220121792172 1171 62083 3037380 9666800914 0 9998 598 7392 26621 4527 0
Notez que si le journal proprement dit (non agrégé) montre le script utilisé pour chaque transaction, le format agrégé ne l'affiche pas. De ce fait, si vous avez besoin des valeurs pour chaque script, vous devrez agréger ces données vous-même.
Avec l'option -r
, pgbench
collecte les statistiques suivantes pour chaque requête :
latency
-- partie du temps de transaction
pour chaque requête. pgbench renvoie une
valeur moyenne de toutes les exécutions de l'ordre.
Le nombre d'échecs de la requête. Voir Failures and Serialization/Deadlock Retries pour plus d'informations.
Le nombre de nouvelles tentatives après une erreur de sérialisation ou un deadlock dans cette requête. Voir Failures and Serialization/Deadlock Retries pour plus d'informations.
Le rapport n'affiche les statistiques des nouvelles tentatives
que si l'option --max-tries
ne vaut pas 1.
Toutes les valeurs sont calculées pour chaque requête exécutée par chaque client et sont affichées après la fin du benchmark.
Pour le script par défaut, le résultat aura la forme suivante :
starting vacuum...end. transaction type: <builtin: TPC-B (sort of)> scaling factor: 1 scaling factor: 1 query mode: simple number of clients: 10 number of threads: 1 maximum number of tries: 1 number of transactions per client: 1000 number of transactions actually processed: 10000/10000 number of failed transactions: 0 (0.000%) number of transactions above the 50.0 ms latency limit: 1311/10000 (13.110 %) latency average = 28.488 ms latency stddev = 21.009 ms initial connection time = 69.068 ms tps = 346.224794 (without initial connection time) statement latencies in milliseconds and failures: 0.012 0 \set aid random(1, 100000 * :scale) 0.002 0 \set bid random(1, 1 * :scale) 0.002 0 \set tid random(1, 10 * :scale) 0.002 0 \set delta random(-5000, 5000) 0.319 0 BEGIN; 0.834 0 UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid; 0.641 0 SELECT abalance FROM pgbench_accounts WHERE aid = :aid; 11.126 0 UPDATE pgbench_tellers SET tbalance = tbalance + :delta WHERE tid = :tid; 12.961 0 UPDATE pgbench_branches SET bbalance = bbalance + :delta WHERE bid = :bid; 0.634 0 INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP); 1.957 0 END;
Autre exemple de sortie pour le script par défaut, en utilisant
le mode de transaction sérialisable (PGOPTIONS='-c
default_transaction_isolation=serializable' pgbench ...
) :
starting vacuum...end. transaction type: <builtin: TPC-B (sort of)> scaling factor: 1 query mode: simple number of clients: 10 number of threads: 1 maximum number of tries: 10 number of transactions per client: 1000 number of transactions actually processed: 6317/10000 number of failed transactions: 3683 (36.830%) number of transactions retried: 7667 (76.670%) total number of retries: 45339 number of transactions above the 50.0 ms latency limit: 106/6317 (1.678 %) latency average = 17.016 ms latency stddev = 13.283 ms initial connection time = 45.017 ms tps = 186.792667 (without initial connection time) statement latencies in milliseconds, failures and retries: 0.006 0 0 \set aid random(1, 100000 * :scale) 0.001 0 0 \set bid random(1, 1 * :scale) 0.001 0 0 \set tid random(1, 10 * :scale) 0.001 0 0 \set delta random(-5000, 5000) 0.385 0 0 BEGIN; 0.773 0 1 UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid; 0.624 0 0 SELECT abalance FROM pgbench_accounts WHERE aid = :aid; 1.098 320 3762 UPDATE pgbench_tellers SET tbalance = tbalance + :delta WHERE tid = :tid; 0.582 3363 41576 UPDATE pgbench_branches SET bbalance = bbalance + :delta WHERE bid = :bid; 0.465 0 0 INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP); 1.933 0 0 END;
Toutes les statistiques sont rapportées séparément si plusieurs scripts sont spécifiés.
Notez que la collecte des informations de chronométrage supplémentaires nécessaires pour calculer la latence par requête ajoute une certaine charge. Cela va réduire la vitesse moyenne calculée pour l'exécution des transactions et réduire le débit de transactions calculé. Le ralentissement varie de manière significative selon la plateforme et le matériel. Comparer la moyenne des valeurs de TPS avec et sans intégration de la latence est une bonne manière de voir si la surcharge induite par le chronométrage est importante ou pas.
À l'exécution de pgbench, il y a trois principaux types d'erreurs, qui comprennent :
Les erreurs du programme principal. Ce sont les plus sérieuses, et elles entraînent toujours une sortie immédiate de pgbench avec le message d'erreur correspondant. Elles incluent :
les erreurs au lancement de pgbench (par exemple une valeur d'option invalide) ;
les erreurs dans la phase d'initialisation (par exemple quand échoue la requête de création des tables pour les scripts inclus)
les erreurs avant le démarrage des threads (par exemple l'impossibilité à se connecter au serveur de bases de données, une erreur de syntaxe dans la métacommande, une erreur à la création du thread) ;
les erreurs internes de pgbench (supposées ne jamais se produire...).
Les erreurs quand le thread gère ses clients (par exemple
si le client ne peut démarrer une connexion au serveur /
si la socket pour connecter le client au serveur est devenu invalide).
Dans ces cas, tous les clients du thread s'arrêtent pendant que
les autres continuent de fonctionner.
Néanmoins, si --exit-on-abort
est indiqué, tous les
threads sont immédiatement stoppés.
Les erreurs directes du client. Elles mènent à un arrêt immédiat de
pgbench, avec le message d'erreur
correspondant, dans le cas d'une erreur interne de
pgbench (supposées ne jamais se produire...) ou quand
--exit-on-abort
est indiqué.
Sinon, dans le pire des cas, ces erreurs n'entraînent que l'arrêt
du client en échec, pendant que les autres continuent leur travail
(mais certaines erreurs des clients sont gérées sans arrêter
le client et sont rapportées séparément, voir plus bas).
Dans la suite de cette section, on suppose que les erreurs
évoquées ne sont que des erreurs du client et non des
erreurs internes de pgbench.
L'exécution d'un client n'est interrompue que dans le cas d'une erreur
sérieuse : par exemple, la connexion au serveur a été perdue,
ou la fin du script a été atteinte sans avoir terminé la dernière
transaction. De plus, si l'exécution d'une commande SQL ou d'une
métacommande échoue, le client est arrêté, sauf pour une erreur de
sérialisation ou un deadlock.
Dans ce dernier cas, la transaction courante est annulée, ce qui comprend
la restauration des variables à leurs valeurs d'avant l'exécution de la
transaction (on suppose qu'un script de transaction ne contient qu'une
transaction ; voir
What Is the "Transaction" Actually Performed in pgbench? pour plus d'informations).
Les transactions avec des erreurs de sérialisation ou des deadlocks
sont répétées après le rollback, jusqu'à ce qu'elles se terminent avec
succès, ou qu'elles atteignent le nombre maximum de tentatives
(indiqué avec --max-tries
) / le délai maximal
pour les tentatives (indiqué avec --latency-limit
)
/ la fin du benchmark (indiqué avec --time
).
Si la dernière tentative d'exécution échoue, la transaction sera
comptabilisée comme échouée, mais le client n'est pas arrếté et
continue de fonctionner.
Sans définition de --max-tries
, une transaction ne
sera jamais retentée après une erreur de sérialisation ou un
deadlock, car la valeur par défaut de l'option est 1.
Pour limiter uniquement la durée totale des tentatives,
utilisez l'option --latency-limit
en indiquant un nombre illimité de tentatives
(--max-tries=0
).
Vous pouvez aussi utiliser --time
pour limiter la
durée du benchmark alors que le nombre de tentatives est illimité.
Faites attention lors de la répétition de scripts avec de multiples transactions : le script est toujours intégralement réexécuté, et les transactions réussies peuvent donc être exécutées plusieurs fois.
Faites aussi attention lors de la répétition de scripts avec des
commandes shell. Leur résultat n'est pas annulé, contrairement
à ce qui passe avec des commandes SQL, à part pour une valeur assignée
avec la commande \setshell
.
La latence d'une transaction réussie inclut la durée entière de l'exécution, y compris les rollbacks et les diverses tentatives. La latence n'est mesurée que pour les transactions et commandes réussies, mais pas pour celles en échec.
Le rapport principal contient le nombre de transactions échouées. Si
--max-tries
n'est pas égal à 1, il contiendra aussi
des statistiques sur les tentatives : le nombre total de
transactions tentées plusieurs fois, et le nombre total de
nouvelles tentatives.
Le rapport par script hérite de tous ces champs. Le rapport par
requête n'affiche les statistiques sur les tentatives que si
--max-tries
n'est pas égal à 1.
Si vous voulez regrouper les échecs par types dans les journaux
par transaction et les journaux agrégés, utilisez l'option
--failures-detailed
.
Si vous voulez aussi distinguer toutes les erreurs et les échecs
(c'est-à-dire les erreurs sans nouvelle tentative), en incluant
quelles limites sur les tentatives ont été dépassées, et de combien,
pour les échecs de sérialisation ou sur deadlock,
utilisez --verbose-errors
.
Vous pouvez préciser la méthode d'accès aux
tables pour les tables de pgbench. La variable d'environnement
PGOPTIONS
indique les options de configuration de la base,
passées à PostgreSQL via la ligne de commande (voir Section 19.1.4). Par exemple, disons que l'utilisateur
souhaite prendre une méthode d'accès aux tables créées par pgbench qui
s'appelle wuzza
. Cette dernière peut être renseignée
ainsi :
PGOPTIONS='-c default_table_access_method=wuzza'
Il est facile d'utiliser pgbench pour produire des résultats complètement dénués de sens ! Voici quelques conseils pour vous aider à obtenir des résultats pertinents.
Tout d'abord, ne croyez jamais en un test qui ne
dure que quelques secondes.
Utilisez l'option -t
ou -T
pour que le test dure au moins quelques minutes, de
façon à lisser le bruit.
Dans certains cas, il vous faudra des heures pour récupérer des
valeurs reproductibles.
C'est une bonne idée de lancer plusieurs fois le test pour voir si vos
chiffres sont ou pas reproductibles.
Pour le scénario de test par défaut typé TPC-B, le facteur d'échelle
d'initialisation (-s
) doit être au moins
aussi grand que le nombre maximum de clients que vous avez
l'intention de tester (-c
) ; sinon vous
testez surtout la contention induite par les mises à jour.
Il n'y a que -s
lignes dans la table
pgbench_branches
, et chaque transaction
veut mettre à jour l'une de ces lignes, donc si la valeur de
-c
est supérieure à la valeur de -s
,
il en résultera sûrement de nombreuses transactions bloquées
en attente de la fin d'autres transactions.
Le scénario par défaut est aussi assez sensible au temps écoulé depuis l'initialisation des tables : l'accumulation des lignes et espaces morts dans les tables change les résultats. Pour comprendre ces résultats, vous devez garder une trace du nombre total de mises à jour et du moment du vacuum. Si l'autovacuum est actif, il peut en résulter des variations imprévisibles dans les performances mesurées.
Une limitation de pgbench est qu'il peut lui-même devenir le goulet d'étranglement lorsqu'on essaie de tester avec un grand nombre de sessions clientes. Cela peut être atténué en utilisant pgbench depuis une machine différente du serveur de base de données, bien qu'une faible latence sur le réseau soit dans ce cas essentielle. Il peut même être utile de lancer plusieurs instances parallèles de pgbench, depuis plusieurs machines clientes vers le même serveur de base de données.
Si des utilisateurs non dignes de confiance ont accès à une base de données qui n'a pas adopté une méthode sécurisée d'utilisation des schémas, il ne faut pas exécuter pgbench dans cette base. pgbench utilise des noms non qualifiés et ne modifie pas le chemin de recherche.