Перейти к основному содержимому

pgbench

примечание

Эта страница переведена при помощи нейросети GigaChat.

pgbench — запускает тест производительности для PostgreSQL.

Синтаксис

pgbench -i [option...] [dbname]

pgbench [option...] [dbname]

Описание

pgbench — утилита для проведения нагрузочного тестирования PostgreSQL. Она многократно выполняет заданную последовательность SQL-команд, возможно, в нескольких параллельных соединениях, и рассчитывает среднюю производительность в виде транзакций в секунду.

По умолчанию pgbench использует сценарий, основанный на модели TPC-B, в котором каждая транзакция включает пять команд SELECT, UPDATE и INSERT. Тем не менее, можно протестировать любые другие сценарии, создав собственные скрипты транзакций.

Пример типичного вывода pgbench:

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)

Первые семь строк вывода содержат ключевые параметры, использованные в тесте. Шестая строка указывает максимальное число повторных попыток выполнения транзакций, если произошли ошибки сериализации или взаимоблокировки (подробнее об этом в разделе «Сбои и повторные попытки сериализации/мертвых блокировок»).

Восьмая строка показывает, сколько транзакций было завершено и сколько планировалось выполнить (рассчитывается как произведение числа клиентов на число транзакций на клиента). Эти значения совпадают, если тест не был прерван и все SQL-команды были успешно выполнены. В случае запуска в режиме -T отображается только фактическое количество завершенных транзакций.

Следующая строка отображает число транзакций, завершившихся с ошибкой из-за проблем с сериализацией или взаимоблокировками. Последняя строка содержит показатель производительности — число транзакций, обработанных в секунду (TPS).

Подготовка базы данных

Чтобы выполнить стандартный тест, основанный на TPC-B, необходимо предварительно создать и заполнить определенные таблицы. Это делается с помощью команды pgbench с параметром -i, который инициализирует тестовую схему. При использовании пользовательских сценариев этот шаг обычно не требуется, однако тогда необходимо вручную подготовить нужные таблицы и данные.

Пример инициализации:

pgbench -i [ other-options ] dbname

Где dbname — это имя существующей базы данных. При необходимости можно указать параметры подключения: -h, -p, -U.

Внимание

Команда pgbench -i создает таблицы pgbench_accounts, pgbench_branches, pgbench_history и pgbench_tellers, перезаписывая любые существующие таблицы с такими именами. Будьте осторожны — лучше использовать отдельную тестовую базу данных.

С «коэффициентом масштабирования», по умолчанию равным 1, таблицы будут содержать следующее количество строк:

table                   # of rows
---------------------------------
pgbench_branches 1
pgbench_tellers 10
pgbench_accounts 100000
pgbench_history 0

Для большинства тестов рекомендуется увеличить объем данных с помощью опции -s (коэффициент масштабирования). Дополнительно можно задать параметр -F (фактор заполнения).

Можно (и, для большинства целей, вероятно, должны) увеличить количество строк, используя опцию -s (коэффициент масштабирования). Опция -F (заполняющий фактор) также может быть использована на этом этапе.

Запуск теста

После инициализации запустите тест без параметра -i:

pgbench [ options ] dbname

Обычно необходимо указать дополнительные параметры для корректной нагрузки. Самые важные из них:

  • -c — количество клиентов (параллельных соединений);
  • -t — количество транзакций на клиента;
  • -T — время выполнения теста;
  • -f — путь к пользовательскому сценарию.

Полный список всех доступных параметров приведен ниже.

Параметры

Раздел состоит из трех подразделов: параметры для инициализации, параметры для тестирования и универсальные параметры, применимые в обоих случаях.

Параметры инициализации

pgbench принимает следующие аргументы командной строки для инициализации:

[-d] dbname
[--dbname=]dbname
Задает имя базы данных для тестирования. Если параметр не указан, используется переменная среды PGDATABASE. Если она не установлена, применяется имя пользователя, указанное для подключения.
-i
--initialize
Включает режим инициализации. Обязателен для подготовки базы данных.
-I init_steps
--init-steps=init_steps
Позволяет выбрать конкретные шаги инициализации. Каждый шаг обозначается одной буквой, и выполняются они в заданном порядке. По умолчанию выполняются шаги dtgvp.

Доступные шаги:

  • d — удаление существующих таблиц pgbench;
  • t — создание таблиц: pgbench_accounts, pgbench_branches, pgbench_history, pgbench_tellers;
  • g — генерация данных на стороне клиента с использованием COPY. Журналирует прогресс каждые 100 000 строк;
  • G — генерация данных на стороне сервера. Меньше нагрузки на сеть, но журнал не выводит прогресс;
  • v — выполнение VACUUM для созданных таблиц;
  • p — создание индексов первичных ключей;
  • f — добавление ограничений внешних ключей между стандартными таблицами (по умолчанию не выполняется).

С помощью g (генерация данных на стороне клиента) данные генерируются на стороне клиента и отправляются на сервер. Это интенсивно использует пропускную способность между клиентом и сервером посредством COPY. pgbench использует опцию FREEZE с версией PostgreSQL 14 или более поздней версии для ускорения последующих операций VACUUM, кроме таблицы pgbench_accounts, если включены разбиения на разделы. Использование g приводит к выводу одного сообщения каждые 100 000 строк при генерации данных для всех таблиц.

В случае G (генерация данных на стороне сервера), pgbench отправляет только небольшие управляющие запросы, а сами данные создаются уже на стороне сервера. Это снижает нагрузку на сеть, но увеличивает нагрузку на сервер. При таком способе генерации сообщений о ходе выполнения в журнал не выводится.

-F fillfactor
--fillfactor=fillfactor
Задает коэффициент заполнения для таблиц pgbench_accounts, pgbench_tellers, pgbench_branches. По умолчанию — 100.
-n
--no-vacuum
Пропускает шаг v (очистку) во время инициализации даже при его указании в -I.
-q
--quiet
Переводит журнал в «тихий» режим: одно сообщение о прогрессе каждые 5 секунд. Не действует при использовании G в параметре -I.

По умолчанию выводится одно сообщение для каждых 100 000 строк, что часто приводит к выводу множества строк каждую секунду.

