pgbench

pgbench — executa testes de desempenho no PostgreSQL

Sinopse

pgbench -i [opção...] [nome_do_banco_de_dados]

pgbench [opção...] [nome_do_banco_de_dados]

Descrição

O utilitário pgbench é um programa simples para executar testes de desempenho (benchmark) no PostgreSQL. Ele executa a mesma sequência de comandos SQL repetidamente, possivelmente em várias sessões de banco de dados concorrentes e, em seguida, calcula a taxa média de transação (transações por segundo). Por padrão, o pgbench testa um contexto vagamente baseado no TPC-B, envolvendo cinco comandos SELECT, UPDATE e INSERT por transação. Entretanto, é fácil testar outros casos escrevendo seus próprios arquivos de script de transação.

A saída típica do pgbench se parece com:

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)

As sete primeiras linhas descrevem algumas das definições de parâmetros mais importantes. A sexta linha relata o número máximo de novas tentativas para transações com erros de serialização ou impasse (veja Falhas e novas tentativas de serialização/impasse para obter mais informações). A oitava linha relata o número de transações concluídas e pretendidas (este último sendo simplesmente o produto do número de clientes pelo número de transações por cliente); estes dois números serão iguais, a menos que a execução falhe antes da conclusão, ou que algum(ns) comando(s) SQL tenha(m) falhado. (No modo -T é mostrado apenas o número efetivo de transações.) A próxima linha relata o número de transações com falha devido a erros de serialização ou impasse. (veja Falhas e novas tentativas de serialização/impasse para obter mais informações). A última linha relata o número de transações por segundo.

O teste de transação padrão do tipo TPC-B requer que sejam configuradas antecipadamente tabelas específicas. O utilitário pgbench deve ser chamado com a opção -i (inicializar) para criar e preencher estas tabelas. (Ao testar um script personalizado, esta etapa não é necessária, mas será preciso fazer as configurações necessárias para o teste.) A inicialização se parece com:

pgbench -i [ outras_opções ] nome_do_banco_de_dados

onde nome_do_banco_de_dados é o nome do banco de dados já criado para o teste. (Também podem ser necessárias as opções -h, -p, e/ou -U, para especificar como se conectar ao servidor de banco de dados.)

Cuidado

A inicialização pgbench -i cria quatro tabelas: pgbench_accounts, pgbench_branches, pgbench_history e pgbench_tellers, removendo antes todas as tabelas existentes com estes nomes. Deve-se tomar o cuidado de usar outro banco de dados se existirem tabelas com estes nomes!

No fator de escala padrão de 1, as tabelas inicialmente contêm esta quantidade de linhas:

SELECT n.nspname AS esquema,
       c.relname AS tabela,
       c.reltuples AS linhas
FROM pg_class c
JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE c.relkind = 'r'
      AND n.nspname NOT IN ('information_schema','pg_catalog')
      AND c.relname LIKE 'pgbench%'
ORDER BY c.relname;

 esquema |      tabela      | linhas
---------+------------------+--------
 public  | pgbench_accounts | 100000
 public  | pgbench_branches |      1
 public  | pgbench_history  |      0
 public  | pgbench_tellers  |     10
(4 linhas)

Pode-se (e, para a maioria das finalidades, provavelmente deve-se) aumentar o número de linhas usando a opção -s (fator de escala). A opção -F (fator de preenchimento) também pode ser usada neste ponto.

Após fazer a configuração necessária, pode-se executar o teste de desempenho com um comando que não inclua -i, ou seja:

pgbench [ opções ] nome_do_banco_de_dados

Em quase todos os casos, serão necessárias algumas opções para fazer um teste útil. As opções mais importantes são -c (número de clientes), -t (número de transações), -T (limite de tempo), e -f (especifica um arquivo de script personalizado). Veja abaixo a lista completa.

Opções

O que segue está dividido em três subseções. São usadas opções diferentes durante a inicialização do banco de dados e durante a execução dos testes de desempenho, mas algumas opções são úteis nos dois casos.

Opções de inicialização

O utilitário pgbench aceita os seguintes argumentos de inicialização na linha de comando:

[-d] nome_do_banco_de_dados
[--dbname=]nome_do_banco_de_dados #

Especifica o nome do banco de dados para realizar os testes. Se não for especificado, será usada a variável de ambiente PGDATABASE. Se esta variável não estiver definida, será usado o nome de usuário especificado para a conexão.

-i
--initialize #

Necessário para chamar o modo de inicialização.

-I passos_iniciais
--init-steps=passos_iniciais #

Executa apenas o conjunto selecionado de etapas normais de inicialização. O valor de init_steps especifica as etapas de inicialização a serem executadas, usando um dos caracteres por etapa. Cada etapa é executada na ordem especificada. O valor padrão é dtgvp. As etapas disponíveis são:

d (Remove tabelas) #

Remove quaisquer tabelas do pgbench existentes.

t (Cria tabelas) #

Cria as tabelas utilizadas pelo cenário padrão do pgbench, isto é: pgbench_accounts, pgbench_branches, pgbench_history e pgbench_tellers.

g ou G (Gera os dados, no lado cliente ou no lado servidor) #

Gera os dados e os carrega nas tabelas padrão, substituindo quaisquer dados existentes.

Com a opção g (geração dos dados no lado cliente), os dados são gerados pelo pgbench no lado cliente e, em seguida, enviados para o servidor. Isto usa extensivamente a largura de banda cliente/servidor por meio de um comando COPY. No PostgreSQL versão 14 ou posterior, o pgbench usa a opção FREEZE para carregar dados em tabelas comuns (não particionadas) para acelerar a operação VACUUM subsequente. O uso da opção g faz com que seja mostrada uma mensagem a cada 100.000 linhas enquanto são gerados os dados para todas as tabelas.

Com a opção G (geração dos dados no lado servidor), são enviadas apenas pequenas consultas pelo cliente pgbench e os dados são gerados no servidor. Não é necessária nenhuma largura de banda significativa para esta variante, mas o servidor terá mais trabalho. O uso da opção G impede que sejam mostradas mensagens de progresso durante a geração dos dados.

O comportamento de inicialização padrão usa a geração dos dados no lado cliente (equivalente a g).

v (Vacuum/Limpeza) #

Chama o comando VACUUM para as tabelas padrão.

p (Cria chaves primárias) #

Cria índices de chave primária nas tabelas padrão.

f (Cria chaves estrangeiras) #

Cria restrições de chave estrangeira entre as tabelas padrão. (Note que esta etapa não é executada por padrão.)

-F fator_de_preenchimento
--fillfactor=fator_de_preenchimento #

Cria as tabelas pgbench_accounts, pgbench_tellers e pgbench_branches com o fator de preenchimento especificado. O valor padrão é 100.

-n
--no-vacuum #

Não efetua a limpeza durante a inicialização. (Esta opção suprime a etapa de inicialização v, mesmo que tenha sido especificada em -I.)

-q
--quiet #

