Caution

pgbench

pgbench —在 PostgreSQL 上运行基准测试

Synopsis

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

pgbench [ option ...] [ dbname ]

Description

pgbench 是一个用于在 PostgreSQL 上运行基准测试的简单程序。它可能在多个并发数据库会话中反复运行相同的 SQL 命令序列,然后计算平均事务速率(每秒事务数)。默认情况下,pgbench 测试基于 TPC-B 的松散方案,每个事务涉及五个SELECTUPDATEINSERT命令。但是,通过编写自己的事务脚本文件来测试其他情况很容易。

pgbench 的典型输出如下所示:

transaction type: <builtin: TPC-B (sort of)>
scaling factor: 10
query mode: simple
number of clients: 10
number of threads: 1
number of transactions per client: 1000
number of transactions actually processed: 10000/10000
tps = 85.184871 (including connections establishing)
tps = 85.296346 (excluding connections establishing)

前六行报告一些最重要的参数设置。下一行报告已完成和打算进行的 Transaction 数量(后者仅是 Client 数量与每个 ClientTransaction 数量的乘积);除非运行在完成前失败,否则它们将相等。 (在-T模式下,仅打印实际的事务数.)最后两行报告每秒的事务数,计算有无开始数据库会话的时间。

默认的类似 TPC-B 的事务测试需要预先设置特定的表。应该使用-i(初始化)选项调用 pgbench 来创建和填充这些表。 (在测试自定义脚本时,不需要此步骤,但是需要执行测试所需的任何设置.)初始化看起来像:

pgbench -i [ other-options ] dbname

其中* dbname *是已创建的要测试的数据库的名称。(您可能还需要-h-p和/或-U选项来指定如何连接到数据库服务器.)

Warning

pgbench -i创建四个表pgbench_accountspgbench_branchespgbench_historypgbench_tellers,销毁这些名称的任何现有表。如果您的表具有这些名称,请务必小心使用其他数据库!

默认的“比例因子”为 1 时,表最初包含以下行:

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

您可以(并且,出于大多数目的,应该)通过使用-s(比例因子)选项来增加行数。此时也可以使用-F(填充因子)选项。

完成必要的设置后,您可以使用不包含-i的命令来运行基准测试,即

pgbench [ options ] dbname

在几乎所有情况下,您都需要一些选项来进行有用的测试。最重要的选项是-c(Client 数量),-t(Transaction 数量),-T(时间限制)和-f(指定自定义脚本文件)。请参阅下面的完整列表。

Options

以下分为三个小节。在数据库初始化期间和运行基准测试时,会使用不同的选项,但是在两种情况下,某些选项都是有用的。

Initialization Options