-s scale_factor
--scale=scale_factor
Масштабирует количество сгенерированных строк в зависимости от заданного коэффициента. Например, -s 100 создаст 10 млн строк в pgbench_accounts. При значении 20 000 и выше, для столбцов, хранящих идентификаторы учетных записей (aid), будет использоваться тип данных bigint, чтобы обеспечить достаточный диапазон для хранения идентификаторов.
--foreign-keys
Добавляет ограничения внешних ключей между стандартными таблицами (параметр добавляет шаг f к последовательности шагов инициализации, если он еще не присутствует).
--index-tablespace=index_tablespace
Создает индексы в указанном табличном пространстве вместо пространства по умолчанию.
--partition-method=NAME
Создает партиционированную таблицу pgbench_accounts с методом NAME: range (по умолчанию) или hash. Требует указания --partitions.
--partitions=NUM
Создает партиционированную таблицу с указанным количеством партиций одинакового размера. Если не задано (по умолчанию) — таблица не партиционируется.
--tablespace=tablespace
Создает таблицы в указанном табличном пространстве, а не в табличном пространстве по умолчанию.
--unlogged-tables
Создает все таблицы как как незарегистрированные (UNLOGGED), а не постоянные.

Параметры эталонного тестирования

pgbench принимает следующие аргументы командной строки для тестирования производительности:

-b scriptname[@weight]
--builtin=scriptname[@weight]
Добавляет встроенный сценарий в список сценариев, которые будут выполнены. Доступные встроенные сценарии включают: tpcb-like, simple-update и select-only. Можно использовать сокращенные формы встроенных имен. Специальное имя list выведет список доступных встроенных сценариев и завершит выполнение.

Дополнительно можно указать вес сценария после @, чтобы задать вероятность его выбора по сравнению с другими. По умолчанию вес равен 1. Подробнее смотрите ниже.

-c clients
--client=clients
Устанавливает количество клиентов, которые будут имитировать одновременные сеансы работы с базой данных. По умолчанию установлено значение 1.
-C
--connect
Устанавливает новое соединение для каждой транзакции, а не использует одно соединение на весь сеанс клиента. Полезно для измерения накладных расходов на соединение.
-D varname=value
--define=varname=value
Определяет переменную для использования в пользовательских сценариях (подробнее ниже). Можно использовать несколько таких параметров.
-f filename[@weight]
--file=filename[@weight]
Добавляет сценарий транзакций, считанный из filename, в список сценариев для выполнения.

Дополнительно можно указать вес после @, чтобы настроить вероятность выбора этого сценария. По умолчанию вес равен 1. (Если имя файла включает @, укажите вес для избежания двусмысленности, например filen@me@1). Подробнее ниже.

-j threads
--jobs=threads
Устанавливает количество рабочих потоков внутри pgbench. Использование нескольких потоков полезно на многопроцессорных системах. Клиенты будут распределены равномерно по доступным потокам. По умолчанию равно 1.
-l
--log
Записывает информацию о каждой транзакции в файл журнала. Подробнее ниже.
-L limit
--latency-limit=limit
Транзакции, которые занимают больше limit миллисекунд, будут отдельно подсчитаны и отмечены как поздние.

При ограничении пропускной способности (--rate=...), транзакции, которые слишком сильно отстают от графика (на более чем limit мс), не будут отправляться на сервер, а будут учтены как пропущенные.

Если используется --max-tries, транзакция, не завершившаяся из-за аномалии сериализации или взаимоблокировки, не будет повторена, если общее время ее попыток превышает limit мс. Подробнее об этом в разделе «Повторные попытки после ошибок».

-M querymode
--protocol=querymode
Выбирает протокол для отправки запросов на сервер:
  • simple — простой протокол запросов;
  • extended — расширенный протокол запросов;
  • prepared — расширенный запрос с подготовленными выражениями.

В режиме prepared pgbench повторно использует результат анализа запросов, что делает его быстрее. По умолчанию используется простой протокол (подробнее описано в разделе «Клиент-серверный протокол»).

-n
--no-vacuum
Не выполняет очистку перед тестом. Это обязательно, если используется пользовательский сценарий без стандартных таблиц pgbench_accounts, pgbench_branches, pgbench_history, pgbench_tellers.
-N
--skip-some-updates
Запускает встроенный упрощенный скрипт simple-update. Краткий вариант записи -b simple-update.
-P sec
--progress=sec
Выводит отчет о прогрессе каждые sec секунд. В отчете отображается прошедшее время с начала теста, количество транзакций в секунду (TPS) с момента последнего отчета, средняя задержка транзакций, стандартное отклонение и число неудачных транзакций за этот период.

Если задано ограничение скорости (-R), задержка рассчитывается относительно планируемого времени начала транзакции, а не фактического, и потому включает среднее значение отставания от расписания. При использовании параметра --max-tries в отчете также указывается количество повторных попыток транзакций и их общее количество.

-r
--report-per-command
Выводит статистику по каждой команде после завершения теста: средняя задержка, количество сбоев и повторных попыток после ошибок сериализации или взаимоблокировки в этой команде. Отчет отображает статистику повторных попыток только в том случае, если параметр --max-tries не равен 1.
-R rate
--rate=rate
Выполняет транзакции с заданной скоростью (транзакции в секунду), а не с максимально возможной (по умолчанию). Если заданная скорость превышает возможности системы, ограничение не будет влиять на результаты теста.

Транзакции запускаются по временной шкале, построенной на основе распределения Пуассона. Ожидаемое время следующей транзакции рассчитывается относительно времени начала теста, а не окончания предыдущей транзакции. Такой подход позволяет транзакциям, которые отстанут от изначально рассчитанного времени завершения, «наверстать» график в дальнейшем.

При включенном ограничении скорости задержка транзакции, отображаемая по завершении теста, рассчитывается от запланированного времени запуска, а не фактического. Поэтому она также учитывает время ожидания, вызванное тем, что предыдущая транзакция еще не завершилась. Это время называется отставанием от расписания — в отчете указывается его среднее значение и максимум. Чтобы определить реальное время выполнения транзакции в базе данных, можно вычесть отставание от общей задержки.