Muda o registro para o modo silencioso, produzindo apenas uma mensagem de progresso a cada 5 segundos. O registro padrão mostra uma mensagem a cada 100.000 linhas, que geralmente gera muitas linhas por segundo (especialmente em um hardware potente).

Esta opção não terá efeito se for especificado G na opção -I.

-s fator_de_escala
--scale=fator_de_escala #

Multiplica o número de linhas geradas pelo fator de escala. Por exemplo, -s 100 irá criar 10.000.000 linhas na tabela pgbench_accounts. O valor padrão é 1. Quando o fator de escala for de 20.000, ou maior, as colunas usadas para conter identificadores de conta (colunas aid), passarão a usar números inteiros grandes (bigint), para serem grandes o suficiente para manter o intervalo de identificadores de conta.

--foreign-keys #

Cria restrições de chave estrangeira entre as tabelas padrão. (Esta opção irá adicionar a etapa f à sequência de etapas de inicialização, se ainda não estiver presente.)

--index-tablespace=espaço_de_tabelas_do_índice #

Cria os índices no espaço de tabela especificado, em vez de no espaço de tabela padrão.

--partition-method=NOME #

Crie a tabela pgbench_accounts particionada com o método NOME. Os valores esperados são range ou hash. Esta opção requer que a opção --partitions seja definida com um valor diferente de zero. Se não for especificado, o valor padrão é range.

--partitions=NUM #

Cria a tabela pgbench_accounts particionada com NUM partições de tamanho quase igual para o número especificado de contas. O valor padrão é 0, significando nenhum particionamento.

--tablespace=espaço_de_tabelas #

Cria as tabelas no espaço de tabelas especificado, em vez de no espaço de tabelas padrão.

--unlogged-tables #

Cria todas as tabelas como tabelas sem registro de transações (unlogged/temporárias), em vez de tabelas permanentes.

Opções de teste de desempenho

O utilitário pgbench aceita os seguintes argumentos na linha de comando para permitir testes de desempenho em diferentes cenários (benchmarking):

-b nome_do_script[@peso]
--builtin=nome_do_script[@peso] #

Adiciona o script integrado especificado à lista de scripts a serem executados. Os scripts integrados disponíveis são: tpcb-like [185], simple-update e select-only. São aceitos prefixos sem ambiguidade dos nomes de script integrado. Com o nome especial list, mostra a lista de scripts integrados, e termina imediatamente.

Opcionalmente, pode-se escrever um peso inteiro após o @ para ajustar a probabilidade de selecionar este script em relação a outros. O peso padrão é 1. Veja os detalhes abaixo.

-c clientes
--client=clientes #

Número de clientes simulados, ou seja, número de sessões de banco de dados concorrentes. O valor padrão é 1.

-C
--connect #

Estabelece uma nova conexão para cada transação, em vez de apenas uma vez por sessão do cliente. Serve para medir a sobrecarga da conexão.

-D nome_da_variável=valor
--define=nome_da_variável=valor #

Define uma variável para uso por um script personalizado (veja abaixo). É permitido haver várias opções -D.

-f nome_do_arquivo[@peso]
--file=nome_do_arquivo[@peso] #

Adiciona o script de transação nome_do_arquivo à lista de scripts a serem executados.

Opcionalmente, pode-se escrever um peso inteiro após o @ para ajustar a probabilidade de selecionar este script em relação a outros. O peso padrão é 1. (Para usar um nome de arquivo de script que inclua o caractere @, deve-se anexar um peso para não haver ambiguidade, por exemplo, filen@me@1.) Veja os detalhes abaixo.

-j threads
--jobs=threads #

Número de threads trabalhadoras (processos trabalhadores) no pgbench. O uso de mais de uma thread pode ser útil em máquinas com várias CPUs. Os clientes são distribuídos o mais uniformemente possível entre as threads disponíveis. O valor padrão é 1.

-l
--log #

Escreve informações sobre cada transação em um arquivo de registro. Veja os detalhes abaixo.

-L limite
--latency-limit=limite #

As transações que demoram mais de limite milissegundos são contadas e relatadas separadamente como late (atrasada).

Quando é especificada uma taxa (--rate=...), as transações que ficam atrasadas mais de limite ms e, portanto, não têm esperança de atingir o limite de latência, não são enviadas para o servidor. Elas são contadas e relatadas separadamente como skipped.

Quando é usada a opção --max-trys, uma transação que falha devido a uma anomalia de serialização ou a um impasse (deadlock) não será tentada novamente se o tempo total de todas as suas tentativas for maior que limite ms. Para limitar apenas o tempo das tentativas e não o seu número, deve ser usado --max-tries=0. Por padrão, a opção --max-tries é definida como 1 e as transações com erros de serialização/impasse não são repetidas Veja Falhas e novas tentativas de serialização/impasse para obter mais informações sobre como tentar novamente estas transações.

-M modo_da_consulta
--protocol=modo_da_consulta #

Protocolo a ser usado para enviar as consultas ao servidor:

  • simple: usa o protocolo de consulta simples.

  • extended: usa o protocolo de consulta estendido.

  • prepared: usa o protocolo de consulta estendido com instruções preparadas.

No modo prepared, o utilitário pgbench reutiliza o resultado da análise a partir da segunda iteração da consulta, de modo que o utilitário pgbench é executado mais rapidamente do que nos outros modos.

O padrão é o protocolo de consulta simples. (Veja Protocolo cliente/servidor para obter mais informações.)

-n
--no-vacuum #

Não efetua nenhuma limpeza antes de executar o teste. Esta opção é necessária se estiver sendo executado um contexto de teste personalizado que não inclua as tabelas padrão pgbench_accounts, pgbench_branches, pgbench_history e pgbench_tellers.

-N
--skip-some-updates #

Executa o script integrado de atualização simples. Atalho para -b simple-update.

-P sec
--progress=sec #

Mostra o relatório de progresso a cada sec segundos. O relatório inclui o tempo decorrido desde o início da execução, o TPS desde o último relatório, a latência média da transação, o desvio padrão e o número de transações com falha desde o último relatório. Com a limitação de taxa (-R), a latência é calculada em relação ao horário de início agendado da transação, e não ao horário real de início da transação; portanto, ela também inclui o tempo médio de atraso do agendamento. Quando é usada a opção --max-tries para ativar novas tentativas de transação após erros de serialização/impasse, o relatório inclui o número de transações repetidas e a soma de todas as novas tentativas.

-r
--report-per-command #

Relata as seguintes estatísticas para cada comando após a conclusão do teste de desempenho: a latência média por instrução (tempo de execução da perspectiva do cliente), o número de falhas e o número de novas tentativas após erros de serialização ou de impasse neste comando. O relatório irá mostrar estatísticas de novas tentativas somente se a opção --max-trys for diferente de 1.

-R razão
--rate=razão #

Executa as transações visando a taxa especificada, em vez de executar o mais rápido possível (o padrão). A taxa é dada em transações por segundo. Se a taxa especificada estiver acima da taxa máxima possível, o limite imposto pela taxa não afetará os resultados.

