pgbench — ejecuta una prueba de rendimiento (benchmark) en PostgreSQL
pgbench -i [option...] [dbname]
pgbench [option...] [dbname]
pgbench es un programa simple para ejecutar pruebas de
rendimiento (benchmark) en PostgreSQL. Ejecuta la misma secuencia de
comandos SQL una y otra vez, posiblemente en múltiples sesiones concurrentes de la base de
datos, y luego calcula la tasa promedio de transacciones (transacciones por segundo).
Por defecto, pgbench evalúa un escenario que está vagamente
basado en TPC-B, el cual involucra cinco comandos SELECT,
UPDATE e INSERT por transacción. Sin embargo,
es fácil evaluar otros casos escribiendo tus propios archivos de script de
transacciones.
La salida típica de pgbench se ve así:
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)
Las primeras siete líneas muestran la configuración de algunos de los parámetros más
importantes.
La sexta línea informa del número máximo de intentos para transacciones con errores de
serialización o de bloqueo mutuo (deadlock) (véase Fallos y reintentos por serialización/bloqueo mutuo
para obtener más información).
La octava línea muestra el número de transacciones completadas y programadas (esta última
es el producto del número de clientes por el número de transacciones por cliente);
ambas serán iguales a menos que la ejecución haya fallado antes de completarse o que algún
comando SQL haya fallado. (En el modo -T, solo se imprime el número
real de transacciones).
La siguiente línea informa del número de transacciones que fallaron debido a errores
de serialización o de bloqueo mutuo (deadlock) (véase Fallos y reintentos por serialización/bloqueo mutuo
para obtener más información).
La última línea muestra el número de transacciones por segundo.
La prueba de transacciones por defecto de tipo TPC-B requiere que se configuren previamente
ciertas tablas. Se debe invocar a pgbench con la opción
-i (inicializar) para crear y poblar estas tablas. (Cuando estés
probando un script personalizado, no necesitas este paso, pero en su lugar necesitarás
realizar cualquier configuración que requiera tu prueba).
La inicialización se realiza de la siguiente manera:
pgbench -i [other-options]dbname
donde dbname es el nombre de la base de datos ya creada
donde realizar la prueba. (También puedes necesitar las opciones -h,
-p y/o -U para especificar cómo conectarse al servidor
de la base de datos).
pgbench -i crea cuatro tablas: pgbench_accounts,
pgbench_branches, pgbench_history y
pgbench_tellers,
destruyendo cualquier tabla existente con estos nombres.
¡Ten mucho cuidado de usar otra base de datos si tienes tablas con estos nombres!
Con el «factor de escala» (scale factor) predeterminado de 1, las tablas contienen inicialmente este número de filas:
table # of rows --------------------------------- pgbench_branches 1 pgbench_tellers 10 pgbench_accounts 100000 pgbench_history 0
Puedes (y, para la mayoría de los propósitos, probablemente deberías) aumentar el número
de filas utilizando la opción -s (factor de escala). La opción
-F (factor de relleno o fillfactor) también podría usarse en este punto.
Una vez que hayas realizado la configuración necesaria, puedes ejecutar tu prueba de
rendimiento con un comando que no incluya la opción -i, es decir:
pgbench [options]dbname
En casi todos los casos, necesitarás algunas opciones para realizar una prueba útil.
Las opciones más importantes son -c (número de clientes),
-t (número de transacciones), -T (límite de tiempo)
y -f (especificar un archivo de script personalizado).
Consulta a continuación la lista completa de opciones.
La siguiente lista se divide en tres subsecciones. Se utilizan diferentes opciones durante la inicialización de la base de datos y durante la ejecución de las pruebas de rendimiento, pero algunas opciones son útiles en ambos casos.
pgbench acepta los siguientes argumentos de inicialización de línea de comandos:
[-d] dbname[--dbname=]dbname #
Especifica el nombre de la base de datos en la que se realizará la prueba. Si no se especifica,
se utiliza la variable de entorno PGDATABASE. Si esta no está definida,
se utiliza el nombre de usuario especificado para la conexión.
-i--initialize #Obligatoria para invocar el modo de inicialización.
-I init_steps--init-steps=init_steps #
Realiza solo un conjunto seleccionado de los pasos normales de inicialización.
init_steps especifica los pasos de inicialización que
se realizarán, utilizando un carácter por paso. Cada paso se invoca en el orden
especificado. El valor predeterminado es dtgvp.
Los pasos disponibles son:
d (Eliminar o Drop) #Elimina cualquier tabla de pgbench existente.
t (crear tablas o Tables) #
Crea las tablas utilizadas por el escenario estándar de pgbench,
a saber, pgbench_accounts,
pgbench_branches,
pgbench_history y
pgbench_tellers.
g o G (Generar datos, del lado del cliente o del servidor) #Genera datos y los carga en las tablas estándar, reemplazando cualquier dato ya presente.
Con g (generación de datos del lado del cliente), los datos
se generan en el cliente pgbench y luego se envían al servidor.
Esto consume un gran ancho de banda de red mediante un comando COPY.
pgbench utiliza la opción FREEZE para cargar datos en
tablas ordinarias (sin particionar) en la versión 14 o posterior de
PostgreSQL para acelerar el VACUUM posterior.
El uso de g hace que se imprima un mensaje en el registro cada
100,000 filas mientras se generan los datos para todas las tablas.
Con G (generación de datos del lado del servidor), solo se envían
pequeñas consultas desde el cliente pgbench y los datos realmente se
generan en el servidor. No se requiere un ancho de banda significativo para esta variante,
pero el servidor trabajará más. El uso de G hace que no se imprima
ningún mensaje de progreso durante la generación de datos.
El comportamiento de inicialización predeterminado utiliza la generación de datos del
lado del cliente (equivalente a g).
v (Vacuum) #
Invoca VACUUM en las tablas estándar.
p (crear llaves Primarias) #Crea los índices de llave primaria en las tablas estándar.
f (crear llaves Foráneas) #Crea restricciones de llave foránea entre las tablas estándar. (Ten en cuenta que este paso no se realiza por defecto).
-F fillfactor--fillfactor=fillfactor #
Crea las tablas pgbench_accounts,
pgbench_tellers y pgbench_branches
con el factor de relleno (fillfactor) dado. El valor predeterminado es 100.
-n--no-vacuum #
No realiza ninguna limpieza (vacuum) durante la inicialización.
(Esta opción suprime el paso de inicialización v,
incluso si se especificó en -I).
-q--quiet #Cambia el registro al modo silencioso (quiet), produciendo solo un mensaje de progreso cada 5 segundos. El registro predeterminado imprime un mensaje cada 100,000 filas, lo que a menudo genera muchas líneas por segundo (especialmente en hardware potente).
Esta configuración no tiene efecto si se especifica G en -I.
-s scale_factor--scale=scale_factor #
Multiplica el número de filas generadas por el factor de escala. Por ejemplo,
-s 100 creará 10,000,000 de filas en la tabla
pgbench_accounts. El valor predeterminado es 1.
Cuando la escala es de 20,000 o mayor, las columnas utilizadas para almacenar los
identificadores de cuentas (columnas aid) cambiarán a enteros
más grandes (bigint), con el fin de ser lo suficientemente grandes como para
contener el rango de identificadores de cuentas.
--foreign-keys #
Crea restricciones de llave foránea entre las tablas estándar.
(Esta opción añade el paso f a la secuencia de pasos de
inicialización, si no está ya presente).
--index-tablespace=index_tablespace #Crea los índices en el tablespace especificado, en lugar del tablespace predeterminado.
--partition-method=NAME #
Crea una tabla pgbench_accounts particionada utilizando el método
NAME. Los valores esperados son range
o hash. Esta opción requiere que --partitions se
establezca con un valor distinto de cero. Si no se especifica, el valor predeterminado es
range.
--partitions=NUM #
Crea una tabla pgbench_accounts particionada con
NUM particiones de tamaño casi igual para el número
escalado de cuentas. El valor predeterminado es 0, lo que significa
sin particionamiento.
--tablespace=tablespace #Crea las tablas en el tablespace especificado, en lugar del tablespace predeterminado.
--unlogged-tables #Crea todas las tablas como tablas no registradas (unlogged), en lugar de tablas permanentes.
pgbench acepta los siguientes argumentos de línea de comandos para las pruebas de rendimiento:
-b scriptname[@weight]--builtin=scriptname[@weight] #
Añade el script incorporado especificado a la lista de scripts que se van a ejecutar.
Los scripts incorporados disponibles son: tpcb-like,
simple-update y select-only. Se aceptan
prefijos no ambiguos de los nombres incorporados. Con el nombre especial
list, muestra la lista de scripts incorporados y termina de inmediato.
Opcionalmente, puedes escribir un peso (weight) entero después de @ para
ajustar la probabilidad de seleccionar este script frente a otros. El peso predeterminado
es 1. Consulta más abajo para obtener detalles.
-c clients--client=clients #Número de clientes simulados, es decir, número de sesiones concurrentes de base de datos. El valor predeterminado es 1.
-C--connect #Establece una nueva conexión para cada transacción, en lugar de realizarla una sola vez por sesión de cliente. Esto es útil para medir la sobrecarga de conexión.
-D varname=value--define=varname=value #
Define una variable para ser utilizada por un script personalizado (véase más abajo).
Se permiten múltiples opciones -D.
-f filename[@weight]--file=filename[@weight] #
Añade un script de transacciones leído de filename a la lista
de scripts que se van a ejecutar.
Opcionalmente, escribe un peso entero después de @ para ajustar la
probabilidad de seleccionar este script frente a otros. El peso predeterminado es 1. (Para
usar un nombre de archivo de script que incluya un carácter @, añade un
peso para evitar ambigüedades, por ejemplo filen@me@1). Consulta más
abajo para obtener detalles.
-j threads--jobs=threads #Número de hilos de trabajo (worker threads) dentro de pgbench. El uso de más de un hilo puede ser útil en máquinas multi-CPU. Los clientes se distribuyen de la manera más uniforme posible entre los hilos disponibles. El valor predeterminado es 1.
-l--log #Escribe información sobre cada transacción en un archivo de registro. Consulta más abajo para obtener detalles.
-L limit--latency-limit=limit #
Las transacciones que duran más de limit milisegundos se cuentan
y se informan por separado, como tardías (late).
Cuando se utiliza la limitación de tasa o regulación (--rate=...), las
transacciones que se retrasan más de limit ms con respecto a lo
programado y, por tanto, no tienen esperanza de cumplir con el límite de latencia, no se envían
al servidor en absoluto. Estas se cuentan y se informan por separado como omitidas
(skipped).
Cuando se utiliza la opción --max-tries, una transacción que falla debido a
una anomalía de serialización o por un bloqueo mutuo no se reintentará si el tiempo total de
todos sus intentos es mayor que limit ms. Para limitar solo el
tiempo de los intentos y no su número, utiliza --max-tries=0. Por defecto,
la opción --max-tries está configurada en 1 y las transacciones con errores
de serialización/bloqueo mutuo no se reintentan. Véase Fallos y reintentos por serialización/bloqueo mutuo
para más información sobre el reintento de tales transacciones.
-M querymode--protocol=querymode #Protocolo a utilizar para enviar consultas al servidor:
simple: utiliza el protocolo de consulta simple.
extended: utiliza el protocolo de consulta extendido.
prepared: utiliza el protocolo de consulta extendido con sentencias preparadas.
En el modo prepared, pgbench reutiliza el
resultado del análisis de la sentencia a partir de la segunda iteración de la consulta, por
lo que pgbench se ejecuta más rápido que en otros modos.
El valor predeterminado es el protocolo de consulta simple. (Véase Chapter 54 para obtener más información).
-n--no-vacuum #
No realiza ninguna limpieza (vacuum) antes de ejecutar la prueba. Esta opción es
necesaria si estás ejecutando un escenario de prueba personalizado que no
incluya las tablas estándar pgbench_accounts,
pgbench_branches, pgbench_history y
pgbench_tellers.
-N--skip-some-updates #
Ejecuta el script incorporado de actualización simple. Abreviatura de
-b simple-update.
-P sec--progress=sec #
Muestra un informe de progreso cada sec segundos. El informe incluye el
tiempo transcurrido desde el inicio de la ejecución, el TPS desde el último informe, la latencia
promedio de las transacciones, la desviación estándar y el número de transacciones fallidas desde
el último informe. Bajo regulación (-R), la latencia se calcula con respecto al
tiempo programado de inicio de la transacción, no al tiempo de inicio real de la misma, por lo que
también incluye el tiempo de retraso promedio del programa. Cuando se utiliza
--max-tries para habilitar los reintentos de transacciones tras errores de
serialización/bloqueo mutuo, el informe incluye el número de transacciones reintentadas y la suma de
todos los reintentos.
-r--report-per-command #
Informa de las siguientes estadísticas para cada comando después de que finalice la prueba de
rendimiento: la latencia promedio por sentencia (tiempo de ejecución desde la perspectiva
del cliente), el número de fallos y el número de reintentos tras errores de serialización
o bloqueo mutuo en este comando. El informe muestra las estadísticas de reintentos solo si
la opción --max-tries no es igual a 1.
-R rate--rate=rate #Ejecuta las transacciones apuntando a la tasa especificada en lugar de ejecutarse lo más rápido posible (el valor predeterminado). La tasa se especifica en transacciones por segundo. Si la tasa objetivo está por encima de la tasa máxima posible, el límite de tasa no afectará a los resultados.
La tasa se busca iniciando transacciones a lo largo de una línea de tiempo programada con distribución de Poisson. El tiempo de inicio programado esperado avanza en función de cuándo comenzó el cliente por primera vez, no de cuándo terminó la transacción anterior. Este enfoque significa que cuando las transacciones superan su tiempo de finalización programado original, es posible que las posteriores se pongan al día de nuevo.
Cuando la regulación está activa, la latencia de la transacción reportada al final de la ejecución se calcula a partir de los tiempos de inicio programados, por lo que incluye el tiempo que cada transacción tuvo que esperar a que terminara la transacción anterior. El tiempo de espera se denomina tiempo de retraso del programa (schedule lag time), y su promedio y máximo también se reportan por separado. La latencia de la transacción con respecto al tiempo de inicio real de la misma, es decir, el tiempo dedicado a ejecutar la transacción en la base de datos, se puede calcular restando el tiempo de retraso del programa de la latencia reportada.
Si se utiliza --latency-limit junto con --rate, una transacción
puede retrasarse tanto que ya supere el límite de latencia cuando termine la transacción anterior,
debido a que la latencia se calcula a partir del tiempo de inicio programado. Dichas transacciones
no se envían al servidor, sino que se omiten por completo y se cuentan por separado.
Un tiempo de retraso de programación elevado es una indicación de que el sistema no puede procesar transacciones a la tasa especificada, con el número de clientes e hilos elegidos. Cuando el tiempo promedio de ejecución de transacciones es mayor que el intervalo programado entre cada transacción, cada transacción sucesiva se retrasará aún más, y el tiempo de retraso de la programación seguirá aumentando cuanto más larga sea la prueba. Cuando esto suceda, tendrás que reducir la tasa de transacciones especificada.
-s scale_factor--scale=scale_factor #
Informa del factor de escala especificado en la salida de pgbench.
Con las pruebas incorporadas, esto no es necesario; el factor de escala correcto se detectará
contando el número de filas en la tabla pgbench_branches. Sin embargo,
cuando se prueben solo pruebas personalizadas (opción -f), el factor de escala
se informará como 1 a menos que se use esta opción.
-S--select-only #
Ejecuta el script incorporado de solo selección. Abreviatura de
-b select-only.
-t transactions--transactions=transactions #Número de transacciones que ejecuta cada cliente. El valor predeterminado es 10.
-T seconds--time=seconds #
Ejecuta la prueba durante este número de segundos, en lugar de un número fijo de transacciones por
cliente. Las opciones -t y -T son mutuamente excluyentes.
-v--vacuum-all #
Limpia (vacuum) las cuatro tablas estándar antes de ejecutar la prueba. Sin -n
ni -v, pgbench limpiará las tablas
pgbench_tellers y pgbench_branches,
y truncará pgbench_history.
--aggregate-interval=seconds #
Duración del intervalo de agregación (en segundos). Puede utilizarse únicamente con la opción
-l. Con esta opción, el registro contiene datos resumidos por intervalo, como
se describe a continuación.
--exit-on-abort #
Termina inmediatamente cuando cualquier cliente es abortado debido a algún error. Sin esta opción,
incluso cuando un cliente es abortado, otros clientes podrían continuar su ejecución según lo
especificado por la opción -t o -T, y
pgbench imprimirá resultados incompletos en este caso.
Ten en cuenta que los fallos de serialización o bloqueo mutuo no abortan al cliente, por lo que no se ven afectados por esta opción. Véase Fallos y reintentos por serialización/bloqueo mutuo para obtener más información.
--failures-detailed #Informa de los fallos en los registros por transacción y de agregación, así como en los informes principales y por script, agrupados por los siguientes tipos:
fallos de serialización;
fallos de bloqueo mutuo;
Véase Fallos y reintentos por serialización/bloqueo mutuo para obtener más información.
--log-prefix=prefix #
Establece el prefijo de nombre de archivo para los archivos de registro creados por
--log. El valor predeterminado es pgbench_log.
--max-tries=number_of_tries #
Habilita los reintentos para transacciones con errores de serialización/bloqueo mutuo y establece
el número máximo de estos intentos. Esta opción se puede combinar con la opción
--latency-limit, que limita el tiempo total de todos los intentos de la
transacción; además, no puedes utilizar un número ilimitado de intentos
(--max-tries=0) sin --latency-limit o --time.
El valor predeterminado es 1 y las transacciones con errores de serialización/bloqueo mutuo no se
reintentan. Véase Fallos y reintentos por serialización/bloqueo mutuo para más información sobre el reintento de
tales transacciones.
--progress-timestamp #
Al mostrar el progreso (opción -P), utiliza una marca de tiempo (Unix epoch) en
lugar del número de segundos transcurridos desde el inicio de la ejecución. La unidad se mide en
segundos, con precisión de milisegundos después del punto. Esto ayuda a comparar los registros
generados por diversas herramientas.
--random-seed=seed #
Establece la semilla del generador aleatorio. Siembra el generador de números aleatorios del sistema,
el cual produce luego una secuencia de estados iniciales del generador, uno para cada hilo.
Los valores para seed pueden ser:
time (el valor predeterminado, la semilla se basa en la hora actual),
rand (utiliza una fuente aleatoria fuerte, fallando si no hay ninguna
disponible), o un valor entero decimal sin signo.
El generador aleatorio se invoca explícitamente desde un script pgbench (funciones
random...) o implícitamente (por ejemplo, la opción --rate lo utiliza para
programar transacciones).
Cuando se establece explícitamente, el valor utilizado para la siembra se muestra en el terminal.
Cualquier valor permitido para seed también se puede proporcionar a través
de la variable de entorno PGBENCH_RANDOM_SEED.
Para asegurar que la semilla proporcionada afecte a todos los usos posibles, coloca esta opción al principio
o utiliza la variable de entorno.
Establecer la semilla explícitamente permite reproducir exactamente una ejecución de pgbench,
en lo que respecta a los números aleatorios. Como el estado aleatorio se gestiona por hilo, esto significa
que la ejecución de pgbench será exactamente la misma para una invocación idéntica si hay un
cliente por hilo y no hay dependencias externas o de datos. Desde un punto de vista estadístico, reproducir
las ejecuciones exactamente es una mala idea porque puede ocultar la variabilidad del rendimiento o mejorar el
rendimiento indebidamente, por ejemplo, al acceder a las mismas páginas que en una ejecución anterior. Sin
embargo, también puede ser de gran ayuda para la depuración, por ejemplo, para volver a ejecutar un caso difícil
que provoque un error. Utilízalo sabiamente.
--sampling-rate=rate #Tasa de muestreo, utilizada al escribir datos en el registro, para reducir la cantidad de registro generado. Si se proporciona esta opción, solo se registra la fracción especificada de transacciones. 1.0 significa que se registrarán todas las transacciones, 0.05 significa que solo se registrará el 5% de las transacciones.
Recuerda tener en cuenta la tasa de muestreo al procesar el archivo de registro. Por ejemplo, al calcular los valores de TPS, deberás multiplicar los números en consecuencia (por ejemplo, con una tasa de muestreo de 0.01, solo obtendrás 1/100 del TPS real).
--show-script=scriptname #
Muestra el código real del script incorporado scriptname en stderr y termina de inmediato.
--verbose-errors #Imprime mensajes sobre todos los errores y fallos (errores sin reintento), incluyendo qué límite de reintentos se superó y por cuánto se superó para los fallos de serialización/bloqueo mutuo. (Ten en cuenta que en este caso la salida puede aumentar significativamente). Véase Fallos y reintentos por serialización/bloqueo mutuo para obtener más información.
pgbench también acepta los siguientes argumentos comunes de línea de comandos para parámetros de conexión y otras configuraciones comunes:
--debug #Imprime la salida de depuración.
-h hostname--host=hostname #El nombre de host del servidor de bases de datos.
-p port--port=port #El número de puerto del servidor de bases de datos.
-U login--username=login #El nombre de usuario con el que conectarse.
-V--version #Muestra la versión de pgbench y termina.
-?--help #Muestra la ayuda sobre los argumentos de línea de comandos de pgbench y termina.
Una ejecución exitosa terminará con el estado 0. El estado de salida 1 indica problemas estáticos como
opciones de línea de comandos no válidas o errores internos que se supone que nunca deben ocurrir. Los
errores tempranos que ocurren al iniciar la prueba de rendimiento, como fallos iniciales de conexión,
también terminan con el estado 1. Los errores durante la ejecución, como errores de la base de datos o
problemas en el script, darán como resultado el estado de salida 2. En este último caso,
pgbench imprimirá resultados parciales si no se especifica la opción
--exit-on-abort.
PGDATABASEPGHOSTPGPORTPGUSER #Parámetros de conexión predeterminados.
Esta utilidad, al igual que la mayoría de las otras utilidades de PostgreSQL, utiliza las variables de entorno admitidas por libpq (véase Section 32.15).
La variable de entorno PG_COLOR especifica si se debe usar
color en los mensajes de diagnóstico. Los valores posibles son
always, auto y
never.
pgbench ejecuta scripts de prueba elegidos aleatoriamente de una lista especificada.
Los scripts pueden incluir scripts incorporados especificados con -b y scripts proporcionados
por el usuario especificados con -f. A cada script se le puede asignar un peso relativo especificado
después de un carácter @ para cambiar su probabilidad de selección. El peso predeterminado es
1. Los scripts con un peso de 0 se ignoran.
El script de transacción incorporado por defecto (también invocado con -b tpcb-like) emite siete
comandos por transacción sobre valores elegidos aleatoriamente de aid, tid,
bid y delta. El escenario está inspirado en la prueba de rendimiento TPC-B,
pero no es realmente TPC-B, de ahí el nombre.
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 seleccionas el script incorporado simple-update (también con -N), los
pasos 4 y 5 no se incluyen en la transacción. Esto evitará la contención de actualizaciones en estas tablas, pero
hace que el caso de prueba se parezca aún menos a TPC-B.
Si seleccionas el script incorporado select-only (también con -S), solo se emite el
comando SELECT.
pgbench admite la ejecución de escenarios de prueba de rendimiento personalizados al reemplazar
el script de transacción predeterminado (descrito anteriormente) con un script de transacción leído de un archivo
(opción -f). En este caso, una “transacción” cuenta como una ejecución de un archivo de script.
Un archivo de script contiene uno o más comandos SQL terminados por puntos y comas. Las líneas vacías y las líneas que
comienzan con -- se ignoran. Los archivos de script también pueden contener “metacomandos”,
que son interpretados por el propio pgbench, como se describe a continuación.
Antes de PostgreSQL 9.6, los comandos SQL en los archivos de script terminaban con saltos de línea, por lo que no podían continuar en las líneas siguientes. Ahora se requiere un punto y coma para separar comandos SQL consecutivos (aunque un comando SQL no lo necesita si va seguido de un metacomando). Si necesitas crear un archivo de script que funcione tanto con versiones antiguas como nuevas de pgbench, asegúrate de escribir cada comando SQL en una sola línea terminada con un punto y coma.
Se asume que los scripts de pgbench no contienen bloques incompletos de transacciones SQL. Si en tiempo de ejecución el cliente llega al final del script sin completar el último bloque de transacción, se abortará.
Existe una funcionalidad simple de sustitución de variables para los archivos de script. Los nombres de las variables deben
consistir en letras (incluyendo letras no latinas), dígitos y guiones bajos, y el primer carácter no debe ser un dígito.
Las variables se pueden establecer mediante la opción -D de la línea de comandos, explicada anteriormente,
o mediante los metacomandos que se explican a continuación.
Además de las variables preestablecidas por las opciones de línea de comandos -D, existen algunas variables
que se preestablecen automáticamente, listadas en Table 301. Un valor especificado para estas
variables mediante -D tiene prioridad sobre los valores automáticos preestablecidos. Una vez establecido, el valor
de una variable se puede insertar en un comando SQL escribiendo :variablename.
Al ejecutar más de una sesión de cliente, cada sesión tiene su propio conjunto de variables. pgbench
admite hasta 255 usos de variables en una sola sentencia.
Table 301. Variables automáticas de pgbench
| Variable | Descripción |
|---|---|
client_id | número único que identifica la sesión del cliente (comienza desde cero) |
default_seed | semilla utilizada en las funciones hash y de permutación pseudoaleatoria por defecto |
random_seed | semilla del generador aleatorio (a menos que se sobrescriba con -D) |
scale | factor de escala actual |
Los metacomandos de los archivos de script comienzan con una barra invertida (\) y normalmente se extienden
hasta el final de la línea, aunque se pueden continuar en líneas adicionales escribiendo barra invertida-retorno. Los argumentos
de un metacomando están separados por espacios en blanco. Se admiten los siguientes metacomandos:
\gset [prefix]
\aset [prefix]
#
Estos comandos se pueden utilizar para finalizar consultas SQL, ocupando el lugar del punto y coma terminal (;).
Cuando se utiliza el comando \gset, se espera que la consulta SQL precedente devuelva una fila, cuyas columnas
se almacenan en variables que llevan el nombre de las columnas y tienen el prefijo prefix si se proporciona.
Cuando se utiliza el comando \aset, todas las consultas SQL combinadas (separadas por \;)
tienen sus columnas almacenadas en variables que llevan el nombre de las columnas y tienen el prefijo prefix
si se proporciona. Si una consulta no devuelve ninguna fila, no se realiza ninguna asignación y se puede comprobar la existencia
de la variable para detectar esto. Si una consulta devuelve más de una fila, se mantiene el último valor.
\gset y \aset no se pueden utilizar en el modo de tubería (pipeline), ya que los resultados
de la consulta aún no están disponibles en el momento en que los comandos los necesitarían.
El siguiente ejemplo coloca el saldo final de la cuenta de la primera consulta en la variable abalance,
y llena las variables p_two y p_three con enteros de la tercera consulta. El
resultado de la segunda consulta se descarta. Los resultados de las dos últimas consultas combinadas se almacenan en las variables
four y five.
UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid RETURNING abalance \gset -- compuesto de dos consultas 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 #
Este grupo de comandos implementa bloques condicionales anidables, de manera similar a \if expression
de psql. Las expresiones condicionales son idénticas a las de \set, interpretando los valores
distintos de cero como verdaderos.
\set varname expression
#
Establece la variable varname en un valor calculado a partir de la expresión expression.
La expresión puede contener la constante NULL, las constantes booleanas TRUE y FALSE,
constantes enteras como 5432, constantes dobles como 3.14159, referencias a variables
:variablename, operadores con su prioridad y asociatividad SQL habituales (véase
Operadores incorporados), llamadas a funciones (véase
Funciones incorporadas), expresiones condicionales genéricas CASE de SQL y paréntesis.
Las funciones y la mayoría de los operadores devuelven NULL ante una entrada NULL.
Para fines condicionales, los valores numéricos distintos de cero son TRUE, los valores numéricos de cero y
NULL son FALSE.
Las constantes enteras y dobles demasiado grandes o pequeñas, así como los operadores aritméticos enteros (+,
-, * y /) producen errores en caso de desbordamiento.
Cuando no se proporciona una cláusula final ELSE a un CASE, el valor predeterminado es NULL.
Ejemplos:
\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 number [ us | ms | s ]
#
Hace que la ejecución del script se detenga (duerma) durante la duración especificada en microsegundos (us),
milisegundos (ms) o segundos (s). Si se omite la unidad, la unidad predeterminada son los segundos.
El parámetro number puede ser una constante entera o una referencia :variablename
a una variable que tenga un valor entero.
Ejemplo:
\sleep 10 ms
\setshell varname command [ argument ... ]
#
Establece la variable varname con el resultado del comando de consola (shell) command
con los argumentos argument dados. El comando debe devolver un valor entero a través de su salida estándar.
El parámetro command y cada argumento argument pueden ser una constante de texto
o una referencia :variablename a una variable. Si deseas utilizar un argumento
argument que comience con dos puntos, escribe un carácter de dos puntos adicional al principio del argumento.
Ejemplo:
\setshell variable_to_be_assigned command literal_argument :variable ::literal_starting_with_colon
\shell command [ argument ... ]
#
Igual que \setshell, pero el resultado del comando se descarta.
Ejemplo:
\shell command literal_argument :variable ::literal_starting_with_colon
\startpipeline\syncpipeline\endpipeline #
Este grupo de comandos implementa la ejecución en tubería (pipelining) de sentencias SQL. Una tubería debe comenzar con un
\startpipeline y terminar con un \endpipeline. En medio puede haber cualquier número de
comandos \syncpipeline, los cuales envían un mensaje de sincronización (sync) sin finalizar la tubería en curso
y sin vaciar el búfer de envío. En el modo de tubería, las sentencias se envían al servidor sin esperar los resultados de las sentencias
anteriores. Véase Section 32.5 para obtener más detalles. El modo de tubería requiere el uso del protocolo
de consulta extendido.
Los operadores aritméticos, de bits, de comparación y lógicos listados en Table 302 están incorporados en
pgbench y pueden utilizarse en expresiones que aparezcan en \set.
Los operadores se enumeran en orden de precedencia creciente. Excepto donde se indique, los operadores que toman dos entradas numéricas
producirán un valor doble si alguna de las entradas es doble; de lo contrario, producen un resultado entero.
Table 302. Operadores de pgbench
Operador Descripción Ejemplo(s) |
|---|
OR lógico
|
AND lógico
|
NOT lógico
|
Pruebas de valor booleano
|
Pruebas de nulidad
|
Igual
|
No igual
|
No igual
|
Menor que
|
Menor o igual que
|
Mayor que
|
Mayor o igual que
|
OR binario
|
XOR binario
|
AND binario
|
NOT binario
|
Desplazamiento binario a la izquierda
|
Desplazamiento binario a la derecha
|
Suma
|
Resta
|
Multiplicación
|
División (trunca el resultado hacia cero si ambas entradas son enteros)
|
Módulo (resto de la división)
|
Negación
|
Las funciones listadas en Table 303 están incorporadas en
pgbench y pueden utilizarse en expresiones que aparezcan en
\set.
Table 303. Funciones de pgbench
Función Descripción Ejemplo(s) |
|---|
Valor absoluto
|
Imprime el argumento en stderr y lo devuelve como resultado.
|
Convierte a tipo double.
|
Exponencial (el número
|
Selecciona el valor más grande de los argumentos.
|
Este es un alias para la función
|
Calcula el hash FNV-1a.
|
Calcula el hash MurmurHash2.
|
Convierte a tipo entero.
|
Selecciona el valor más pequeño de los argumentos.
|
Logaritmo natural
|
Módulo (resto de la división)
|
Valor permutado de
|
Valor aproximado de π
|
El valor
|
Calcula un entero aleatorio distribuido uniformemente en el rango
|
Calcula un entero aleatorio con distribución exponencial en el rango
|
Calcula un entero aleatorio con distribución gaussiana en el rango
|
Calcula un entero aleatorio con distribución de Zipf en el rango
|
Raíz cuadrada
|
La función random genera valores utilizando una distribución uniforme, lo que significa
que todos los valores en el rango especificado se obtienen con la misma probabilidad. Las funciones
random_exponential, random_gaussian y random_zipfian
requieren un parámetro double adicional que determina la forma exacta de la distribución.
Para una distribución exponencial, el parámetro parameter controla la distribución
al truncar una distribución exponencial decreciente en el valor de parameter, y luego
la proyecta sobre los enteros entre los límites. Para ser precisos, con:
f(x) = exp(-parameter * (x - min) / (max - min + 1)) / (1 - exp(-parameter))
El valor de i entre min y max inclusive
se obtiene con la probabilidad: f(i) - f(i + 1).
Intuitivamente, cuanto mayor sea el parámetro parameter, más frecuentemente se accederá a
valores cercanos a min, y menos frecuentemente se accederá a valores cercanos a
max. Cuanto más cercano a 0 sea el parámetro parameter, más plana
(más uniforme) será la distribución del acceso. Una aproximación burda de la distribución es que el 1% de los valores más
frecuentes en el rango, cercanos a min, se obtienen el parameter% del
tiempo. El valor del parámetro parameter debe ser estrictamente positivo.
Para una distribución gaussiana, el intervalo se mapea sobre una distribución normal estándar (la curva de campana gaussiana
clásica) truncada en -parameter a la izquierda y en +parameter a la derecha. Los valores
situados en el centro del intervalo tienen mayor probabilidad de ser seleccionados. Para ser precisos, si PHI(x)
es la función de distribución acumulada de la distribución normal estándar, con la media mu definida como
(max + min) / 2.0, con:
f(x) = PHI(2.0 * parameter * (x - mu) / (max - min + 1)) /
(2.0 * PHI(parameter) - 1)
el valor de i entre min y max inclusive se
obtiene con la probabilidad: f(i + 0.5) - f(i - 0.5).
Intuitivamente, cuanto mayor sea el parámetro parameter, más frecuentemente se seleccionarán los
valores cercanos al centro del intervalo, y menos frecuentemente los valores cercanos a los límites de min
y max. Alrededor del 67% de los valores se seleccionan de la zona media 1.0 / parameter,
es decir, una zona relativa de 0.5 / parameter alrededor de la media, y el 95% de la zona media
2.0 / parameter, es decir, una zona relativa de 1.0 / parameter alrededor de la media; por
ejemplo, si el parámetro parameter es 4.0, el 67% de los valores se seleccionan del cuarto central
(1.0 / 4.0) del intervalo (es decir, desde 3.0 / 8.0 hasta 5.0 / 8.0) y el 95% de la mitad
central (2.0 / 4.0) del intervalo (segundo y tercer cuartil). El valor mínimo permitido para el parámetro
parameter es 2.0.
random_zipfian genera una distribución de Zipf acotada. El parámetro parameter
define el grado de sesgo de la distribución. Cuanto mayor sea el parámetro parameter, con mayor
frecuencia se seleccionarán valores cercanos al principio del intervalo. La distribución es tal que, suponiendo que el rango comience
en 1, la relación de probabilidad de obtener k frente a obtener k+1 es
((.
Por ejemplo, k+1)/k)**parameterrandom_zipfian(1, ..., 2.5) produce el valor 1 unas
(2/1)**2.5 = 5.66 veces más frecuentemente que 2, el cual a su vez se produce
(3/2)**2.5 = 2.76 veces más frecuentemente que 3, y así sucesivamente.
La implementación de pgbench se basa en la obra «Non-Uniform Random Variate Generation», Luc Devroye,
pág. 550-551, Springer 1986. Debido a las limitaciones de ese algoritmo, el valor del parámetro parameter
está restringido al rango [1.001, 1000].
Al diseñar una prueba de rendimiento que seleccione filas de forma no uniforme, ten en cuenta que las filas elegidas pueden estar correlacionadas con otros datos como los IDs de una secuencia o el orden físico de las filas, lo que puede sesgar las mediciones del rendimiento.
Para evitar esto, puedes utilizar la función permute, u otro paso adicional con efecto similar, para mezclar
las filas seleccionadas y eliminar dichas correlaciones.
Las funciones hash hash, hash_murmur2 y hash_fnv1a aceptan un valor de entrada
y un parámetro de semilla opcional. En caso de que no se proporcione la semilla, se utiliza el valor de
:default_seed, que se inicializa de forma aleatoria a menos que se establezca mediante la opción -D
de la línea de comandos.
La función permute acepta un valor de entrada, un tamaño y un parámetro de semilla opcional. Genera una permutación
pseudoaleatoria de enteros en el rango [0, size) y devuelve el índice del valor de entrada en los valores permutados.
La permutación elegida está parametrizada por la semilla, cuyo valor predeterminado es :default_seed si no se especifica.
A diferencia de las funciones hash, la función permute garantiza que no haya colisiones ni vacíos en los valores de salida.
Los valores de entrada fuera del intervalo se interpretan como el módulo del tamaño. La función genera un error si el tamaño no es positivo.
permute puede utilizarse para dispersar la distribución de funciones aleatorias no uniformes como
random_zipfian o random_exponential para que los valores obtenidos con más frecuencia no estén
correlacionados de forma trivial. Por ejemplo, el siguiente script de pgbench simula una carga de trabajo
posible en el mundo real, típica de los medios sociales y las plataformas de blogs donde unas pocas cuentas generan una carga excesiva:
\set size 1000000 \set r random_zipfian(1, :size, 1.07) \set k 1 + permute(:r, :size)
En algunos casos se necesitan varias distribuciones distintas que no se correlacionen entre sí, y aquí es donde resulta muy útil el parámetro opcional de la semilla:
\set k1 1 + permute(:r, :size, :default_seed + 123) \set k2 1 + permute(:r, :size, :default_seed + 321)
Un comportamiento similar también puede aproximarse con la función hash:
\set size 1000000 \set r random_zipfian(1, 100 * :size, 1.07) \set k 1 + abs(hash(:r)) % :size
Sin embargo, dado que la función hash genera colisiones, algunos valores no serán accesibles y otros serán más
frecuentes de lo esperado según la distribución original.
Como ejemplo, la definición completa de la transacción incorporada de tipo TPC-B es:
\set aid random(1, 100000 * :scale) \set bid random(1, 1 * :scale) \set tid random(1, 10 * :scale) \set delta random(-5000, 5000) \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;
Este script permite que cada iteración de la transacción haga referencia a filas diferentes elegidas al azar. (Este ejemplo también muestra por qué es importante que cada sesión de cliente tenga sus propias variables; de lo contrario, no accederían de forma independiente a filas diferentes).
Con la opción -l (pero sin la opción --aggregate-interval), pgbench
escribe información sobre cada transacción en un archivo de registro. El archivo de registro se nombrará
, donde el prefijo
prefix.nnnprefix por defecto es pgbench_log, y nnn es el PID del proceso
pgbench. El prefijo se puede cambiar utilizando la opción --log-prefix. Si la opción
-j es 2 o superior, de modo que haya múltiples hilos de trabajo, cada uno tendrá su propio archivo de registro.
El primer hilo de trabajo utilizará el mismo nombre para su archivo de registro que en el caso estándar de un solo hilo. Los archivos
de registro adicionales para los demás hilos de trabajo se nombrarán
,
donde prefix.nnn.mmmmmm es un número secuencial para cada hilo que comienza con 1.
Cada línea de un archivo de registro describe una transacción. Contiene los siguientes campos separados por espacios:
client_ididentifica la sesión de cliente que ejecutó la transacción
transaction_nocuenta cuántas transacciones han sido ejecutadas por esa sesión
timetiempo transcurrido de la transacción, en microsegundos
script_no
identifica el archivo de script que se utilizó para la transacción (útil cuando se especifican múltiples scripts con
-f o -b)
time_epochtiempo de finalización de la transacción, expresado como una marca de tiempo Unix epoch
time_usparte decimal del segundo de la hora de finalización de la transacción, en microsegundos
schedule_lag
retraso en el inicio de la transacción, es decir, la diferencia entre la hora de inicio programada de la transacción y la hora
en que realmente comenzó, en microsegundos (solo presente si se especifica --rate)
retries
recuento de reintentos tras errores de serialización o de bloqueo mutuo durante la transacción (solo presente si
--max-tries no es igual a uno)
Cuando se utilizan tanto --rate como --latency-limit, el tiempo time para una
transacción omitida se reportará como skipped. Si la transacción termina con un fallo, su tiempo time
se reportará como failed. Si utilizas la opción --failures-detailed, el tiempo time
de la transacción fallida se reportará como serialization o deadlock dependiendo de la naturaleza del fallo
(véase Fallos y reintentos por serialización/bloqueo mutuo para obtener más información).
A continuación se muestra un fragmento de un archivo de registro generado en una ejecución con un solo cliente:
0 199 2241 0 1175850568 995598 0 200 2465 0 1175850568 998079 0 201 2513 0 1175850569 608 0 202 2038 0 1175850569 2663
Otro ejemplo con --rate=100 y --latency-limit=5 (observa la columna adicional
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
En este ejemplo, la transacción 82 se retrasó porque su latencia (6.173 ms) superó el límite de 5 ms. Las dos siguientes transacciones se omitieron porque ya se habían retrasado antes de iniciarse.
El siguiente ejemplo muestra un fragmento de un archivo de registro con fallos y reintentos, con el número máximo de intentos configurado en 10
(observa la columna adicional 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
Si se utiliza la opción --failures-detailed, el tipo de fallo se reporta en el tiempo time de la siguiente manera:
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
Al ejecutar una prueba larga en hardware que pueda procesar una gran cantidad de transacciones, los archivos de registro pueden llegar a ser muy
grandes. La opción --sampling-rate se puede utilizar para registrar únicamente una muestra aleatoria de transacciones.
Con la opción --aggregate-interval, se utiliza un formato diferente para los archivos de registro. Cada línea del registro describe
un intervalo de agregación. Contiene los siguientes campos separados por espacios:
interval_starthora de inicio del intervalo, expresada como una marca de tiempo Unix epoch
num_transactionsnúmero de transacciones dentro del intervalo
sum_latencysuma de las latencias de las transacciones
sum_latency_2suma de los cuadrados de las latencias de las transacciones
min_latencylatencia mínima de transacción
max_latencylatencia máxima de transacción
sum_lag
suma de los retrasos de inicio de transacción (cero a menos que se especifique --rate)
sum_lag_2
suma de los cuadrados de los retrasos de inicio de transacción (cero a menos que se especifique --rate)
min_lag
retraso mínimo de inicio de transacción (cero a menos que se especifique --rate)
max_lag
retraso máximo de inicio de transacción (cero a menos que se especifique --rate)
skipped
número de transacciones omitidas porque habrían comenzado demasiado tarde (cero a menos que se especifiquen tanto --rate
como --latency-limit)
retried
número de transacciones reintentadas (cero a menos que --max-tries no sea igual a uno)
retries
número de reintentos tras errores de serialización o de bloqueo mutuo (cero a menos que --max-tries no sea igual a uno)
serialization_failures
número de transacciones que obtuvieron un error de serialización y no fueron reintentadas posteriormente (cero a menos que se especifique
--failures-detailed)
deadlock_failures
número de transacciones que obtuvieron un error de bloqueo mutuo y no fueron reintentadas posteriormente (cero a menos que se especifique
--failures-detailed)
A continuación se muestra un ejemplo de salida generada con esta opción:
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
Observa que mientras que el formato de registro simple (no agregado) muestra qué script se utilizó para cada transacción, el formato agregado no lo hace. Por tanto, si necesitas datos por script, tendrás que agregar los datos por tu cuenta.
Con la opción -r, pgbench recopila las siguientes estadísticas para cada sentencia:
latency — tiempo de transacción transcurrido para cada sentencia. pgbench
informa de un valor promedio de todas las ejecuciones exitosas de la sentencia.
El número de fallos en esta sentencia. Véase Fallos y reintentos por serialización/bloqueo mutuo para obtener más información.
El número de reintentos tras un error de serialización o de bloqueo mutuo en esta sentencia. Véase Fallos y reintentos por serialización/bloqueo mutuo para obtener más información.
El informe muestra las estadísticas de reintentos solo si la opción --max-tries no es igual a 1.
Todos los valores se calculan para cada sentencia ejecutada por cada cliente y se reportan después de que finalice la prueba de rendimiento.
Para el script predeterminado, la salida se verá similar a esto:
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: 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;
Otro ejemplo de salida para el script predeterminado utilizando el nivel de aislamiento de transacciones predeterminado serializable
(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;
Si se especifican múltiples archivos de script, todas las estadísticas se informan por separado para cada archivo de script.
Ten en cuenta que recopilar la información temporal adicional necesaria para el cálculo de la latencia por sentencia añade cierta sobrecarga. Esto ralentizará la velocidad promedio de ejecución y disminuirá el TPS calculado. El grado de ralentización varía significativamente dependiendo de la plataforma y del hardware. Comparar los valores promedio de TPS con y sin el informe de latencia habilitado es una buena manera de medir si la sobrecarga de tiempo es significativa.
Al ejecutar pgbench, existen tres tipos principales de errores:
Errores del programa principal. Son los más graves y siempre dan como resultado una salida inmediata de pgbench con el mensaje de error correspondiente. Incluyen:
errores al inicio de pgbench (por ejemplo, un valor de opción no válido);
errores en el modo de inicialización (por ejemplo, falla la consulta para crear tablas para los scripts incorporados);
errores antes de iniciar los hilos de ejecución (por ejemplo, no se pudo conectar al servidor de bases de datos, error de sintaxis en el metacomando, fallo en la creación de hilos);
errores internos de pgbench (los cuales se supone que nunca deben ocurrir...).
Errores cuando el hilo gestiona sus clientes (por ejemplo, el cliente no pudo iniciar una conexión con el servidor de bases de
datos o el socket para conectar el cliente al servidor de bases de datos ha dejado de ser válido). En tales casos, todos los clientes
de este hilo se detienen mientras que los otros hilos continúan trabajando. Sin embargo, si se especifica la opción
--exit-on-abort, todos los hilos se detienen inmediatamente en este caso.
Errores directos del cliente. Llevan a una salida inmediata de pgbench con el mensaje de error
correspondiente en el caso de un error interno de pgbench (los cuales se supone que nunca deben ocurrir...)
o cuando se especifica la opción --exit-on-abort. De lo contrario, en el peor de los casos, solo llevan a la
cancelación (abort) del cliente fallido mientras que otros clientes continúan su ejecución (pero algunos errores del cliente se manejan
sin la cancelación del cliente y se informan por separado, véase más abajo). En el resto de esta sección se asume que los errores
analizados son únicamente los errores directos del cliente y que no son errores internos de pgbench.
La ejecución de un cliente se cancela en caso de un error grave; por ejemplo, si se perdió la conexión con el servidor de bases de datos
o si se llegó al final del script sin completar la última transacción. Además, si la ejecución de un comando SQL o metacomando falla por
razones distintas a errores de serialización o de bloqueo mutuo, el cliente es cancelado. De lo contrario, si un comando SQL falla con errores
de serialización o de bloqueo mutuo, el cliente no se cancela. En tales casos, la transacción actual se revierte (rollback), lo cual también
incluye restablecer las variables del cliente tal y como estaban antes de la ejecución de esta transacción (se asume que un script de
transacciones contiene solo una transacción; véase ¿Qué es la «transacción» que realmente se ejecuta en pgbench? para obtener más información).
Las transacciones con errores de serialización o de bloqueo mutuo se repiten tras las reversiones hasta que se completen con éxito o alcancen
el número máximo de intentos (especificado por la opción --max-tries) o el tiempo máximo de reintentos (especificado por la
opción --latency-limit) o el final de la prueba de rendimiento (especificado por la opción --time). Si el
último intento de ejecución falla, esta transacción se reportará como fallida, pero el cliente no se cancela y continúa trabajando.
Sin especificar la opción --max-tries, una transacción nunca se reintentará tras un error de serialización o de bloqueo mutuo
porque su valor predeterminado es 1. Utiliza un número ilimitado de intentos (--max-tries=0) y la opción
--latency-limit para limitar únicamente el tiempo máximo de los intentos. También puedes utilizar la opción
--time para limitar la duración de la prueba de rendimiento bajo un número ilimitado de intentos.
Ten cuidado al repetir scripts que contengan múltiples transacciones: el script siempre se reintenta por completo, por lo que las transacciones exitosas pueden ejecutarse varias veces.
Ten cuidado al repetir transacciones con comandos de consola (shell). A diferencia de los resultados de los comandos SQL, los resultados
de los comandos de consola no se revierten, excepto por el valor de variable del comando \setshell.
La latencia de una transacción exitosa incluye todo el tiempo de ejecución de la transacción con reversiones y reintentos. La latencia se mide únicamente para las transacciones y comandos exitosos, pero no para las transacciones o comandos fallidos.
El informe principal contiene el número de transacciones fallidas. Si la opción --max-tries no es igual a 1, el informe principal
también contiene estadísticas relacionadas con los reintentos: el número total de transacciones reintentadas y el número total de reintentos.
El informe por script hereda todos estos campos del informe principal. El informe por sentencia muestra estadísticas de reintentos únicamente
si la opción --max-tries no es igual a 1.
Si deseas agrupar los fallos por tipos básicos en los registros por transacción y de agregación, así como en los informes principal y por
script, utiliza la opción --failures-detailed. Si también deseas distinguir todos los errores y fallos (errores sin reintento)
por tipo, incluyendo qué límite de reintentos se superó y por cuánto se superó para los fallos de serialización/bloqueo mutuo, utiliza la opción
--verbose-errors.
Puedes especificar el método de acceso a tablas para las tablas de pgbench. La variable de entorno
PGOPTIONS especifica las opciones de configuración de la base de datos que se pasan a PostgreSQL a través de la línea de comandos
(Véase Section 19.1.4). Por ejemplo, un hipotético método de acceso a tablas predeterminado para las tablas que crea
pgbench llamado wuzza se puede especificar con:
PGOPTIONS='-c default_table_access_method=wuzza'
Es muy fácil utilizar pgbench para producir cifras completamente sin sentido. A continuación se presentan algunas pautas que te ayudarán a obtener resultados útiles.
En primer lugar, nunca creas en ninguna prueba que dure solo unos pocos segundos. Utiliza la opción -t o
-T para hacer que la ejecución dure al menos unos minutos, con el fin de promediar el ruido. En algunos casos podrías necesitar
horas para obtener cifras que sean reproducibles. Es una buena idea intentar ejecutar la prueba varias veces, para averiguar si tus cifras
son reproducibles o no.
Para el escenario de prueba predeterminado similar a TPC-B, el factor de escala de inicialización (-s) debería ser al menos tan
grande como el número más grande de clientes que planeas probar (-c); de lo contrario, estarás midiendo principalmente la
contención de actualizaciones. Solo hay -s filas en la tabla pgbench_branches, y cada transacción desea
actualizar una de ellas, por lo que los valores de -c que superen a -s sin duda darán como resultado muchas
transacciones bloqueadas en espera de otras transacciones.
El escenario de prueba predeterminado también es bastante sensible al tiempo transcurrido desde que se inicializaron las tablas: la acumulación de filas muertas y espacio muerto en las tablas altera los resultados. Para comprender los resultados, debes realizar un seguimiento del número total de actualizaciones y de cuándo se realiza la limpieza (vacuum). Si la limpieza automática (autovacuum) está habilitada, puede dar lugar a cambios impredecibles en el rendimiento medido.
Una limitación de pgbench es que él mismo puede convertirse en el cuello de botella cuando se intenta probar una gran cantidad de sesiones de clientes. Esto se puede mitigar ejecutando pgbench en una máquina diferente a la del servidor de bases de datos, aunque será esencial contar con una baja latencia de red. Incluso podría ser útil ejecutar varias instancias de pgbench simultáneamente, en varias máquinas cliente, contra el mismo servidor de bases de datos.
Si los usuarios no confiables tienen acceso a una base de datos que no ha adoptado un patrón de uso seguro de esquemas, no ejecutes pgbench en esa base de datos. pgbench utiliza nombres no calificados y no manipula la ruta de búsqueda (search path).