Если параметр --latency-limit используется совместно с --rate, возможна ситуация, когда транзакция еще до запуска уже превысила допустимый порог задержки. В этом случае она не выполняется и учитывается отдельно как пропущенная.

Значительное отставание от расписания указывает на то, что система не справляется с заданной нагрузкой при текущем числе клиентов и потоков. Если среднее время выполнения транзакции превышает заданный интервал между транзакциями, отставание будет расти на протяжении всего теста. Чтобы избежать этого, рекомендуется снизить целевую скорость транзакций.

-s scale_factor
--scale=scale_factor
Устанавливает коэффициент масштабирования в выводе pgbench. Встроенные тесты автоматически определяют этот коэффициент подсчетом количества строк в таблице pgbench_branches, но для пользовательских тестов (-f параметр) коэффициент масштабирования будет сообщен как 1, если этот параметр не используется.
-S
--select-only
Запускает встроенный сценарий только для выборки. Краткий вариант записи -b select-only.
-t transactions
--transactions=transactions
Указывает количество транзакций, которые выполняет каждый клиент. По умолчанию равно 10.
-T seconds
--time=seconds
Выполняет тест в течение заданного количества секунд вместо фиксированного числа транзакций на клиента. -t и -T исключают друг друга.
-v
--vacuum-all
Выполняет очистку всех четырех стандартных таблиц перед тестом. Без -n или -v pgbench очищает только таблицы pgbench_tellers, pgbench_branches и pgbench_history.
--aggregate-interval=seconds
Устанавливает интервал агрегирования (в секундах). Используется только с параметром -l. Журнал будет содержать агрегированные данные за каждый интервал.
--exit-on-abort
Немедленно завершить работу, если какой-либо клиент прерывается из-за ошибки. Без этой опции даже прерванный клиент мог бы продолжить свою работу согласно параметрам, указанным в опциях -t или -T, и pgbench выведет неполный результат в таком случае.

Обратите внимание, что ошибки сериализации или тупики не приводят к прекращению работы клиента, поэтому они не затрагиваются этим параметром.

--failures-detailed
Сообщает о сбоях в журналах транзакций и агрегирования, а также в отчетах, сгруппированных по типам ошибок (сбои сериализации, блокировки).

Подробнее описано в разделе «Сбои и повторные попытки сериализации/мертвых блокировок»

--log-prefix=prefix
Устанавливает префикс для имени файлов журнала, создаваемых с помощью --log. По умолчанию используется pgbench_log.
--max-tries=number_of_tries
Активирует повторные попытки выполнения транзакций, завершившихся с ошибкой сериализации или из-за мертвой блокировки, и задает максимальное число таких попыток. Параметр можно использовать совместно с --latency-limit, который ограничивает суммарное время выполнения всех попыток одной транзакции. Обратите внимание, что нельзя задать неограниченное число попыток (--max-tries=0), если не указаны параметры --latency-limit или --time. По умолчанию значение равно 1 — это означает, что транзакции с ошибками не повторяются. Подробности о повторных попытках в разделе «Сбои и повторные попытки сериализации/мертвых блокировок».
--progress-timestamp
При выводе отчета о ходе выполнения (с помощью параметра -P) вместо времени, прошедшего с начала запуска, отображается временная метка в формате (эпоха Unix). Значение указывается в секундах с точностью до миллисекунд. Это облегчает сопоставление с логами, полученными из других инструментов.
--random-seed=seed
Устанавливает начальное значение для генератора случайных чисел. Этот параметр инициализирует системный генератор, который затем формирует отдельные начальные состояния случайных чисел для каждого потока. В качестве значения seed можно указать:
  • time (значение по умолчанию) — инициализация на основе текущего времени;
  • rand — использование криптографически стойкого источника случайности (в случае его недоступности pgbench завершит выполнение);
  • неотрицательное целое число — фиксированное значение для воспроизводимости.

Генератор используется явно (через вызовы random... в скриптах pgbench) и неявно, например, для генерации расписания транзакций при использовании --rate.

При явной установке начального значения pgbench выводит его в терминал. Это значение также может быть задано через переменную окружения PGBENCH_RANDOM_SEED.

Внимание!

Чтобы гарантировать влияние на все участки, использующие случайность, укажите параметр --random-seed первым в командной строке или воспользуйтесь переменной окружения.

--sampling-rate=rate
Устанавливает частоту выборки, применяемую при логировании для сокращения объема создаваемого журнала. При указании этого параметра в журнал попадает только заданная доля всех транзакций. Значение 1.0 означает, что журналируются все транзакции, а, например, 0.05 — только 5% от общего количества.
примечание

Учитывайте частоту выборки при последующем анализе журнала. Например, при расчете TPS (транзакций в секунду) необходимо скорректировать значения с учетом выборки: если использовалась частота 0.01, то в журнале отражена лишь сотая часть фактического TPS.

--show-script=scriptname
Выводит код встроенного сценария scriptname и завершается.
--verbose-errors
Выводит сообщения обо всех ошибках и сбоях (включая ошибки, для которых не предусмотрены повторные попытки), а также указывает, какой именно предел повторных попыток был превышен и насколько — в случаях сбоев сериализации или мертвой блокировки.
Внимание!

Объем вывода в этом режиме может существенно возрасти. Подробнее описано в разделе «Сбои и повторные попытки сериализации/мертвых блокировок».

Общие параметры

pgbench также принимает следующие общие аргументы командной строки для параметров подключения и других общих настроек:

--debug
Выводить отладочную информацию.
-h host
--host = host
Указывает имя хоста сервера баз данных.
-p port
--port = port
Указывает номер порта сервера баз данных.
-U username
--username=username
Указывает имя пользователя для подключения.
-V
--version
Выводит версию pgbench и завершается.
-?
--help
Показывает справку о параметрах командной строки утилиты pgbench и завершается.

Код завершения

Успешный запуск завершается статусом 0. Статус завершения 1 указывает на статические проблемы, такие как неверные параметры командной строки или внутренние ошибки, которые никогда не должны возникать. Ранние ошибки, возникающие при запуске эталона, такие как первоначальные отказы соединения, также приводят к выходу со статусом 1. Ошибки во время выполнения, такие как ошибки базы данных или проблемы со сценарием, приведут к статусу выхода 2. В последнем случае программа pgbench выведет частичные результаты, если не указана опция --exit-on-abort.