A taxa é definida iniciando as transações ao longo de uma linha do tempo escalonada segundo uma distribuição de Poisson. A programação de hora de início esperada avança com base em quando o cliente foi iniciado pela primeira vez, e não em quando a transação anterior foi encerrada. Esta abordagem significa que, se uma transação exceder sua hora de término programada, ainda será possível recuperar o atraso nas próximas transações.

Quando a taxa está ativa, a latência da transação informada no final da execução é calculada a partir das horas de início programadas, ou seja, inclui o tempo que cada transação teve que esperar até que a transação anterior fosse concluída. O tempo de espera é chamado de tempo de atraso do agendamento, e seus valores médio e máximo também são informados separadamente. A latência da transação em relação à hora real de início da transação, ou seja, o tempo gasto na execução da transação no banco de dados, pode ser calculada subtraindo o tempo de atraso do agendamento da latência informada.

Se a opção --latency-limit for usada com a opção --rate, uma transação pode ficar tão atrasada que já estaria acima do limite de latência quando a transação anterior terminar, porque a latência é calculada a partir da hora de início agendada. Estas transações não são enviadas ao servidor, sendo totalmente ignoradas e contadas separadamente.

Um tempo de atraso alto em relação ao agendado, é uma indicação de que o sistema não pode processar transações na taxa especificada, com o número escolhido de clientes e de processos. Quando o tempo médio de execução da transação for maior do que o intervalo esperado entre cada transação, cada transação sucessiva ficará mais para trás, e o tempo de atraso da programação continuará aumentando quanto mais longa for a execução do teste. Quando isto acontecer, será necessário reduzir a taxa de transação especificada.

-s fator_de_escala
--scale=fator_de_escala #

Relata o fator de escala especificado na saída do utilitário pgbench. Com os testes integrados, isto não é necessário; o fator de escala correto será detectado contando o número de linhas na tabela pgbench_branches. Entretanto, ao realizar apenas testes personalizados (opção -f), o fator de escala será relatado como 1, a menos que esta opção seja usada.

-S
--select-only #

Executa somente o script de seleção integrado. Atalho para -b select-only.

-t transações
--transactions=transações #

Número de transações que cada cliente executa. O valor padrão é 10.

-T segundos
--time=segundos #

Executa o teste por esta quantidade de segundos, em vez de pelo número fixado de transações por cliente. As opções -t e -T são mutuamente exclusivas.

-v
--vacuum-all #

Limpa todas as quatro tabelas padrão antes de executar o teste. Sem -n nem -v, o utilitário pgbench irá limpar as tabelas pgbench_tellers e pgbench_branches, e irá truncar a tabela pgbench_history.

--aggregate-interval=segundos #

Duração do intervalo de agregação (em segundos). Pode ser usado apenas com a opção -l. Com esta opção, o registro (log) conterá dados resumidos por intervalo, conforme descrito abaixo.

--exit-on-abort #

Sair imediatamente quando qualquer cliente for encerrado devido a algum erro. Sem esta opção, mesmo quando um cliente é encerrado, outros clientes poderiam continuar sua execução conforme especificado pela opção -t ou -T e, neste caso, pgbench iria mostrar resultados incompletos.

Note que falhas de serialização ou impasses não interrompem o cliente, portanto, não são afetados por esta opção. Veja Falhas e novas tentativas de serialização/impasse para obter mais informações.

--failures-detailed #

Relata falhas nos registros por transação e agregação, bem como nos relatórios principais e por script, agrupadas pelos seguintes tipos:

  • falhas de serialização;

  • falhas de impasse;

Veja Falhas e novas tentativas de serialização/impasse para obter mais informações.

--log-prefix=prefix #

Define o prefixo do nome de arquivo para os arquivos de registro criados pela opção --log. O valor padrão é pgbench_log.

--max-tries=número_de_tentativas #

Ativa novas tentativas para transações com erros de serialização/impasse e define o número máximo dessas tentativas. Esta opção pode ser combinada com a opção --latency-limit que limita o tempo total de todas as tentativas de transação; além disso, não se pode usar um número ilimitado de tentativas (--max-tries=0) sem --latency-limit ou --time. O valor padrão é 1 e as transações com erros de serialização/impasse não são repetidas. Veja Falhas e novas tentativas de serialização/impasse para obter mais informações sobre como tentar repetir novamente estas transações.

--progress-timestamp #

Ao mostrar o progresso (opção -P), usa um carimbo de data/hora (Unix time), em vez do número de segundos desde o início da execução. A unidade é em segundos, com precisão de milissegundos após o ponto. Isto ajuda a comparar registros gerados por várias ferramentas.

--random-seed=semente #

Define a semente que inicia o gerador de números aleatórios do sistema, que produzirá uma sequência de estados geradores iniciais, um para cada processo (thread). Os valores para a semente podem ser: time (o padrão, a semente é baseada na hora corrente), rand (usa uma fonte fortemente aleatória, falhando se nenhuma estiver disponível), ou um valor inteiro decimal sem sinal. O gerador aleatório é chamado explicitamente de um script pgbench (funções random...), ou implicitamente (por exemplo, a opção --rate usa para agendar transações). Quando é definido explicitamente, o valor usado para semente é mostrado no terminal. Qualquer valor permitido para semente também pode ser fornecido por meio da variável de ambiente PGBENCH_RANDOM_SEED. Para garantir que a semente fornecida impacte todos os usos possíveis, deve-se colocar esta opção primeiro, ou usar a variável de ambiente.

Definir a semente explicitamente permite reproduzir uma execução do utilitário pgbench exatamente idêntica, no que diz respeito a números aleatórios. Como o estado do gerador de números aleatórios é gerenciado por processo (thread), o pgbench será executado de forma idêntica, se houver um cliente por processo, e não houver dependências externas ou de dados. Do ponto de vista estatístico, a reprodução exata das execuções é uma má ideia, porque pode mascarar a variabilidade do desempenho, ou melhorar o desempenho indevidamente, por exemplo, acessando as mesmas páginas de uma execução anterior. Entretanto, também pode ser de grande ajuda para depuração, por exemplo, reexecutando um caso complicado que leva a um erro. Deve ser usado com sabedoria.

--sampling-rate=razão #

Taxa de amostragem, usada ao escrever dados no registro (log), para reduzir a quantidade de registro gerada. Se esta opção for fornecida, apenas a fração especificada de transações será registrada. 1.0 significa que todas as transações serão registradas, 0.05 significa que apenas 5% das transações serão registradas.

Deve-se lembrar levar em consideração a taxa de amostragem ao processar o arquivo de registro. Por exemplo, ao calcular valores de TPS, é necessário multiplicar os números de acordo (por exemplo, com uma taxa de amostragem de 0.01, será obtido apenas 1/100 do TPS real).

--show-script=nome_do_script #

Escreve o código real do script integrado nome_do_script na saída de erro padrão (stderr), e termina imediatamente.

--verbose-errors #

Mostra mensagens sobre todos os erros e falhas (erros sem novas tentativas), incluindo qual limite de novas tentativas foi excedido e em que medida este limite foi excedido para as falhas de serialização/impasse. (Note que, neste caso, a saída poderá ser significativamente aumentada.) Veja Falhas e novas tentativas de serialização/impasse para obter mais informações.