pgbench 接受以下命令行初始化参数:

  • -i
    --initialize

    • 调用初始化模式时需要。
  • -I init_steps
    --init-steps=init_steps

    • 仅执行一组选定的常规初始化步骤。 * init_steps *指定要执行的初始化步骤,每步使用一个字符。每个步骤均以指定的 Sequences 调用。默认值为dtgvp。可用步骤为:
  • d(删除)

    • 删除任何现有的 pgbench 表。

    • t(创建表)

      • 创建标准 pgbench 场景使用的表,即pgbench_accountspgbench_branchespgbench_historypgbench_tellers
    • g(生成数据)

      • 生成数据并将其加载到标准表中,替换现有数据。
    • v(真空)

      • 在标准表上调用VACUUM
    • p(创建主键)

      • 在标准表上创建主键索引。
    • f(创建外键)

      • 在标准表之间创建外键约束。 (请注意,默认情况下不执行此步骤.)
  • -F fillfactor
    --fillfactor= fillfactor

    • 使用给定的 fillfactor 创建pgbench_accountspgbench_tellerspgbench_branches表。默认值为 100.
  • -n
    --no-vacuum

    • 初始化期间不执行清理操作。 (即使在-I中指定了此选项,它也会禁止v初始化步骤.)
  • -q
    --quiet

    • 将日志记录切换到安静模式,每 5 秒仅生成一条进度消息。默认日志记录每 100000 行打印一条消息,该消息通常每秒输出许多行(尤其是在良好的硬件上)。
  • -s scale_factor
    --scale= scale_factor

    • 将比例因子生成的行数相乘。例如,-s 100将在pgbench_accounts表中创建 10,000,000 行。默认值为 1.如果小数位数为 20,000 或更大,则用于容纳帐户标识符的列(aid列)将切换为使用较大的整数(bigint),以使其足够大以容纳帐户标识符的范围。
  • --foreign-keys

    • 在标准表之间创建外键约束。 (此选项将f步骤添加到初始化步骤序列中(如果尚未存在的话.)
  • --index-tablespace=index_tablespace

    • 在指定的表空间而不是默认表空间中创建索引。
  • --tablespace=tablespace

    • 在指定的表空间而不是默认表空间中创建表。
  • --unlogged-tables

    • 将所有表创建为未记录表,而不是永久表。

Benchmarking Options

pgbench 接受以下命令行基准测试参数:

  • -b scriptname[@weight]
    --builtin = scriptname[@weight]

    • 将指定的内置脚本添加到已执行脚本的列表中。 @之后的可选整数权重允许调整绘制脚本的概率。如果未指定,则将其设置为 1.可用的内置脚本为:tpcb-likesimple-updateselect-only。接受内置名称的明确前缀。使用特殊名称list,显示内置脚本列表并立即退出。
  • -c clients
    --client= clients

    • 模拟的 Client 端数,即并发数据库会话数。默认值为 1.
  • -C
    --connect

    • 为每个事务构建一个新的连接,而不是在每个 Client 端会话中仅进行一次。这对于测量连接开销很有用。
  • -d
    --debug

    • 打印调试输出。
  • -D varname = value
    --define= varname = value

    • 定义一个供自定义脚本使用的变量(请参见下文)。允许多个-D选项。
  • -f filename[@weight]
    --file= filename[@weight]

    • 将从* filename *读取的事务脚本添加到已执行脚本的列表中。 @之后的可选整数权重允许调整绘制测试的概率。有关详情,请参见下文。
  • -j threads
    --jobs= threads

    • pgbench 中的工作线程数。在多 CPU 机器上使用多个线程可能会有所帮助。Client 端在可用线程中尽可能均匀地分布。默认值为 1.
  • -l
    --log

    • 将有关每个事务的信息写入日志文件。有关详情,请参见下文。
  • -L limit
    --latency-limit= limit

    • 持续时间超过* limit 毫秒的事务将单独计算并报告为 late *。

使用限制(--rate=...)时,比计划滞后时间多* limit * ms 的事务将因此根本不发送到服务器,因此它们不希望达到延迟限制。它们被计数并分别报告为“已跳过”。

  • -M querymode
    --protocol= querymode

    • 用于向服务器提交查询的协议:
  • simple:使用简单的查询协议。

  • extended:使用扩展查询协议。

  • prepared:对准备好的语句使用扩展查询协议。

默认为简单查询协议。 (有关更多信息,请参见Chapter 53。)

  • -n
    --no-vacuum

    • 运行测试之前,请勿抽真空。如果您正在运行不包含标准表pgbench_accountspgbench_branchespgbench_historypgbench_tellers的自定义测试方案,则此选项为[必需]。
  • -N
    --skip-some-updates

    • 运行内置的简单更新脚本。 -b simple-update的简写。
  • -P sec
    --progress= sec

    • 每隔sec *秒显示进度报告。该报告包括自运行开始以来的时间,自上次报告以来的 TPS,以及自上次报告以来的事务延迟平均数和标准差。在节流(-R)下,延迟是相对于事务调度的开始时间而不是实际事务开始时间计算的,因此它还包括平均调度滞后时间。
  • -r
    --report-latencies

    • 在基准测试完成后,报告每个命令的平均每个语句延迟(从 Client 端的角度来看,执行时间)。有关详情,请参见下文。
  • -R rate
    --rate= rate

    • 执行针对指定速率的事务,而不是尽可能快地运行(默认)。速率以每秒事务数给出。如果目标费率高于最大可能费率,则费率限制不会影响结果。

通过按照 Poisson 分配的时间表时间线开始 Transaction 来确定汇率。预期的开始时间表是根据 Client 首次启动的时间而不是先前的事务结束的时间而向前移动。这种方法意味着,当 Transaction 超过其原始计划的结束时间时,以后的 Transaction 有可能再次赶上。

当节流处于活动状态时,将根据计划的开始时间计算运行结束时报告的事务延迟,因此它包括每个事务必须 await 上一个事务完成的时间。await 时间称为调度延迟时间,它的平均值和最大值也分别报告。相对于实际事务开始时间的事务 await 时间,即在数据库中执行事务所花费的时间,可以通过从报告的 await 时间中减去调度延迟时间来计算。

如果将--latency-limit--rate一起使用,则事务可能会滞后很多,以至于上一个事务结束时它已经超过了延迟限制,因为延迟是根据计划的开始时间计算的。此类事务不会发送到服务器,但会完全跳过并单独计数。

较高的调度滞后时间表示系统无法使用选定数量的 Client 端和线程以指定的速率处理事务。当平均事务执行时间长于每个事务之间的计划时间间隔时,每个连续事务将进一步落后,并且调度滞后时间将随着测试运行时间的延长而持续增加。发生这种情况时,您将不得不降低指定的 Transaction 率。

  • -s scale_factor
    --scale= scale_factor

    • 在 pgbench 的输出中报告指定的比例因子。对于内置测试,这是没有必要的。通过计算pgbench_branches表中的行数,可以检测到正确的比例因子。但是,当仅测试自定义基准(-f选项)时,除非使用此选项,否则比例因子将报告为 1.
  • -S
    --select-only

    • 运行内置的全选脚本。 -b select-only的简写。
  • -t transactions
    --transactions= transactions

    • 每个 Client 端运行的事务数。默认值是 10.
  • -T seconds
    --time= seconds

    • 运行测试持续这么长时间,而不是每个 Client 端进行固定数量的事务。 -t-T是互斥的。
  • -v
    --vacuum-all

    • 在运行测试之前,先抽真空所有四个标准表。既不使用-n也不使用-v,pgbench 将清理pgbench_tellerspgbench_branches表,并截断pgbench_history
  • --aggregate-interval=seconds

    • 聚合时间间隔的长度(以秒为单位)。只能与-l选项一起使用。使用此选项,日志包含每个时间间隔的摘要数据,如下所述。
  • --log-prefix=prefix

    • --log创建的日志文件设置文件名前缀。默认值为pgbench_log
  • --progress-timestamp

    • 显示进度(选项-P)时,请使用时间戳记(Unix 纪元),而不是自运行开始以来的秒数。单位为秒,点后为毫秒精度。这有助于比较各种工具生成的日志。
  • --random-seed= SEED

    • 设置随机生成器种子。播种系统随机数生成器,然后生成一系列初始生成器状态,每个线程一个。 * SEED *的值可以是:time(默认值,种子基于当前时间),rand(使用强随机数源,如果没有可用则失败)或无符号十进制整数值。从 pgbench 脚本(random...函数)显式调用或隐式调用随机生成器(例如,选项--rate使用它来调度事务)。明确设置后,用于播种的值将显示在终端上。 * SEED *允许的任何值也可以通过环境变量PGBENCH_RANDOM_SEED提供。为确保所提供的种子会影响所有可能的用途,请首先放置此选项或使用环境变量。

就随机数而言,显式设置种子可以精确地再现pgbench运行。由于每个线程都 Management 随机状态,因此,如果每个线程有一个 Client 端并且没有外部或数据依赖性,则对于相同的调用,完全相同的pgbench运行。从统计的角度来看,精确复制运行是一个坏主意,因为它可以隐藏性能差异或不适当地提高性能,例如点击与上次运行相同的页面。但是,它对于调试也可能有很大帮助,例如重新运行棘手的情况导致错误。明智地使用。

  • --sampling-rate=rate

    • 将数据写入日志时使用的采样率,以减少生成的日志量。如果指定了此选项,则仅记录指定部分的事务。 1.0 表示将记录所有事务,0.05 表示将仅记录 5%的事务。

在处理日志文件时,请记住要考虑采样率。例如,在计算 TPS 值时,您需要相应地将数字相乘(例如,以 0.01 的采样率,您只会得到实际 TPS 的 1/100)。

Common Options

pgbench 接受以下命令行通用参数:

  • -h hostname
    --host= hostname

    • 数据库服务器的主机名
  • -p port
    --port= port

    • 数据库服务器的端口号
  • -U login
    --username= login

    • 连接的用户名
  • -V
    --version

    • 打印 pgbench 版本并退出。
  • -?
    --help

    • 显示有关 pgbench 命令行参数的帮助,然后退出。

Notes

在 pgbench 中实际执行的“事务”是什么?

pgbench 执行从指定列表中随机选择的测试脚本。它们包括带有-b的内置脚本和带有-f的用户提供的自定义脚本。可以给每个脚本一个@之后指定的相对权重,以更改其绘制概率。默认权重为1。权重为0的脚本将被忽略。

默认的内置事务脚本(也用-b tpcb-like调用)在随机选择的aidtidbiddelta上为每个事务发出七个命令。该方案的灵感来自 TPC-B 基准,但实际上不是 TPC-B,因此得名。

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

如果您选择simple-update内置(也是-N),则事务中不包含步骤 4 和 5.这样可以避免在这些表上发生更新争用,但是这会使测试用例像 TPC-B 一样少。

如果选择内置的select-only(也为-S),则仅发出SELECT

Custom Scripts

pgbench 通过使用从文件中读取的事务脚本(-f选项)替换默认事务脚本(如上所述)来支持运行自定义基准测试方案。在这种情况下,“事务”被视为脚本文件的一次执行。

脚本文件包含一个或多个以分号终止的 SQL 命令。空行和以--开头的行将被忽略。脚本文件还可以包含“元命令”,这由 pgbench 本身解释,如下所述。

Note

在 PostgreSQL 9.6 之前,脚本文件中的 SQL 命令以换行符终止,因此不能跨行 continue。现在,“ *”号是必需的,以分隔连续的 SQL 命令(尽管如果 SQL 命令后面跟有 meta 命令,则不需要一个分号)。如果您需要创建一个适用于新旧版本 pgbench 的脚本文件,请确保将每条 SQL 命令写在以分号结尾的一行上。

脚本文件有一个简单的变量替换工具。变量名称必须由字母(包括非拉丁字母),数字和下划线组成。可以通过上面说明的命令行-D选项或下面说明的 meta 命令来设置变量。除了-D命令行选项预设的任何变量外,还有一些自动预设的变量,列在Table 241中。使用-D为这些变量指定的值优先于自动预设。设置后,可以通过写入: * variablename *将变量的值插入到 SQL 命令中。当运行多个 Client 端会话时,每个会话都有自己的一组变量。

表 241.自动变量

VariableDescription
client_id标识 Client 端会话的唯一编号(从零开始)
default_seed默认情况下,哈希函数中使用的种子
random_seed随机生成器种子(除非被-D覆盖)
scale当前比例因子

脚本文件元命令以反斜杠(\)开头,并且通常延伸到该行的末尾,尽管可以通过编写反斜杠-返回将它们 continue 到其他行。 meta 命令的参数由空格分隔。支持以下 meta 命令:

  • \if expression
    \elif expression
    \else
    \endif

    • 这组命令实现了可嵌套的条件块,类似于psql\if expression。条件表达式与\set的条件表达式相同,非零值解释为 true。
  • \set varname expression

    • 将变量* varname 设置为从 expression *计算得出的值。该表达式可以包含NULL常数,布尔常数TRUEFALSE,整数常数(例如5432),双常数(例如3.14159),对变量: * variablename *,operators的引用以及它们通常的 SQL 优先级和关联性function calls,SQL CASE 通用条件表达式和括号。

函数和大多数运算符在NULLImporting 上返回NULL

出于条件目的,非零数值是TRUE,零数值和NULLFALSE

如果没有为CASE提供最终的ELSE子句,则默认值为NULL

Examples:

\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 *引用。

Example:

\sleep 10 ms
  • \setshell varname command [ argument ... ]

    • 将变量* varname 设置为具有给定 argument (s)的 shell 命令 command *的结果。该命令必须通过其标准输出返回一个整数值。
  • command 和每个 argument *可以是文本常量,也可以是对变量的: * variablename 引用。如果要使用以冒号开头的 argument ,请在 argument *的开头写入另一个冒号。

Example:

\setshell variable_to_be_assigned command literal_argument :variable ::literal_starting_with_colon
  • \shell command [ argument ... ]

    • \setshell相同,但是命令的结果将被丢弃。

Example:

\shell command literal_argument :variable ::literal_starting_with_colon

Built-In Operators

Table 242中列出的算术,按位,比较和逻辑运算符内置在 pgbench 中,可以在\set中出现的表达式中使用。

表 242. pgbench 运算符的优先级提高

OperatorDescriptionExampleResult
ORlogical or5 or 0TRUE
ANDlogical and3 and 0FALSE
NOTlogical notnot falseTRUE
IS [NOT] (NULL|TRUE|FALSE)value tests1 is nullFALSE
ISNULL|NOTNULLnull tests1 notnullTRUE
=is equal5 = 4FALSE
<>不相等5 <> 4TRUE
!=不相等5 != 5FALSE
<lower than5 < 4FALSE
<=低于或等于5 <= 4FALSE
>greater than5 > 4TRUE
>=大于或等于5 >= 4TRUE
|整数按位或1 | 23
#整数按位异或1 # 32
&整数按位与1 & 31
~整数按位非~ 1-2
<<整数左移1 << 24
>>整数按位右移8 >> 22
+addition5 + 49
-subtraction3 - 2.01.0
*multiplication5 * 420
/除(整数将结果截断)5 / 31
%modulo3 % 21
-opposite- 2.0-2.0

Built-In Functions

Table 243中列出的函数内置在 pgbench 中,可以在\set中出现的表达式中使用。

表 243. pgbench 函数

FunctionReturn TypeDescriptionExampleResult
abs(a)与* a *相同absolute valueabs(-17)17
debug(a)与* a *相同打印* a 到 stderr,然后返回 a *debug(5432.1)5432.1
double(i)double投双double(5432)5432.0
exp(x)doubleexponentialexp(1.0)2.718281828459045
greatest(a [, ... ] )如果有任何* a *为 double,则为 double,否则为整数参数中的最大值greatest(5, 4, 3, 2)5
hash(a [, seed ] )integerhash_murmur2()的别名hash(10, 5432)-5817877081768721676
hash_fnv1a(a [, seed ] )integerFNV-1a hashhash_fnv1a(10, 5432)-7793829335365542153
hash_murmur2(a [, seed ] )integerMurmurHash2 hashhash_murmur2(10, 5432)-5817877081768721676
int(x)integer转换为 intint(5.4 + 3.8)9
least(a [, ... ] )如果有任何* a *为 double,则为 double,否则为整数参数中的最小值least(5, 4, 3, 2.1)2.1
ln(x)doublenatural logarithmln(2.718281828459045)1.0
mod(i, j)integermodulomod(54, 32)22
pi()double常数 PI 的值pi()3.14159265358979323846
pow(x, y), power(x, y)doubleexponentiationpow(2.0, 10) , power(2.0, 10)1024.0
random(lb, ub)integer[lb, ub]中均匀分布的随机整数random(1, 10)110之间的整数
random_exponential(lb, ub, parameter)integer[lb, ub]中的指数分布随机整数,请参见下文random_exponential(1, 10, 3.0)110之间的整数
random_gaussian(lb, ub, parameter)integer[lb, ub]中的高斯分布随机整数,请参见下文random_gaussian(1, 10, 2.5)110之间的整数
random_zipfian(lb, ub, parameter)integer[lb, ub]中的 Zipfian 分布随机整数,请参见下文random_zipfian(1, 10, 1.5)110之间的整数
sqrt(x)doublesquare rootsqrt(2.0)1.414213562

random函数使用均匀分布生成值,即所有值均以相等的概率绘制在指定范围内。 random_exponentialrandom_gaussianrandom_zipfian函数需要一个附加的 double 参数,该参数确定分布的精确形状。

  • 对于指数分布,* parameter 通过在 parameter *处截断快速递减的指数分布,然后投影到边界之间的整数上来控制分布。确切地说,

f(x)= exp(-参数*(x-最小)/(max-最小 1))/(1-exp(-参数))

然后以f(i) - f(i + 1)的概率得出* min max *之间的值i1 *。

直观地,* parameter 越大,访问 min 的值的频率越高,访问 max *的值的频率越低。越接近 0 * parameter ,访问分布越平坦(越均匀)。分布的粗略估计是,在 min 范围内,最接近 1%的值被绘制为 parameter *%的时间。 * parameter *值必须严格为正。

  • 对于高斯分布,间隔被 Map 到标准正态分布(经典的钟形高斯曲线),该正态分布在左侧的-parameter和右侧的+parameter处被截断。间隔中间的值更可能被绘制。确切地说,如果PHI(x)是标准正态分布的累 Integration 布函数,且均值mu定义为(max + min) / 2.0,则

f(x)= PHI(2.0 参数(x-mu)/(max-min 1))/
(2.0 * PHI(参数)-1)

然后以概率f(i + 0.5) - f(i - 0.5)绘制* min max *之间的值i1 。直观地, parameter 越大,绘制到区间中间的值越频繁,而接近 min max 边界的值越少。大约 67%的值来自中间的1.0 / parameter,即平均值附近的相对0.5 / parameter,而 95%的中间值2.0 / parameter则是平均值附近的1.0 / parameter;例如,如果 parameter 为 4.0,则从间隔的中间四分之一(1.0/4.0)(即从3.0 / 8.05.0 / 8.0)中抽取 67%的值,从间隔的中间一半(2.0 / 4.0)中抽取 95%的值(第二和第三个四分位数)。对于 Box-Muller 变换,最小值 parameter *为 2.0.

  • random_zipfian生成近似有界 Zipfian 分布。对于(0,1)中的* parameter ,从吉姆·格雷(Jim Gray)等人的“快速生成十亿记录的合成数据库”中获取近似算法,SIGMOD1994.对于(1,1000)中的 parameter *,则拒绝方法是使用,基于“非均匀随机变量生成”,Luc Devroye,p。 550-551,Springer1986.当参数值为 1.0 时,未定义分布。当参数值接近且大于 1.0 且在较小范围内时,该函数的性能较差。
  • parameter *定义分布的偏斜度。 * parameter *越大,越接近间隔的开始绘制值。越接近 0 * parameter ,输出分布越平坦(越均匀)。这种分布使得假设范围从 1 开始,绘制 k 与绘制 k+1 *的概率之比为((k+1)/k)**parameter。例如,random_zipfian(1, ..., 2.5)产生值1的频率是2(2/1)**2.5 = 5.66倍,而1产生的值本身是3 (3/2)**2.5 = 2.76倍,依此类推。

哈希函数hashhash_murmur2hash_fnv1a接受 Importing 值和可选的种子参数。如果未提供种子,则使用:default_seed的值,除非通过命令行-D选项设置,否则它将随机初始化。哈希函数可用于分散随机函数(例如random_zipfianrandom_exponential)的分布。例如,以下 pgbench 脚本模拟了社交媒体和博客平台典型的可能的现实工作负载,其中很少有帐户会产生过多的负载:

\set r random_zipfian(0, 100000000, 1.07)
\set k abs(hash(:r)) % 1000000

在某些情况下,需要几个彼此不相关的不同分布,这在隐式种子参数派上用场时:

\set k1 abs(hash(:r, :default_seed + 123)) % 1000000
\set k2 abs(hash(:r, :default_seed + 321)) % 1000000

例如,内置 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;

该脚本允许事务的每次迭代引用不同的随机选择的行。 (此示例还显示了为什么每个 Client 端会话都有自己的变量很重要,否则它们将不会独立地涉及不同的行.)

Per-Transaction Logging

使用-l选项(但不使用--aggregate-interval选项),pgbench 将有关每个事务的信息写入日志文件。日志文件将命名为prefix.nnn,其中* prefix 默认为pgbench_log nnn 是 pgbench 进程的 PID。可以使用--log-prefix选项更改前缀。如果-j选项为 2 或更高,则有多个工作线程,每个工作线程都有自己的日志文件。第一个工作程序将在其日志文件中使用与标准单工作程序情况下相同的名称。其他工作程序的其他日志文件将命名为prefix.nnn.mmm,其中 mmm *是每个工作程序从 1 开始的序号。

日志格式为:

client_id transaction_no time script_no time_epoch time_us [ schedule_lag ]

其中* client_id 指示哪个 Client 端会话运行了该事务, transaction_no 计算该会话已运行了多少个事务, time 是经过的总事务处理时间(以微秒为单位), script_no 标识使用了哪个脚本文件(使用-f-b指定了多个脚本,并且 time_epoch / time_us *是 Unix 时代的时间戳,其偏移量以微秒为单位(适用于创建带有小数秒的 ISO 8601 时间戳),用于显示 Transaction 完成的时间。 * schedule_lag 字段是事务的计划开始时间与实际开始时间之间的差,以微秒为单位。仅在使用--rate选项时存在。当同时使用--rate--latency-limit时,跳过事务的 time *将报告为skipped

这是在单 Client 端运行中生成的日志文件的片段:

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

另一个带有--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 晚了,因为它的 await 时间(6.173 ms)超过了 5 ms 的限制。接下来的两个事务被跳过,因为它们已经很晚才开始。

在可以处理大量事务的硬件上进行长时间测试时,日志文件可能会变得非常大。 --sampling-rate选项只能用于记录随机的事务 samples。

Aggregated Logging

使用--aggregate-interval选项,日志文件将使用其他格式:

interval_start num_transactions sum_latency sum_latency_2 min_latency max_latency [ sum_lag sum_lag_2 min_lag max_lag [ skipped ] ]

其中* interval_start 是间隔的开始(以 Unix 纪元时间戳记), num_transactions 是间隔内的事务数, sum_latency 是间隔内的事务延迟之和, sum_latency_2 是间隔内事务延迟的平方和, min_latency 是间隔内的最小延迟, max_latency 是间隔内的最大延迟。接下来的字段 sum_lag sum_lag_2 min_lag max_lag 仅在使用--rate选项时出现。它们提供有关每个事务必须 await 上一个事务完成的时间的统计信息,即每个事务的计划开始时间与实际开始时间之间的差额。仅当也使用--latency-limit选项时,才出现最后一个字段 skipped *。它计算因 Transaction 开始得太晚而跳过的 Transaction 数量。每个事务在提交后的时间间隔中进行计数。

这是一些示例输出:

1345828501 5601 1542744 483552416 61 2573
1345828503 7884 1979812 565806736 60 1479
1345828505 7208 1979422 567277552 59 1391
1345828507 7685 1980268 569784714 60 1398
1345828509 7073 1979779 573489941 236 1411

请注意,虽然普通(未聚合)日志文件显示了每个事务使用的脚本,但聚合日志未显示。因此,如果需要按脚本的数据,则需要自己汇总数据。

Per-Statement Latencies

使用-r选项,pgbench 会收集每个 Client 端执行的每个语句的经过的事务时间。然后,它会在基准测试完成后报告这些值的平均值,称为每个语句的 await 时间。

对于默认脚本,输出将类似于以下内容:

starting vacuum...end.
transaction type: <builtin: TPC-B (sort of)>
scaling factor: 1
query mode: simple
number of clients: 10
number of threads: 1
number of transactions per client: 1000
number of transactions actually processed: 10000/10000
latency average = 15.844 ms
latency stddev = 2.715 ms
tps = 618.764555 (including connections establishing)
tps = 622.977698 (excluding connections establishing)
statement latencies in milliseconds:
        0.002  \set aid random(1, 100000 * :scale)
        0.005  \set bid random(1, 1 * :scale)
        0.002  \set tid random(1, 10 * :scale)
        0.001  \set delta random(-5000, 5000)
        0.326  BEGIN;
        0.603  UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;
        0.454  SELECT abalance FROM pgbench_accounts WHERE aid = :aid;
        5.528  UPDATE pgbench_tellers SET tbalance = tbalance + :delta WHERE tid = :tid;
        7.335  UPDATE pgbench_branches SET bbalance = bbalance + :delta WHERE bid = :bid;
        0.371  INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP);
        1.212  END;

如果指定了多个脚本文件,则将分别报告每个脚本文件的平均值。

请注意,收集每个语句 await 时间计算所需的其他时序信息会增加一些开销。这将降低平均执行速度并降低计算的 TPS。减速的程度因平台和硬件而异。比较启用和未启用延迟报告的平均 TPS 值是衡量计时开销是否重大的一种好方法。

Good Practices

使用 pgbench 产生完全没有意义的数字非常容易。以下是一些指南,可帮助您获得有用的结果。

首先,永远不要相信任何运行仅几秒钟的测试。使用-t-T选项可以使运行至少持续几分钟,以平均噪音。在某些情况下,您可能需要几个小时才能获得可重现的数字。尝试运行几次测试是一个好主意,以查明您的数字是否可重复。

对于默认的类似 TPC-B 的测试方案,初始化比例因子(-s)至少应与要测试的最大 Client 端数量(-c)一样大。否则,您将主要衡量更新争用。 pgbench_branches表中只有-s行,并且每个事务都想要更新其中之一,因此-c值超过-s无疑会导致许多事务被阻塞,以 await 其他事务。

默认的测试方案对于初始化表以来已经有多长时间非常敏感:死行和死空间的累积会改变结果。要了解结果,您必须跟踪更新的总数以及清理发生的时间。如果启用了自动真空,则可能会导致测量性能发生不可预测的变化。

pgbench 的局限性在于,它在尝试测试大量 Client 端会话时本身可能成为瓶颈。可以通过在与数据库服务器不同的机器上运行 pgbench 来缓解这种情况,尽管低网络延迟是必不可少的。在多台 Client 端计算机上针对同一数据库服务器同时运行多个 pgbench 实例甚至可能很有用。

Security

如果不受信任的用户可以访问未采用安全模式使用模式的数据库,则不要在该数据库中运行 pgbench。 pgbench 使用非限定名称,并且不操纵搜索路径。