Параметры окружения

Утилита поддерживает переменные окружения:

  • PGDATABASE
  • PGHOST
  • PGPORT
  • PGUSER
  • PG_COLOR

PG_COLOR указывает, использовать ли цвет в диагностических сообщениях. Возможные значения - always, auto и never.

Утилита pgbench использует переменные окружения, поддерживаемые libpq.

Примечания

Сценарии тестирования

pgbench выполняет один или несколько сценариев, выбирая их случайным образом из заданного набора. Это могут быть:

  • встроенные сценарии (указываются с помощью параметра -b);
  • пользовательские сценарии (задаются через параметр -f).

Каждому сценарию можно задать вес (после символа @), определяющий вероятность его выбора. По умолчанию вес равен 1. Сценарии с весом 0 не используются.

Встроенные сценарии

Встроенный сценарий транзакций по умолчанию (также вызываемый с помощью -b tpcb-like) выполняет семь SQL-команд в каждой транзакции, используя случайно выбранные значения aid, tid, bid и delta. Он вдохновлен тестом TPC-B, но не является его реализацией:

  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;

Если использовать встроенный сценарий simple-update (также -N), то шаги 4 и 5 (обновления tellers и branches) исключаются из транзакции. Это снижает вероятность конфликтов при параллельном обновлении, но еще сильнее отдаляет тест от модели TPC-B.

Если выбрать встроенный сценарий select-only (также -S), выполняется только SELECT.

Пользовательские сценарии

pgbench позволяет использовать собственные сценарии для тестирования производительности, заменяя стандартный сценарий транзакции (описанный выше) скриптом из файла. Для этого используется параметр -f. В данном случае одна «транзакция» соответствует одному полному выполнению содержимого файла.

Скрипт должен содержать одну или несколько SQL-команд, каждая из которых завершается точкой с запятой (;). Строки, начинающиеся с --, считаются комментариями и игнорируются, как и пустые строки. Помимо SQL-команд, скрипт может включать специальные метакоманды, которые обрабатываются самим pgbench, как описано ниже.

Примечание

В версиях PostgreSQL до 9.6 разделение SQL-команд в скриптах осуществлялось переносом строки, поэтому команды не могли занимать несколько строк. Начиная с версии 9.6, команды должны разделяться точками с запятой. Однако, если после SQL-команды идет метакоманда, точка с запятой не требуется. Чтобы обеспечить совместимость с разными версиями, рекомендуется размещать каждую SQL-команду на отдельной строке с точкой с запятой в конце.

pgbench ожидает, что скрипт содержит полные транзакции. Если клиент дойдет до конца сценария без завершения текущей транзакции, выполнение будет прервано.

Существует простая функция подстановки переменных в скриптах. Имена переменных могут включать буквы (в том числе нелатинские), цифры и символ подчеркивания, но не могут начинаться с цифры.

Переменные можно задать двумя способами:

  • через параметр командной строки -D, описанного выше;
  • через метакоманды в теле скриптаЭ, описанных ниже.

Кроме явно заданных переменных, pgbench автоматически устанавливает ряд встроенных переменных, перечисленных в таблице «Автоматические переменные pgbench». При этом значения, указанные через параметр -D, имеют приоритет над автоматически установленными.

Для подстановки переменной в SQL-команду используется синтаксис :variablename. При многопоточном запуске каждый клиент использует собственный изолированный набор переменных. В одном SQL-операторе допускается использовать до 255 переменных.

Автоматические переменные pgbench

Ниже перечислены переменные, которые pgbench автоматически устанавливает при выполнении сценария:

Автоматические переменные pgbench

Переменная
Описание
client_id
уникальный номер, идентифицирующий клиентскую сессию (начинается с нуля)
default_seed
значение по умолчанию для генерации хеша и псевдослучайных перестановок
random_seed
начальное значение генератора случайных чисел (если не переопределено через -D)
scale
текущий коэффициент масштабирования

Метакоманды сценариев pgbench начинаются с обратной косой черты (\) и обычно продолжаются до конца строки, но при необходимости могут быть продолжены на следующую строку с помощью символа переноса (\). Аргументы метакоманд разделяются пробелами. Ниже описаны поддерживаемые метакоманды:

\gset [prefix]
\aset [prefix]
Могут завершать SQL-запрос, заменяя точку с запятой (;).

\gset сохраняет результат SQL-запроса, который должен вернуть одну строку: каждый столбец этой строки записывается в переменные, названные по именам столбцов, с указанным префиксом (если есть).

\aset используется при выполнении составных запросов (разделенных \;). Она сохраняет значения столбцов из последней строки результата каждого запроса в переменные с именами столбцов и указанным префиксом. Если запрос не возвращает строк, переменные не создаются — это можно использовать для проверки успешности запроса.

Обе команды не поддерживаются в режиме конвейера, так как в этом режиме результаты еще не получены на момент выполнения этих команд.

В следующем примере результат первого запроса — итоговый баланс счета — сохраняется в переменную abalance. Третий запрос присваивает значения переменным p_two и p_three. Второй запрос выполняется, но его результат не используется. Последняя пара объединенных запросов сохраняет свои значения в переменные four и five соответственно.

UPDATE pgbench_accounts
SET abalance = abalance + :delta
WHERE aid = :aid
RETURNING abalance \gset
-- compound of two queries
SELECT 1 \;
SELECT 2 AS two, 3 AS three \gset p_
SELECT 4 AS four \; SELECT 5 AS five \aset
\if expression
\elif expression
\else
\endif
Реализуют вложенные условные блоки, аналогичные блокам \if expression в psql. Выражения работают так же, как в \set, где ненулевое значение означает TRUE.
\set varname expression
Устанавливает значение переменной varname, вычисленное из выражения expression, которое может включать:
  • логические значения TRUE, FALSE;
  • NULL;
  • целочисленные константы (5432) и константы с плавающей точкой (3.14159);
  • ссылки на переменные (:variablename);
  • встроенные операторы с их обычным приоритетом и ассоциативностью SQL;
  • вызовы функций;
  • общие условные выражения SQL CASE;
  • скобки.