Opções comuns

O utilitário pgbench também aceita os seguintes argumentos de linha de comando comuns para parâmetros de conexão:

--debug #

Mostra saída de depuração.

-h nome_do_hospedeiro
--host=nome_do_hospedeiro #

O nome do hospedeiro do servidor de banco de dados

-p porta
--port=porta #

O número da porta do servidor de banco de dados

-U usuário
--username=usuário #

O nome de usuário para se conectar como

-V
--version #

Mostra a versão do utilitário pgbench, e termina.

-?
--help #

Mostra a ajuda sobre os argumentos da linha de comando do pgbench, e termina.

Código de saída

Uma execução bem-sucedida resultará no código de saída 0. O código de saída 1 indica problemas estáticos, como opções de linha de comando inválidas ou erros internos que não deveriam ocorrer. Erros iniciais que ocorrem ao iniciar o teste de desempenho, como falhas de conexão iniciais, também são encerrados com o código 1. Erros durante a execução, como erros de banco de dados ou problemas no script, resultarão no código de saída 2. Neste último caso, o pgbench irá relatar resultados parciais se não for especificada a opção --exit-on-abort.

Variáveis de ambiente

PGDATABASE
PGHOST
PGPORT
PGUSER

Parâmetros de conexão padrão.

Este utilitário, como a maioria dos outros utilitários do PostgreSQL, também usa as variáveis de ambiente com suporte pela libpq (veja Variáveis ​​de ambiente).

A variável de ambiente PG_COLOR especifica se devem ser usadas cores nas mensagens de diagnóstico. Os valores possíveis são always, auto e never.

Notas

Qual é a transação realmente executada pelo pgbench?

O utilitário pgbench executa scripts de teste escolhidos aleatoriamente de uma lista especificada. Os scripts podem incluir scripts integrados especificados com a opção -b, e scripts fornecidos pelo usuário especificados coma opção -f. Cada script pode receber um peso relativo especificado após o @ para alterar sua probabilidade de seleção. O peso padrão é 1. Os scripts com peso 0 são ignorados.

O script de transação integrado padrão (também chamados com a opção -b tpcb-like) executa sete comandos por transação escolhidos aleatoriamente entre aid, tid, bid, e delta. O contexto é inspirado no teste de desempenho TPC-B, mas como, na verdade, não é o TPC-B, daí vem seu nome.

  1. BEGIN;

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

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

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

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

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

  7. END;

Se for selecionado o script integrado simple-update (também a opção -N), as etapas 4 e 5 não serão incluídas na transação. Isto evita a contenção de atualização nessas tabelas, mas torna o caso de teste ainda menos parecido com o TPC-B.

Se for selecionado o script integrado select-only (também a opção -S), será executado apenas o SELECT.

Scripts personalizados

O utilitário pgbench oferece suporte para a execução de contexto de testes de desempenho personalizados, substituindo o script de transação padrão (descrito acima) por um script de transação lido de um arquivo (opção -f). Neste caso, uma transação conta como a execução de um arquivo de script.

O arquivo de script contém um ou mais comandos SQL terminados por ponto e vírgula. As linhas vazias, e as linhas começando com --, são ignoradas. Arquivos de script também podem conter metacomandos, interpretados pelo próprio utilitário pgbench, conforme descrito abaixo.

Nota

Antes do PostgreSQL 9.6, os comandos SQL em arquivos de script eram finalizados pelo caractere de nova-linha e, portanto, não podiam ser continuados entre as linhas. Agora é necessário o ponto e vírgula para separar comandos SQL consecutivos (embora o comando SQL não precise dele, se for seguido por um metacomando). Se for necessário criar um arquivo de script que funcione com as versões antiga e nova do utilitário pgbench, certifique-se de escrever cada comando SQL em uma única linha terminando com ponto e vírgula.

É assumido que os scripts do pgbench não contêm blocos incompletos de transações SQL. Se, durante a execução, o cliente chegar ao final do script sem concluir o último bloco de transação, a operação será cancelada.

Existe um recurso simples de substituição de variável para arquivos de script. Os nomes das variáveis devem consistir em letras (incluindo letras não latinas), dígitos e sublinhados, com o primeiro caractere não sendo um dígito. As variáveis podem ser definidas pela opção -D da linha de comando, explicada acima, ou pelos metacomandos explicados abaixo. Além de quaisquer variáveis predefinidas pelas opções de linha de comando -D, existem algumas variáveis predefinidas automaticamente, listadas na Tabela 301. Um valor especificado para estas variáveis usando a opção -D tem precedência sobre as predefinições automáticas. Uma vez definido, o valor de uma variável pode ser inserido em um comando SQL escrevendo :nome_da_variável. Ao executar mais de uma sessão cliente, cada sessão possui seu próprio conjunto de variáveis. O utilitário pgbench oferece suporte a até 255 usos de variáveis em uma instrução.

Tabela 301. Variáveis automáticas do pgbench

VariávelDescrição
client_id número único que identifica a sessão do cliente (começa em zero)
default_seed semente usada por padrão em funções de permutação hash e pseudo-aleatória
random_seed semente do gerador de números aleatórios (a menos que seja sobreposto pela opção -D)
scale fator de escala corrente

Os metacomandos do arquivo de script começam com uma contrabarra (\), e normalmente se estendem até o final da linha, embora possam ser continuados em linhas adicionais escrevendo contrabarra-retorno. Os argumentos para o metacomando são separados por espaços em branco. Os seguintes metacomandos têm suporte:

\gset [prefix] \aset [prefix] #

Estes comandos podem ser usados para finalizar instruções SQL, substituindo o ponto e vírgula (;) final.

Quando é usado o metacomando \gset, espera-se que a instrução SQL anterior retorne uma linha, cujas colunas serão armazenadas nas variáveis indicadas após os nomes das colunas, e prefixadas com o prefixo, se fornecido.

Quando é usado o metacomando \aset, todas as instruções SQL combinadas (separadas por \;) têm suas colunas armazenadas nas variáveis indicadas após os nomes das colunas, e prefixadas com o prefixo, se fornecido. Se a instrução não retornar nenhuma linha, nenhuma atribuição será feita, e a variável poderá ser testada quanto à existência para detectar isto. Se a instrução retornar mais de uma linha, será mantido o último valor.

Os metacomandos \gset e \aset não podem ser usados no modo encadeado (|), porque os resultados da instrução ainda não estarão disponíveis no momento em que os comandos vão precisar deles.

O exemplo a seguir coloca o saldo final da conta da primeira instrução na variável abalance, e preenche as variáveis p_two e p_three com valores inteiros da terceira instrução. O resultado da segunda instrução é descartado. O resultado combinado das duas últimas instruções são armazenados nas variáveis four e five.

UPDATE pgbench_accounts
  SET abalance = abalance + :delta
  WHERE aid = :aid
  RETURNING abalance \gset
-- composto de duas consultas
SELECT 1 \;
SELECT 2 AS two, 3 AS three \gset p_
SELECT 4 AS four \; SELECT 5 AS five \aset
\if expressão
\elif expressão
\else
\endif #

Este grupo de comandos implementa blocos condicionais aninhados, similares ao \if expressão do psql. As expressões condicionais são idênticas àquelas com \set, com valores diferentes de zero interpretados como verdade.

\set nome_da_variável expressão #

Define a variável nome_da_variável para o valor calculado a partir de expressão. A expressão pode conter a constante NULL, constantes booleanas TRUE e FALSE, constantes inteiras como 5432, constantes de precisão dupla como 3.14159, referências a variáveis :nome_da_variável, operadores com sua precedência e associatividade SQL usuais, chamadas de função, expressões condicionais genéricas SQL CASE, e parênteses.

As funções, e a maioria dos operadores, retornam NULL na entrada NULL.

Para fins de condição, os valores numéricos diferentes de zero são TRUE, os valores numéricos zero e NULL são FALSE.

Constantes inteiras e de dupla precisão muito grandes ou muito pequenas, bem como operadores aritméticos inteiros (+, -, * e /), geram erros de estouro (overflow).

Quando não é fornecida nenhuma cláusula final ELSE para o CASE, o valor padrão é NULL.

Exemplos:

\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 número [ us | ms | s ] #

Faz com que a execução do script seja suspensa pela duração especificada em microssegundos (us), milissegundos (ms), ou segundos (s). Se for omitida a unidade, por padrão será segundos. O número pode ser uma constante inteira, ou uma referência :nome_da_variável a uma variável com um valor inteiro.

Exemplo:

\sleep 10 ms
\setshell nome_da_variável comando [ argumento ... ] #

Define a variável nome_da_variável como o resultado do comando do interpretador de comandos, com o(s) argumento(s) fornecido(s). O comando deve retornar um valor inteiro por meio da saída padrão.

O comando e cada argumento pode ser uma constante de texto, ou uma referência :nome_da_variável a uma variável. Se for desejado usar um argumento começando com dois pontos, deve-se escrever dois pontos adicionais no início do argumento.

Exemplo:

\setshell variável_a_ser_atribuída comando argumento_literal :variável ↵
          ::literal_começando_com_dois_pontos
\shell comando [ argumento ... ] #

O mesmo que \setshell, mas o resultado do comando é descartado.

Exemplo:

\shell comando argumento_literal :variável ::literal_começando_com_dois_pontos
\startpipeline
\syncpipeline
\endpipeline #

Este grupo de comandos implementa um fluxo (pipeline) de instruções SQL. O fluxo deve começar com \startpipeline e terminar com \endpipeline. Entre elas pode haver qualquer número de comandos \syncpipeline que enviam uma mensagem de sincronização sem encerrar o fluxo de dados em andamento e descarregando o buffer de envio. No modo de fluxo, as instruções são enviadas ao servidor sem esperar pelos resultados das instruções anteriores. Veja Pipeline Mode para obter mais informações. O modo fluxo requer o uso de um protocolo de consulta estendido.

Operadores integrados

Os operadores aritméticos, bit a bit, de comparação, e lógicos, listados na Tabela 302, são integrados ao utilitário pgbench, podendo ser usados nas expressões que aparecem em \set nome_da_variável expressão . Os operadores são listados na ordem crescente de precedência. Exceto onde indicado, os operadores que usam duas entradas numéricas produzirão um resultado de precisão dupla se uma das entradas for de precisão dupla, caso contrário, produzirão um resultado inteiro.

Tabela 302. Operadores do pgbench

Operador

Descrição

Exemplo(s)

booleano OR booleanobooleano

OU lógico

5 or 0TRUE

booleano AND booleanobooleano

E lógico

3 and 0FALSE

NOT booleanobooleano

NÃO lógico

not falseTRUE

booleano IS [NOT] (NULL|TRUE|FALSE)booleano

Testes de valor booleano

1 is nullFALSE

valor ISNULL|NOTNULLbooleano

Testes de valor nulo

1 notnullTRUE

número = númerobooleano

Igual

5 = 4FALSE

número <> númerobooleano

Não igual

5 <> 4TRUE

número != númerobooleano

Não igual

5 != 5FALSE

número < númerobooleano

Menor que

5 < 4FALSE

número <= númerobooleano

Menor que, ou igual a

5 <= 4FALSE

número > númerobooleano

Maior que

5 > 4TRUE

número >= númerobooleano

Maior que, ou igual a

5 >= 4TRUE

inteiro | inteirointeiro

OU bit a bit

1 | 23

inteiro # inteirointeiro

OU exclusivo (XOR) bit a bit

1 # 32

inteiro & inteirointeiro

E bit a bit

1 & 31

~ inteirointeiro

NÃO bit a bit

~ 1-2

inteiro << inteirointeiro

Deslocamento bit a bit para a esquerda

1 << 24

inteiro >> inteirointeiro

Deslocamento bit a bit para a direita

8 >> 22

número + númeronúmero

Adição

5 + 49

número - númeronúmero

Subtração

3 - 2.01.0

número * númeronúmero

Multiplicação

5 * 420

número / númeronúmero

Divisão (trunca o resultado em direção a zero se as duas entradas forem números inteiros)

5 / 31

inteiro % inteirointeiro

Módulo (resto)

3 % 21

- númeronúmero

Negação

- 2.0-2.0


Funções integradas

As funções listadas na Tabela 303 são integradas ao pgbench, podendo ser usadas em expressões que aparecem em \set nome_da_variável expressão .

Tabela 303. Funções de pgbench

Função

Descrição

Exemplo(s)

abs ( número ) → mesmo tipo de dados da entrada

Valor absoluto

abs(-17)17

debug ( número ) → mesmo tipo de dados da entrada

Escreve o argumento na saída de erro padrão (stderr), e retorna o argumento.

debug(5432.1)5432.1

double ( número ) → double

Converte para precisão dupla

double(5432)5432.0

exp ( número ) → double

Exponencial (e elevado à potência fornecida)

exp(1.0)2.718281828459045

greatest ( número [, ... ] ) → double, se algum argumento for de precisão dupla, senão integer

Seleciona o maior valor entre os argumentos.

greatest(5, 4, 3, 2)5

hash ( valor [, semente ] ) → integer

Esta função é um alias para a função hash_murmur2.

hash(10, 5432)-5817877081768721676

hash_fnv1a ( valor [, semente ] ) → integer

Computa FNV-1a hash.

hash_fnv1a(10, 5432)-7793829335365542153

hash_murmur2 ( valor [, semente ] ) → integer

Computa MurmurHash2 hash.

hash_murmur2(10, 5432)-5817877081768721676

int ( número ) → integer

Converte em inteiro.

int(5.4 + 3.8)9

least ( número [, ... ] ) → double se algum argumento for de precisão dupla, senão integer

Seleciona o menor valor entre os argumentos.

least(5, 4, 3, 2.1)2.1

ln ( número ) → double

Logaritmo natural

ln(2.718281828459045)1.0

mod ( inteiro, inteiro ) → integer

Módulo (resto)