Функции и большинство операторов возвращают значение NULL, если хотя бы один из их аргументов равен NULL.

В логических выражениях числовые значения, отличные от нуля, интерпретируются как TRUE, а ноль и NULL считаются FALSE.

При переполнении — например, при слишком больших или малых числах, а также при использовании целочисленных арифметических операторов (+, -, *, /) — возникает ошибка.

Без блока ELSE в CASE результатом будет NULL.

Примеры использования команды:

\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 ]
Приостанавливает выполнение скрипта на указанное время в микросекундах (us), миллисекундах (ms) или секундах (s). Если единица измерения опущена, то по умолчанию используются секунды. number может быть либо целой константой, либо :variablename ссылкой на переменную с целым значением.

Пример использования команды:

\sleep 10 ms
\setshell varname command [ argument ... ]
Выполняет внешнюю команду command и сохраняет ее целочисленный результат в переменную varname с заданными аргументами argument.

Команды и аргументы могут быть текстовой константой или ссылкой на переменную :variablename. Если аргумент начинается с двоеточия, его нужно экранировать дополнительным двоеточием.

Пример использования команды:

\setshell variable_to_be_assigned command literal_argument :variable ::literal_starting_with_colon
\shell command [ argument ... ]
Работает аналогично \setshell, но результат команды игнорируется.

Пример использования команды:

\shell command literal_argument :variable ::literal_starting_with_colon
\startpipeline
\syncpipeline
\endpipeline
Эта группа команд реализует конвейерную обработку операторов SQL. Конвейер должен начинаться с \startpipeline и заканчиваться \endpipeline. Между ними может находиться любое количество команд \syncpipeline, которые отправляют сообщение sync, не завершая текущий конвейер и не очищая буфер отправки. В режиме конвейера операторы отправляются на сервер без ожидания результатов предыдущих операторов. Для использования режима конвейера требуется расширенный протокол запросов.

Встроенные операторы

pgbench поддерживает арифметические, побитовые, операторы сравнения и логические операторы, которые можно использовать в выражениях, встречающихся в \set. Операторы перечислены в порядке возрастания приоритета.

Операторы с двумя числовыми аргументами возвращают double, если хотя бы один из аргументов — дробный, в противном случае возвращается целочисленный результат.

Ниже представлен подробный список операторов:

Операторы pgbench

Оператор
Описание
Пример
boolean OR booleanboolean
Логическое ИЛИ
5 or 0TRUE
boolean AND booleanboolean
Логическое И
3 and 0FALSE
NOT booleanboolean
Логическое НЕ
not falseTRUE
boolean IS [NOT] (NULL|TRUE|FALSE)boolean
Проверка логических значений
1 is nullFALSE
number = numberboolean
Равно
5 = 4FALSE
number <> numberboolean
Не равно
5 <> 4TRUE
number != numberboolean
Не равно
5 != 5FALSE
number < numberboolean
Меньше чем
5 < 4FALSE
number <= numberboolean
Меньше или равно
5 <= 4FALSE
number > numberboolean
Больше чем
5 > 4TRUE
number >= numberboolean
Больше или равно
5 >= 4TRUE
integer | integerinteger
Побитовое ИЛИ
1 | 23
integer # integerinteger
Побитовое исключающее ИЛИ
1 # 32
integer & integerinteger
Побитовое И
1 & 31
~ integerinteger
Побитовое НЕ
~ 1 → -2
integer << integerinteger
Побитовый сдвиг влево
1 << 24
integer >> integerinteger
Побитовый сдвиг вправо
8 >> 22
number + numbernumber
Сложение
5 + 49
number - numbernumber
Вычитание
3 - 2.01.0
number * numbernumber
Умножение
5 * 420
number / numbernumber
Деление (результат округляется в сторону нуля, если оба аргумента являются целыми числами)
5 / 31
integer % integerinteger
Остаток от деления
3 % 21
- numbernumber
Смена знака
- 2.0-2.0

Встроенные функции

Функции, перечисленные в таблице «Функции pgbench», встроены в pgbench и могут использоваться в выражениях, встречающихся в \set.

Функции pgbench