mod(54, 32)22

permute ( i, tamanho [, semente ] ) → integer

Valor permutado de i, no intervalo [0, tamanho). Esta é a nova posição de i (módulo tamanho), em uma permutação pseudoaleatória de números inteiros 0...tamanho-1, parametrizada por semente, veja abaixo.

permute(0, 4)um número inteiro entre 0 e 3

pi () → double

Valor aproximado de π;

pi()3.14159265358979323846

pow ( x, y ) → double

power ( x, y ) → double

x elevado à potência y

pow(2.0, 10)1024.0

random ( lb, ub ) → integer

Computa um número inteiro aleatório uniformemente distribuído no intervalo [lb, ub].

random(1, 10)um número inteiro entre 1 e 10

random_exponential ( lb, ub, parâmetro ) → integer

Computa um número inteiro aleatório distribuído exponencialmente no intervalo [lb, ub], veja abaixo.

random_exponential(1, 10, 3.0)um número inteiro entre 1 e 10

random_gaussian ( lb, ub, parâmetro ) → inteiro

Computa um número inteiro aleatório numa distribuição Gaussiana no intervalo [lb, ub], veja abaixo.

random_gaussian(1, 10, 2.5)um número inteiro entre 1 e 10

random_zipfian ( lb, ub, parâmetro ) → integer

Calcula um número inteiro aleatório distribuído segundo a Lei de Zipf no intervalo [lb, ub], veja abaixo.

random_zipfian(1, 10, 1.5)um número inteiro entre 1 e 10

sqrt ( número ) → um número de precisão dupla

Raiz quadrada

sqrt(2.0)1.414213562


A função random gera valores usando uma distribuição uniforme, ou seja, todos os valores são distribuídos dentro do intervalo especificado com igual probabilidade. As funções random_exponential, random_gaussian e random_zipfian requerem um parâmetro adicional de precisão dupla, que determina a forma precisa da distribuição.

  • Para uma distribuição exponencial, o parâmetro controla a distribuição, truncando uma distribuição exponencial decrescente rapidamente no parâmetro e, em seguida, fazendo a projeção dos resultados em números inteiros entre os limites. Para ser preciso, usando

    f(x) = exp(-parâmetro * (x - min) / (max - min + 1)) / (1 - exp(-parâmetro))
    

    Então o valor i, entre min e max inclusive, é recuperado com a probabilidade: f(i) - f(i + 1).

    Intuitivamente, quanto maior for o parâmetro, serão acessados com maior frequência os valores próximos a min, e serão acessados menor frequência os valores próximos a max. Quanto mais próximo de zero estiver o parâmetro, mais plana (mais uniforme) será a distribuição de acesso. Uma aproximação grosseira da distribuição é que 1% dos valores mais frequentes no intervalo, próximos a min, são selecionados parâmetro% das vezes. O valor do parâmetro deve ser estritamente positivo.

  • Para uma distribuição Gaussiana, o intervalo corresponde a uma distribuição normal padrão (a curva Gaussiana clássica em forma de sino), truncada em -parâmetro à esquerda, e +parâmetro à direita. Valores no meio do intervalo têm maior probabilidade de serem selecionados. Para ser preciso, se PHI(x) for a função de distribuição cumulativa da distribuição normal padrão, com a média mu definida como (max + min) / 2,0, com

    f(x) = PHI(2.0 * parâmetro * (x - mu) / (max - min + 1)) /
           (2.0 * PHI(parâmetro) - 1)
    

    então o valor i, entre min e max inclusive, é selecionado com probabilidade: f(i + 0.5) - f(i - 0.5). Intuitivamente, quanto maior for o parâmetro, mais frequentemente serão selecionados valores próximos ao meio do intervalo, e menos frequentemente os valores próximos aos limites min e max. Cerca de 67% dos valores serão selecionados a partir do centro 1.0 / parâmetro, ou seja, 0,5 / parâmetro em torno da média, e 95% no centro 2.0 / parâmetro, ou 1.0 / parâmetro em torno da média; por exemplo, se parâmetro for 4.0, 67% dos valores serão selecionados do quarto do meio (1,0 / 4,0) do intervalo (ou seja, de 3.0 / 8.0 a 5.0 / 8.0), e 95% da metade intermediária (2.0 / 4.0) do intervalo (segundo e terceiro quartis). O valor mínimo permitido para o parâmetro é 2.0.

  • A função random_zipfian gera uma distribuição com limites segundo a lei de Zipf. O parâmetro define o quão distorcida é a distribuição. Quanto maior for o parâmetro, mais frequentemente serão escolhidos os valores mais próximos do início do intervalo A distribuição é tal que, assumindo que o intervalo comece em 1, a razão da probabilidade de selecionar k contra selecionar k+1 é ((k+1)/k)**parâmetro. Por exemplo, random_zipfian(1, ..., 2.5) produz o valor 1 cerca de (2/1)**2.5 = 5.66 vezes mais frequentemente do que 2, que é produzido (3/2)**2.5 = 2.76 vezes mais frequentemente do que 3, e assim por diante.

    A implementação do pgbench é baseada em Non-Uniform Random Variate Generation, Luc Devroye, p. 550-551, Springer 1986. Devido às limitações desse algoritmo, o valor do parâmetro está restrito ao intervalo [1.001, 1000].

Nota

Ao projetar um teste de desempenho que seleciona linhas de maneira não uniforme, deve-se estar ciente de que as linhas selecionadas podem estar correlacionadas com outros dados, como identificadores de sequência, ou a ordem física da linha, o que pode distorcer as medições de desempenho.

Para evitar esta situação, pode-se desejar usar a função permute, ou alguma outra etapa adicional com efeito semelhante, para embaralhar as linhas selecionadas, e remover estas correlações.

As funções de hash: hash, hash_murmur2 e hash_fnv1a, aceitam um valor de entrada e um parâmetro de semente opcional. Caso a semente não seja fornecida, é usado o valor de :default_seed, que é inicializado aleatoriamente, a menos que seja definido pela opção -D da linha de comando.

A função permute aceita um valor de entrada, um tamanho e um parâmetro de semente opcional. Ela gera uma permutação pseudo-aleatória de inteiros no intervalo [0, tamanho), e retorna o índice do valor de entrada nos valores permutados. A permutação escolhida é parametrizada pela semente, cujo valor padrão é :default_seed, se não for especificado. Ao contrário das funções de hash, a função permute garante que não haja colisões ou buracos nos valores de saída. Valores de entrada fora do intervalo são interpretados módulo o tamanho. A função gera um erro se o tamanho não for positivo. A função permute pode ser usada para dispersar a distribuição de funções aleatórias não uniformes, como random_zipfian ou random_exponential, para que os valores selecionados com mais frequência não estejam trivialmente correlacionados. Por exemplo, o script para o pgbench a seguir simula uma possível carga de trabalho do mundo real, típica para mídias sociais e plataformas de blog, onde algumas contas geram carga excessiva:

\set size 1000000
\set r random_zipfian(1, :size, 1.07)
\set k 1 + permute(:r, :size)