Функция
Описание
Пример
abs (number) → тип аргумента
Модуль числа (абсолютное значение)
abs(-17)17
debug (number) → тип аргумента
Выводит аргумент в stderr и возвращает его
debug(5432.1)5432.1
double (number)double
Приводит к двойному типу
double(5432)5432.0
exp (number)double
Экспонента (e возводится в заданную степень)
exp(1.0)2.718281828459045
greatest (number [, ...])double если аргумент с плавающей точкой, иначе integer
Выбирает наибольшее значение среди аргументов
greatest(5, 4, 3, 2)5
hash (value [, seed])integer
Псевдоним hash_murmur2
hash(10, 5432)-5817877081768721676
hash_fnv1a (value [, seed])integer
Вычисляет хеш по алгоритму FNV-1a
hash_fnv1a(10, 5432)-7793829335365542153
hash_murmur2 (value [, seed])integer
Вычисляет хеш по алгоритму MurmurHash2.
hash_murmur2(10, 5432)-5817877081768721676
int (number)integer
Приводит аргумент к целому числу
int(5.4 + 3.8)9
least (number [, ...])double если аргумент с плавающей точкой, иначе integer
Выбирает наименьшее значение среди аргументов
least(5, 4, 3, 2.1)2.1
ln (number) → double`
Натуральный логарифм
ln(2.718281828459045)1.0
mod (integer, integer)integer
Остаток от деления
mod(54, 32)22
permute (i, size [, seed])integer
Новое положение значения i в диапазоне от 0 до size — это результат псевдослучайной перестановки чисел от 0 до size – 1, зависящей от параметра seed, подробнее об этом описано ниже
permute(0, 4) → целое число в диапазоне от 0 до 3
pi ()double
Приблизительное значение π
pi()3.14159265358979323846
pow (x, y)double

power (x, y)double
x возведено в степень y
pow(2.0, 10)1024.0
random (lb, ub)integer
Выдает случайное целое число с равномерным распределением в диапазоне от lb до ub
random(1, 10) → целое число в диапазоне от 1 до 10
random_exponential (lb, ub, parameter)integer
Выдает случайное целое число с экспоненциальным распределением в диапазоне от lb до ub, подробнее об этом описано ниже
random_exponential(1, 10, 3.0) → целое число в диапазоне от 1 до 10
random_gaussian (lb, ub, parameter)integer
Выдает целое число с распределением Гаусса в диапазоне от lb до ub, подробнее об этом описано ниже
random_gaussian(1, 10, 2.5) → целое число в диапазоне от 1 до 10
random_zipfian (lb, ub, parameter)integer
Выдает целое число с распределением Ципфа в диапазоне от lb до ub, подробнее об этом описано ниже
random_zipfian(1, 10, 1.5) → целое число в диапазоне от 1 до 10
sqrt (number)double
Квадратный корень
sqrt(2.0)1.414213562

Функция random генерирует случайные значения с равномерным распределением,то есть вероятности получения всех чисел в диапазоне равны. Функции random_exponential, random_gaussian и random_zipfian требуют дополнительного параметра типа double, который задает форму распределения.

Для экспоненциального распределения parameter контролирует распределение, обрезая быстро спадающее экспоненциальное распределение в точке parameter и проецируя это распределение на целые числа в пределах указанного диапазона. Формула распределения выглядит следующим образом:

f(x) = exp(-parameter * (x - min) / (max - min + 1)) / (1 - exp(-parameter))

Значение i между min и max выдается с вероятностью: f(i) - f(i + 1).

Чем больше parameter, тем чаще выбираются значения, ближе к min, и реже — значения, ближе к max. Чем parameter ближе к 0, тем более равномерным будет распределение. Например, наиболее частый 1% значений в диапазоне рядом с min выбираются parameter% времени. parametr должен быть строго положительным.

Для распределения Гаусса по интервалу строится обычное нормальное распределение (классическая кривая Гаусса в форме колокола), усеченное на значениях -parameter слева и +parameter справа. В этом случае средние значения более вероятно будут выбраны. Формула для распределения следующая:

f(x) = PHI(2.0 * parameter * (x - mu) / (max - min + 1)) / (2.0 * PHI(parameter) - 1)

Где:

  • mu — это среднее значение, определяемое как (max + min) / 2.0;
  • PHI(x) — функция кумулятивного распределения стандартного нормального распределения.

Значение i выбирается с вероятностью: f(i + 0.5) - f(i - 0.5).

Чем больше parameter, тем чаще выбираются значения, близкие к середине интервала, и реже — значения, близкие к границам min и max. Например, при parameter равном 4.0, 67% значений будут взяты из средней четверти интервала, а 95% — из средней половины. Минимально допустимое значение для parameter — 2.0.

Функция random_zipfian генерирует распределение по закону Ципфа, где parameter управляет степенью смещения распределения. Чем выше parameter, тем чаще выбираются значения, близкие к началу диапазона. Распределение устроено так, что вероятность выбора значения k в сравнении с выбором значения k+1 равна ((k + 1) / k) ** parameter. Например, вызов random_zipfian(1, ..., 2.5) выбирает значение 1 примерно (2/1)**2.5 = 5.66 раз чаще, чем значение 2, и так далее.

Реализация основана на алгоритме из книги «Генерация неравномерно распределенных случайных чисел» Люк Деврой, стр. 550-551, Springer 1986. Из-за ограничений алгоритма значение parameter должно быть в пределах от 1.001 до 1000.

Примечание

При проектировании теста, где выбираются строки с неравномерным распределением, учтите, что эти строки могут быть связаны с другими данными, такими как идентификаторы или физическое расположение строк, что может исказить результаты тестирования производительности.

Чтобы избежать этой корреляции, используйте функцию permute или аналогичный шаг, который перемешает строки перед выбором.

Хеш-функции hash, hash_murmur2 и hash_fnv1a принимают входное значение и необязательный параметр начального значения. Если начальное значение не задано, используется параметр :default_seed, который инициализируется случайным образом, если не установлено через параметр командной строки -D.

Функция permute принимает входное значение, размер и необязательный параметр начального значения, генерируя псевдослучайную перестановку целых чисел в диапазоне от 0 до size, и возвращает индекс входного значения в этой перестановке. В отличие от хеш-функций, permute гарантирует отсутствие пропусков и наложений в выходных значениях. Входные значения, которые выходят за пределы диапазона, интерпретируются по модулю размера. Если значение размера не положительное, функция выдает ошибку.

permute можно использовать для выравнивания результатов функций, выдающих неравномерно распределенные случайные числа, таких как random_zipfian или random_exponential, чтобы чаще выдаваемые значения не создавали корреляции. Например, следующий скрипт pgbench имитирует возможную реальную нагрузку, типичную для платформ социальных сетей и блогов, где несколько пользователей создают избыточную нагрузку:

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

Для использования нескольких распределений, не коррелирующих друг с другом, можно указать разные начальные значения для функции permute:

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

Аналогичный результат можно получить с использованием функции hash:

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

Однако учтите, что hash может приводить к повреждению данных, что может повлиять на распределение.

Например, встроенная транзакция типа 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;

Этот сценарий позволяет каждой итерации транзакции выбирать разные случайные строки. (Этот пример также показывает, почему важно, чтобы в каждом клиентском сеансе были собственные переменные — иначе они не будут независимо обращаться к разным строкам).

Регистрация транзакций

При использовании параметра -l (без указания --aggregate-interval), pgbench записывает данные о каждой завершенной транзакции в отдельный файл журнала. По умолчанию имя такого файла имеет формат prefix.nnn, где prefix — это pgbench_log (значение по умолчанию, настраивается через --log-prefix), а nnnPID процесса pgbench.

Если используется несколько рабочих потоков (например, с параметром -j ≥ 2), каждый поток создает собственный лог-файл. Первый поток записывает журнал в файл с обычным именем (prefix.nnn), а остальные — в файлы с именами вида prefix.nnn.mmm, где mmm — это порядковый номер потока, начиная с 1.

Каждая строка в журнале представляет собой описание одной транзакции и содержит следующие поля, разделенные пробелами:

  • client_id: номер клиента, выполнившего транзакцию;
  • transaction_no: порядковый номер транзакции у данного клиента;
  • time: время выполнения транзакции (в микросекундах);
  • script_no: номер использованного скрипта (актуально при использовании параметров -f или -b);
  • time_epoch: метка времени окончания транзакции (в секундах с начала эпохи Unix);
  • time_us: микросекундная часть времени завершения транзакции.

Дополнительные поля:

  • schedule_lag: задержка начала транзакции (разница между запланированным и фактическим стартом), появляется при использовании параметра --rate;
  • retries: число повторных попыток выполнения транзакции (присутствует, если --max-tries > 1).

Если одновременно указаны параметры --rate и --latency-limit, то в случае пропуска транзакции ее поле time будет содержать skipped. Если транзакция завершилась неудачно, в поле time будет failed. При включенном параметре --failures-detailed, вместо failed будут указаны конкретные причины — serialization или deadlock в зависимости от типа сбоя. Подробнее описано в разделе «Сбои и повторные попытки сериализации/мертвых блокировок».

Фрагмент протокола, полученного при выполнении с одним клиентом:

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

Пример с параметрами --rate=100 и --latency-limit=5 (обратите внимание на дополнительный столбец 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

В этом фрагменте видно, что транзакция 82 превысила допустимую задержку (6.173 мс при лимите 5 мс), после чего несколько следующих транзакций были пропущены.

Фрагмент протокола с отказами и повторными попытками при максимальном числе попыток, равном 10 (обратите внимание на дополнительный столбец 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

Если используется параметр --failures-detailed, тип сбоя сообщается в time следующим образом:

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

При запуске длительного теста на мощных системах объем лог-файлов может стать значительным. Для ограничения объема журнала можно использовать параметр --sampling-rate, который позволяет записывать только случайную выборку транзакций.

Агрегированное ведение журнала

При использовании параметра --aggregate-interval формат журналов pgbench отличается от стандартного. В этом режиме каждая строка журнала представляет собой сводку статистики за один интервал времени. Поля строки, разделенные пробелами, имеют следующие значения:

interval_start
Время начала интервала в формате Unix-метки времени.
num_transactions
Общее количество завершенных транзакций за интервал.
sum_latency
Суммарное время выполнения всех транзакций.
sum_latency_2
Сумма квадратов времени выполнения транзакций.
min_latency
Минимальное время выполнения среди транзакций.
max_latency
Максимальное время выполнения среди транзакций.
sum_lag
Суммарная задержка старта транзакций (равна нулю, если не указан параметр --rate).
sum_lag_2
Сумма квадратов задержек старта (равна нулю, если не указан параметр --rate).
min_lag
Минимальная задержка начала (равна нулю, если не указан параметр --rate).
max_lag
Максимальная задержка начала (равна нулю, если не указан параметр --rate).
skipped
Количество пропущенных транзакций, не запущенных из-за превышения лимита задержки (равно нулю, если не указаны параметры --rate и --latency-limit).
retried
Количество транзакций, которые были выполнены не с первой попытки (равно нулю при --max-tries=1).
retries
Общее число повторов после неудач из-за ошибок сериализации или взаимоблокировок (равно нулю при --max-tries=1).
serialization_failures
Число транзакций, завершившихся ошибкой сериализации без повторной попытки (равно нулю, если не указан параметр --failures-detailed).
deadlock_failures
Число транзакций, завершившихся ошибкой взаимоблокировки без повтора (равно нулю, если не указан параметр --failures-detailed).

Ниже представлен пример вывода с этим параметром:

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

Обратите внимание, что в отличие от неагрегированного журнала, агрегированный формат не показывает, какой именно сценарий (-f/-b) был использован для транзакций. Если нужна детализация по отдельным сценариям, необходимо собирать и агрегировать информацию вручную.

Отчет о каждом предложении

При использовании параметра -r (--report-per-command) утилита pgbench собирает детальную статистику по каждому SQL-оператору в сценарии. В отчете указываются следующие метрики:

  • latency — среднее время выполнения оператора среди всех успешных запусков;
  • Количество сбоев — число неудачных выполнений данного оператора. Подробнее описано в разделе «Сбои и повторные попытки сериализации/мертвых блокировок»;
  • Количество повторных попыток — число повторных выполнений оператора после ошибок сериализации или взаимоблокировки (при --max-tries > 1). Также описано в соответствующем разделе.

Все показатели собираются индивидуально для каждого клиента и каждого оператора, а затем выводятся по завершении теста.

Для скрипта по умолчанию вывод будет выглядеть примерно так:

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;

Пример вывода для скрипта по умолчанию с выбором сериализуемого уровня изоляции (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;

Если используется несколько файлов сценариев (-f), статистика формируется отдельно для каждого из них.

Важно

Сбор дополнительной статистики увеличивает накладные расходы и может снизить общую производительность (TPS). Насколько это повлияет на результат, зависит от конкретного оборудования и платформы. Сравнение значений TPS с параметром -r и без него позволяет оценить влияние этого параметра.

Ошибки и повторы при сериализационных и взаимоблокировочных ошибках

При выполнении программы pgbench возможны три основных типа ошибок:

  • Ошибки основной программы. Они самые серьезные и всегда приводят к немедленному завершению работы pgbench с соответствующим сообщением об ошибке. К ним относятся:

    • ошибки в начале работы pgbench (например, неверное значение параметра);
    • ошибки в режиме инициализации (например, запрос на создание таблиц для встроенных сценариев завершился неудачей);
    • ошибки перед запуском потоков (например, невозможно подключиться к серверу базы данных, синтаксическая ошибка в мета-команде, сбой создания потока);
    • внутренние ошибки pgbench (которые никогда не должны возникать).
  • Ошибки, возникающие при управлении потоком своих клиентов (например, клиент не смог установить соединение с сервером базы данных / сокет, используемый для подключения клиента к серверу базы данных, стал недействительным). В таких случаях все клиенты данного потока останавливают свою работу, тогда как другие потоки продолжают работать. Однако, если задано условие --exit-on-abort, все потоки немедленно прекращают свою работу в таком случае.

  • Прямые ошибки клиента. При возникновении внутренней ошибки pgbench (которая никогда не должна возникнуть) или при указании параметра --exit-on-abort они приводят к немедленному выходу из pgbench с соответствующим сообщением об ошибке. В противном случае в худшем случае это приводит лишь к прерыванию работы неисправного клиента, тогда как остальные клиенты продолжают выполнение (хотя некоторые ошибки клиента обрабатываются без прерывания его работы и сообщаются отдельно, см. ниже). Далее в этом разделе предполагается, что обсуждаемые ошибки являются прямыми ошибками клиента и не являются внутренними ошибками pgbench.

Выполнение клиента прерывается в случае серьезной ошибки; например, потеряно соединение с сервером базы данных или достигнут конец сценария без завершения последней транзакции. Кроме того, если выполнение команды SQL или метакоманды завершается неудачно по причинам, отличным от ошибок сериализации или взаимоблокировок, работа клиента также прерывается. В противном случае, если команда SQL завершается неудачно из-за ошибок сериализации или взаимоблокировок, клиент не прерывает свою работу. В таких случаях текущая транзакция откатывается, включая восстановление переменных клиента такими, какими они были до начала выполнения данной транзакции (предполагается, что один сценарий транзакции содержит только одну транзакцию). Транзакции с ошибками сериализации или взаимоблокировок повторяются после откатов до тех пор, пока они успешно завершатся или не будут достигнуты максимальное число попыток (заданное параметром --max-tries), максимальное время повторных попыток (заданное параметром --latency-limit), окончание теста (заданное параметром --time). Если последняя попытка завершилась неудачей, эта транзакция будет отмечена как неуспешная, но клиент продолжит свою работу.

Примечание

Если не указать --max-tries, значение по умолчанию — 1, и повторные попытки при ошибках сериализации/мертвой блокировки не выполняются.

Для бесконечных повторных попыток используйте --max-tries=0 вместе с --latency-limit, чтобы ограничить только время попыток. Также можно задать --time, чтобы ограничить длительность всего теста.

При использовании повторных попыток будьте осторожны со сценариями, содержащими несколько транзакций: при повторе выполняется весь сценарий, и успешные транзакции могут выполниться повторно.

Также важно помнить, что команды оболочки в сценарии (например, \shell) при откате транзакции не отменяются, за исключением команды \setshell, которая управляет переменными.

Задержка каждой успешной транзакции включает в себя все время, затраченное на ее выполнение, включая откаты и повторные попытки. Задержки фиксируются только для успешных транзакций и команд — неуспешные транзакции или команды в отчеты по времени не включаются.

Основной отчет pgbench содержит:

  • общее количество неудачных транзакций;
  • при --max-tries > 1 — также статистику по повторным попыткам: сколько транзакций было повторено и общее число попыток.
  • эти же данные включаются в отчеты по сценариям. В отчетах по отдельным операторам статистика повторов показывается только при --max-tries > 1.

Чтобы дополнительно сгруппировать типы сбоев в отчетах (основном, по сценариям и в логах), используйте:

  • --failures-detailed — сгруппирует ошибки по типу;
  • --verbose-errors — различит все типы ошибок и сбоев, в том числе укажет, какой лимит был превышен при ошибке сериализации или мертвой блокировке.

Табличные методы доступа

Можно указать метод доступа к таблице Table Access Method для таблиц pgbench. Переменная окружения PGOPTIONS задает параметры конфигурации базы данных, передаваемые PostgreSQL через командную строку. Например, гипотетический метод доступа к таблице по умолчанию для таблиц, создаваемых pgbench под названием wuzza, можно задать следующим образом:

PGOPTIONS='-c default_table_access_method=wuzza'

Полезные советы

pgbench очень легко использовать для получения ничего не значащих результатов. Чтобы избежать этого и получить действительно полезные данные, следуйте этим рекомендациям:

  • Избегайте коротких тестов.

    Не доверяйте результатам, полученным за несколько секунд. Всегда запускайте тест хотя бы на несколько минут с помощью параметров -t (число транзакций) или -T (время выполнения). Чем дольше длится тест, тем надежнее результат. В некоторых случаях для получения воспроизводимых цифр потребуется несколько часов. Полезно также запускать тесты несколько раз, чтобы проверить стабильность и повторяемость результатов;

  • Выбирайте корректный масштаб теста.

    При использовании стандартного встроенного сценария, основанного на TPC-B, убедитесь, что коэффициент масштабирования инициализации (-s) не меньше количества клиентов (-c).

    Если клиентов больше, чем строк в таблице pgbench_branches (а их всего -s), то транзакции начнут конфликтовать при обновлениях и блокироваться, и будет измеряться не производительность, а конкуренцию за ресурсы;

  • Учитывайте сколько всего изменений было произведено и когда выполнялась очистка.

    Результаты тестов могут искажаться, если таблицы не были недавно инициализированы. По мере накопления «мертвых» строк и пустого пространства внутри таблиц производительность меняется. Чтобы правильно интерпретировать данные, отслеживайте общее количество обновлений и анализируйте выполнение VACUUM. Включенная автоматическая очистка (autovacuum) может вмешиваться в процесс и вызвать нестабильность результатов;

  • Избегайте ограниченность со стороны самого pgbench.

    Результаты тестирования с помощью pgbench могут быть менее достоверными, если само приложение становится источником нагрузки при большом количестве клиентских сессий. Чтобы минимизировать это влияние, рекомендуется запускать pgbench на отдельной машине, а не на том же сервере, где работает база данных. При этом критически важна низкая задержка в сети между клиентом и сервером.

    В некоторых случаях, особенно при оценке производительности одного сервера баз данных, имеет смысл запускать несколько экземпляров pgbench одновременно — на разных клиентских машинах — чтобы достичь необходимого уровня нагрузки без искажения результатов.

Безопасность

Не запускайте pgbench в базе данных, доступ к которой имеют ненадежные пользователи, если она не следует безопасному шаблону использования схемы.

pgbench использует неквалифицированные имена объектов и не управляет путем поиска (search_path). Это значит, что при наличии недобросовестных пользователей они могут подменить объекты в других схемах и повлиять на выполнение теста.