Em alguns casos, são necessárias várias distribuições distintas que não se correlacionam entre si, e é aí que o parâmetro semente opcional é útil:

\set k1 1 + permute(:r, :size, :default_seed + 123)
\set k2 1 + permute(:r, :size, :default_seed + 321)

Um comportamento semelhante também pode ser aproximado com a função hash:

\set size 1000000
\set r random_zipfian(1, 100 * :size, 1.07)
\set k 1 + abs(hash(:r)) % :size

Entretanto, como a função hash gera colisões, alguns valores não serão alcançáveis, e outros serão mais frequentes do que o esperado na distribuição original.

Como exemplo, a definição completa da transação integrada do tipo TPC-B é:

\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;

Este script permite que cada iteração da transação faça referência a diferentes linhas escolhidas aleatoriamente. (Este exemplo também mostra por que é importante que cada sessão cliente tenha suas próprias variáveis — caso contrário, elas não estariam tocando linhas diferentes de forma independente.)

Registro por transação

Com a opção -l (mas sem a opção --aggregate-interval), o utilitário pgbench escreve informações sobre cada transação em um arquivo de registro (log). O arquivo de registro terá o nome prefixo.nnn, onde prefixo, por padrão, é pgbench_log, e nnn é o PID do processo pgbench. O prefixo pode ser alterado usando a opção --log-prefix. Se a opção -j for igual a 2, ou superior, de modo que haja vários processos trabalhadores (threads), cada um terá seu próprio arquivo de registro. O primeiro processo trabalhador usará o mesmo nome para seu arquivo de registro, como no processo único padrão. Os arquivos de registro adicionais para os demais processos trabalhadores terão os nomes prefixo.nnn.mmm, onde mmm é um número sequencial para cada processo trabalhador começando com 1.

Cada linha em um arquivo de registro descreve uma transação. Ela contém os seguintes campos separados por espaços:

client_id

identifica a sessão do cliente que executou a transação

transaction_no

conta quantas transações foram executadas por esta sessão

time

tempo decorrido da transação, em microssegundos

script_no

identifica o arquivo de script usado para a transação (útil quando vários scripts são especificados com -f ou -b)

time_epoch

tempo de conclusão da transação, em formato de carimbo de data e hora Unix-epoch

time_us

fração de segundo do tempo de conclusão da transação, em microssegundos

schedule_lag

o atraso no início da transação, que é a diferença entre a hora de início da transação programada e a hora em que ela realmente começou, em microssegundos (presente somente se for especificado --rate)

retries

contagem de novas tentativas após erros de serialização ou de impasse durante a transação (presente apenas se --max-trys for diferente de um.)

Quando são usadas as duas opções --rate e --latency-limit, o tempo para uma transação ignorada será relatado como skipped. Se a transação terminar com falha, seu tempo será relatado como failed. Se for usada a opção --failures-detailed, o tempo da transação que falhou será relatado como serialization ou deadlock dependendo do tipo de falha (veja Falhas e novas tentativas de serialização/impasse para obter mais informações).

A seguir está um trecho de um arquivo de registro gerado em uma execução de um único 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

Outro exemplo com --rate=100 e --latency-limit=5 (note a coluna 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

Neste exemplo, a transação 82 estava atrasada, porque sua latência (6,173 ms) estava acima do limite de 5 ms. As duas transações seguintes foram ignoradas, porque já estavam atrasadas antes mesmo de serem iniciadas.

O exemplo a seguir mostra um trecho de um arquivo de registro com falhas e novas tentativas, com o número máximo de novas tentativas definido como 10. (note a coluna 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

Se for usada a opção --failures-detailed, o tipo de falha será relatado no time desta forma:

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

Ao executar um teste longo em hardware capaz de lidar com muitas transações, os arquivos de registro podem ficar muito grandes. Pode ser usada a opção --sampling-rate para registrar apenas uma amostra aleatória de transações.

Registro agregado

Com a opção --aggregate-interval, é usado um formato diferente para os arquivos de registro. Cada linha do registro descreve um intervalo de agregação. O registro contém os seguintes campos separados por espaços:

interval_start

hora de início do intervalo, no formato de carimbo de data e hora Unix-epoch

num_transactions

número de transações dentro do intervalo

sum_latency

soma das latências de transação

sum_latency_2

soma dos quadrados das latências de transação

min_latency

latência mínima de transação

max_latency

latência máxima de transação

sum_lag

soma dos atrasos de início das transações (zero, a menos que seja especificado --rate)

sum_lag_2

soma dos quadrados dos atrasos de início das transações (zero, a menos que seja especificado --rate)

min_lag

atraso mínimo de início de transação (zero, a menos que seja especificado --rate)

max_lag

atraso máximo de início de transação (zero, a menos que seja especificado --rate)

skipped

número de transações ignoradas, porque teriam começado muito tarde. (zero, a menos que seja especificado --rate e --latency-limit)

retried

número de transações tentadas novamente (zero, a menos que --max-tries não seja igual a 1)

retries

número de novas tentativas após erros de serialização ou impasse (zero, a menos que --max-tries não seja igual a 1)

serialization_failures

número de transações que apresentaram erro de serialização e não foram tentadas novamente posteriormente (zero, a menos que seja especificados --failures-detailed)

deadlock_failures

número de transações que apresentaram erro de impasse e não foram tentadas novamente posteriormente (zero, a menos que seja especificados --failures-detailed)

A seguir está um exemplo de saída gerada com esta opção:

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

Note que, enquanto o formato de registro simples (não agregado) mostra qual script foi usado para cada transação, o formato agregado não o faz. Portanto, se houver necessidade de obter dados por script, será necessário agregar os dados por conta própria.

Relatório por instrução

Com a opção -r, o pgbench coleta as seguintes estatísticas para cada instrução:

O relatório irá mostrar as estatísticas de novas tentativas somente se a opção --max-trys for diferente de 1.

Todos os valores são calculados para cada instrução executada por cada cliente e são relatados após a conclusão do teste de desempenho.

Para o script padrão, a saída será semelhante a esta:

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;

Outro exemplo de saída para o script padrão usando o nível de isolamento de transação padrão serializável (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;

Se forem especificados vários arquivos de script, todas as estatísticas serão relatadas separadamente para cada arquivo de script.

Note que a coleta das informações adicionais de tempo necessárias para o cálculo da latência por instrução adiciona alguma sobrecarga. Isto reduzirá a velocidade média de execução e diminuirá o TPS calculado. O grau de lentidão varia significativamente dependendo da plataforma e do hardware. Comparar os valores médios de TPS com e sem o relatório de latência ativado é uma boa maneira de medir se a sobrecarga de tempo é significativa.

Falhas e novas tentativas de serialização/impasse

Ao executar o utilitário pgbench existem três tipos principais de erros:

  • Erros do programa principal. Estes são os mais graves e sempre resultam em uma saída imediata do pgbench com a mensagem de erro correspondente. Estão incluídos:

    • erros no início de pgbench (por exemplo, um valor de opção inválido);

    • erros no modo de inicialização (por exemplo, a consulta para criar tabelas nos scripts integrados falha);

    • erros antes de iniciar os processos (por exemplo, não foi possível conectar ao servidor de banco de dados, erro de sintaxe no comando meta, falha na criação de processo);

    • erros internos do pgbench (que supostamente nunca deveriam ocorrer...).

  • Erros ocorrem quando o processo gerencia seus clientes. (por exemplo, o cliente não conseguiu iniciar uma conexão com o servidor de banco de dados / o soquete para conectar o cliente ao servidor de banco de dados tornou-se inválido). Nesses casos, todos os clientes desse processo param de funcionar, enquanto os outros processos continuam a operar. Entretanto, se for especificada a opção --exit-on-abort, todos os processos serão interrompidos imediatamente neste caso.

  • Erros diretos do cliente. Estes erros levam à saída imediata do utilitário pgbech com a mensagem de erro correspondente no caso de um erro interno do pgbench (que supostamente nunca deveriam ocorrer...) ou quando é especificado --exit-on-abort. Caso contrário, na pior das hipóteses, levam apenas à interrupção do cliente que falhou, enquanto os outros clientes continuam sua execução. (mas alguns erros do cliente são tratados sem a interrupção do cliente e relatados separadamente, veja abaixo.). Mais adiante nesta seção, assume-se que os erros discutidos são apenas erros diretos do cliente e não erros internos do pgbench.

A execução de um cliente é interrompida em caso de erro grave; por exemplo, a conexão com o servidor de banco de dados foi perdida ou o script chegou ao fim sem que a última transação fosse concluída. Além disso, se a execução de um comando SQL ou meta comando falhar por motivos que não sejam erros de serialização ou impasse, o cliente será encerrado. Caso contrário, se um comando SQL falhar com erros de serialização ou impasse, o cliente não será interrompido. Nesses casos, a transação corrente é desfeita, o que também inclui redefinir as variáveis ​​do cliente para seus valores anteriores à execução dessa transação (se presume que um script de transação contenha apenas uma transação; veja Qual é a "transação" realmente executada no pgbench? para obter mais informações). As transações com erros de serialização ou impasse são tentadas novamente após serem desfeitas, até serem concluídas com sucesso ou atingirem o número máximo de novas tentativas (especificado pela opção --max-tries) / o tempo máximo de novas tentativas (especificado pela opção --latency-limit) / o fim do tempo (especificado pela opção --time). Se a última tentativa falhar, esta transação será relatada como falhada, mas o cliente não será encerrado e continuará funcionando.

Nota

Se não for especificada a opção --max-tries, uma transação nunca será tentada novamente após um erro de serialização ou de impasse, porque seu valor padrão é 1. Deve ser usado um número ilimitado de tentativas (--max-tries=0) juntamente com a opção --latency-limit para limitar apenas o tempo máximo de tentativas. Também pode ser usada a opção --time para limitar a duração do teste de desempenho sob um número ilimitado de tentativas.

Tenha cuidado ao repetir scripts que contenham várias transações: o script é sempre tentado novamente integralmente, fazendo com que transações bem-sucedidas sejam executadas diversas vezes.

Tenha cuidado ao repetir transações com comandos do interpretador de comandos (shell). Ao contrário dos resultados dos comandos SQL, os resultados dos comandos do interpretador de comandos não são revertidos, exceto pelo valor da variável do comando \setshell.

A latência de uma transação bem-sucedida inclui todo o tempo de execução da transação, incluindo reversões e novas tentativas. A latência é medida apenas para transações e comandos bem-sucedidos, e não para transações ou comandos com falha.

O relatório principal contém o número de transações com falha. Se a opção --max-tries não for igual a 1, o relatório principal também conterá estatísticas relacionadas a novas tentativas: o número total de transações tentadas novamente e o número total de novas tentativas. O relatório por script herda estes campos do relatório principal. O relatório por instrução irá mostrar estatísticas de novas tentativas somente se a opção --max-trys for diferente de 1.

Se for desejado agrupar as falhas por tipos básicos em registros por transação e agregação, bem como nos relatórios principais e por script, deve ser usada a opção --failures-detailed. Se também for desejado distinguir todos os erros e falhas (erros sem nova tentativa) por tipo, incluindo qual limite de novas tentativas foi excedido e em quanto foi excedido para as falhas de serialização/impasse, deve ser usada a opção --verbose-errors.

Métodos de acesso à tabela

Pode ser especificado o Método de acesso à tabela para as tabelas do pgbench. A variável de ambiente PGOPTIONS especifica as opções de configuração do banco de dados passadas para o PostgreSQL através da linha de comando. (Veja Interação com parâmetros via linha de comando). Por exemplo, um hipotético método de acesso à tabela padrão para as tabelas que o pgbench cria, chamado wuzza, pode ser especificado usando:

PGOPTIONS='-c default_table_access_method=wuzza'

Boas práticas

É muito fácil usar o utilitário pgbench para produzir números inteiramente sem sentido. Aqui estão algumas diretrizes para auxiliar na obtenção de resultados úteis.

Em primeiro lugar, nunca se deve acreditar em qualquer teste que dure apenas alguns segundos. Deve ser usada a opção -t ou -T para fazer a execução durar pelo menos alguns minutos, de modo a reduzir o ruído. Em alguns casos, podem ser necessárias horas para obter números reproduzíveis. É uma boa ideia fazer o teste algumas vezes, para descobrir se os números são reprodutíveis ou não.

Para o cenário de teste padrão do tipo TPC-B, o fator de escala de inicialização (-s) deve ser pelo menos tão grande quanto o número máximo de clientes que se pretende testar (-c); caso contrário, se estará medindo principalmente a contenção induzida por atualizações. Como existem apenas -s linhas na tabela pgbench_branches, e cada transação deseja atualizar uma dessas linhas, então se o valor de -c for maior que o valor de -s certamente resultará em muitas transações bloqueadas aguardando a conclusão de outras transações.

O contexto de teste padrão também é bastante sensível a quanto tempo se passou desde que as tabelas foram inicializadas: o acúmulo de linhas mortas e espaço morto nas tabelas altera os resultados. Para entender os resultados, deve-se acompanhar o número total de atualizações e quando ocorre a limpeza. Se o autovacuum estiver ativado, pode resultar em alterações imprevisíveis no desempenho medido.

Uma limitação do pgbench é que ele pode se tornar um gargalo ao tentar testar muitas sessões clientes. Isto pode ser aliviado executando pgbench em uma máquina diferente do servidor de banco de dados, embora uma baixa latência de rede seja essencial. Pode até ser útil executar várias instâncias do pgbench concorrentemente em várias máquinas cliente, no mesmo servidor de banco de dados.

Segurança

Se usuários não confiáveis tiverem acesso a um banco de dados que não adotou um padrão de uso de esquema seguro, não se deve executar o pgbench neste banco de dados. O utilitário pgbench usa nomes não qualificados, e não trata o caminho de procura.



[185] O TPC-B mede a capacidade de processamento em termos de quantas transações por segundo um sistema consegue realizar. TPC-B (N. T.)