Postgresql 中文操作指南

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
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)

前七行报告了一些最重要的参数设置。第六行报告事务序列化或死锁错误的最大尝试次数(有关详细信息,请参见 Failures and Serialization/Deadlock Retries )。第八行报告完成和预期的事务数(后者只是客户端数和每个客户端事务数的乘积);除非运行在完成前失败或某些 SQL 命令失败,否则这两者将相等。(在 -T 模式中,只会打印实际事务数。)下一行报告由于序列化或死锁错误而导致的事务失败数(有关详细信息,请参见 Failures and Serialization/Deadlock Retries )。最后一行报告每秒事务数。

默认的类似 TPC-B 的事务测试需要事先设置特定表。应使用 -i (初始化)选项调用 pgbench 以创建和填充这些表。(当您测试自定义脚本时,您不需要此步骤,但需要按照您的测试需要进行任何设置。)初始化如下所示:

pgbench -i [ other-options ] dbname

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

Caution

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 (客户端数量)、 -t (交易数量)、 -T (时间限制)和 -f (指定自定义脚本文件)。请参阅下列内容以获取完整列表。

Options

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

Initialization Options

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

  • dbname #

    • 指定待测试的数据库的名称。如果不指定,则使用环境变量 PGDATABASE 。如果未设置,则使用为连接指定的用户名。

  • -i_—​initialize_ #

    • 调用初始化模式时必填。

  • -I _init_steps—​init-steps=_init_steps #

    • 仅执行选择的常规初始化步骤集。 init_steps 指定要执行的初始化步骤,每一步使用一个字符。每个步骤按指定顺序调用。默认值为 dtgvp 。可用的步骤有:

  • -F fillfactor—​fillfactor=fillfactor #

    • 使用给定的填充因子创建 pgbench_accountspgbench_tellerspgbench_branches 表。默认值为100。

  • -n_—​no-vacuum_ #

    • 在此期间不执行真空。 (即使在 -I 中指定了此选项,此选项也会禁止执行 v 初始化步骤。)

  • -q_—​quiet_ #

    • 将日志记录切换为安静模式,每5秒仅生成一条进度消息。默认日志记录每100,000行打印一条消息,这通常每秒会输出许多行(特别是在良好的硬件上)。

    • 如果在 -I 中指定了 G ,则此设置无效。

  • -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_ #

    • 在指定表空间中创建索引,而不是默认表空间。

  • —​partition-method=_NAME_ #

    • 使用 NAME 方法创建分区 pgbench_accounts 表。预期值是 rangehash 。此选项要求将 &#8212;&#8203;partitions 设置为非零。如果未指定,则默认为 range

  • —​partitions=_NUM_ #

    • 使用 NUM 个大小几乎相等的区块创建分区 pgbench_accounts 表,针对已调整数量的帐户。默认值为 0 ,表示无分区。

  • —​tablespace=_tablespace_ #

    • 在指定表空间中创建表,而不是默认表空间。

  • —​unlogged-tables #

    • 将所有表创建为非日志表,而不是永久表。

  • d (Drop) #

    • 删除任何现有的 pgbench 表。

  • t (create Tables) #

    • 创建标准 pgbench 方案中的表,即 pgbench_accountspgbench_branchespgbench_historypgbench_tellers

  • gG (生成数据,客户端或服务器端) #

    • 生成数据并将其加载到标准表中,替换已存在的任何数据。

    • 使用 g (客户端数据生成),数据是在 pgbench 客户端生成,然后发送到服务器。这会通过 COPY 大量使用客户端/服务器带宽。 pgbench 使用 PostgreSQL 14 或更高版本的 FREEZE 选项来加速后续 VACUUM ,除非启用分区。使用 g 使得日志在为 pgbench_accounts 表生成数据时每 100,000 行打印一条消息。

    • 使用 G (服务器端数据生成),仅从 pgbench 客户端发送少量查询,然后仅在服务器中生成数据。此变体不需要大量的带宽,但服务器的工作量会增加。使用 G 使得日志在生成数据时不会打印任何进度消息。

    • 默认的初始化行为使用客户端数据生成(相当于 g )。

  • v (Vacuum) #

    • 对标准表调用 VACUUM

  • p (创建主键) #

    • 在标准表上创建主键索引。

  • f (创建外键) #

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

Benchmarking Options

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

  • -b scriptname[@weight]_—​builtin_=scriptname[@weight] #

    • 将指定内置脚本添加到要执行的脚本列表中。可用的内置脚本为: tpcb-likesimple-updateselect-only 。接受内置名称的明确前缀。使用特殊名称 list ,显示内置脚本列表,然后立即退出。

    • 对于 @ ,可以选择写一个整数值的权重,用以调整相对于其他脚本选择该脚本的可能性。默认权重为 1。参见下文了解详细信息。

  • -c clients—​client=clients #

    • 模拟的客户端数,即并发的数据库会话数。默认值为 1。

  • -C_—​connect_ #

    • 为每个事务建立一个新连接,而不是每个客户端会话只建立一个。这对于衡量连接开销很有用。

  • -d_—​debug_ #

    • Print debugging output.

  • -D varname=value—​define=varname=value #

    • 定义一个变量,供自定义脚本使用(参见下文)。允许使用多个 -D 选项。

  • -f filename—​file=filename[@weight] #

    • 向要执行的脚本列表中添加从 filename 读入的交易脚本。

    • 您可以在 @ 后选择性地写入一个整数权重,以调整选择此脚本(与其它脚本相对)的概率。默认权重为 1。(若要使用包含 @ 字符的脚本文件名称,请附加权重,这样就没有歧义了,例如 filen@me@1 。)有关详细信息,请见下方。

  • -j threads—​jobs=threads #

    • pgbench 中的后台线程数。在多 CPU 机器上,使用一个以上的线程可能有所帮助。客户端会尽可能平均地分配给可用线程。默认值为 1。

  • -l_—​log_ #

    • 将有关每个交易的信息写入日志文件。有关详细信息,请见下方。

  • -L limit—​latency-limit=limit #

    • 持续时间超过 limit 毫秒的交易会被计数并单独报告,称为 late

    • 当使用速控时( &#8212;&#8203;rate=&#8230;&#8203; ),落后计划 أكثر من limit 毫秒的交易,没有希望满足延迟极限,不会发送到服务器。它们会被计数并单独报告为 skipped

    • 当使用 &#8212;&#8203;max-tries 选项时,如果交易因序列化异常或死锁而失败,则当所有尝试的总时间超过 limit 毫秒时,将不会重试。若要仅限制尝试的时间而不是尝试数,请使用 &#8212;&#8203;max-tries=0 。默认情况下,选项 &#8212;&#8203;max-tries 设置为 1,有序列化/死锁错误的交易不会重试。请参阅 Failures and Serialization/Deadlock Retries ,以获取有关重试此类交易的更多信息。

  • -M querymode—​protocol=querymode #

    • 用于向服务器提交查询的协议:

    • prepared 模式下,pgbench 从第二次查询迭代开始重新使用解析分析结果,因此,pgbench 比其它模式下运行速度更快。

    • 默认值为简单查询协议。(请参阅 Chapter 55 了解更多信息。)

  • -n_—​no-vacuum_ #

    • 在运行测试之前不执行 VACUUM 操作。如果您正在运行不包含标准表 pgbench_accountspgbench_branchespgbench_historypgbench_tellers 的自定义测试场景,则此选项为 necessary

  • -N_—​skip-some-updates_ #

    • 运行内置的简单更新脚本。相当于 -b simple-update

  • -P sec—​progress=sec #

    • sec 秒显示进度报告。此报告包括从运行开始以来的时间、自上一次报告以来的 TPS、交易延迟平均值、标准差和自上一次报告以来的失败事务数。在速控( -R )下,延迟是相对于交易计划的开始时间(而不是实际的交易开始时间)计算的,因此还包括平均计划延迟时间。当 &#8212;&#8203;max-tries 用于在序列化/死锁错误后启用事务重试时,此报告包括重试事务的数量和所有重试的总和。

  • -r_—​report-per-command_ #

    • 基准完成后为每个命令报告以下统计数据:每个语句的平均延迟(从客户端角度来看的执行时间)、失败的数量和此命令中序列化或死锁错误后的重试次数。仅当 &#8212;&#8203;max-tries 选项不等于 1 时,此报告才会显示重试统计数据。

  • -R rate—​rate=rate #

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

    • 通过沿着泊松分布的计划时间线启动交易来设定速率的目标。预期的开始时间计划会根据客户端首次启动的时间向前移动,而不是根据前一个交易结束的时间移动。这意味着当交易超出其原始计划结束时间时,后面的交易有可能赶上来。

    • 在速控处于活动状态时,运行结束时报告的交易延迟是从计划的开始时间计算的,因此它包括了每个交易为等待前一个交易结束而不得不等待的时间。等待时间称为计划延迟时间,它的平均值和最大值也单独报告。相对于实际交易开始时间的交易延迟(即在数据库中执行交易时花费的时间),可以通过从报告的延迟中减去计划延迟时间计算。

    • 如果 &#8212;&#8203;latency-limit&#8212;&#8203;rate 一起使用,则交易可能会延迟很多,以致当前一个交易结束时,它已经超过延迟限制,因为延迟是从计划的开始时间计算的。此类交易不会发送到服务器,而是直接跳过并单独计数。

    • 较长的计划延迟时间表明系统无法以所选的客户端和线程数以指定速率处理交易。当平均交易执行时间长于每个交易之间的计划间隔时,每个连续的交易都会进一步落后,计划延迟时间将随着测试运行时间的延长而持续增加。发生这种情况时,您必须降低指定的交易速率。

  • -s scale_factor—​scale=scale_factor #

    • 在 pgbench 的输出中报告指定比例因子。对于内置的测试,这没有必要;正确的比例因子将通过计算 pgbench_branches 表中的行数来检测。然而,当仅测试自定义 benchmark( -f 选项)时,此比例因子将被报告为 1,除非使用了此选项。

  • -S_—​select-only_ #

    • 运行内置的仅选择脚本。 -b select-only 的简写。

  • -t transactions—​transactions=transactions #

    • 每个客户端运行的事务数。默认值为 10。

  • -T seconds—​time=seconds #

    • 运行测试的时间为这么长(以秒为单位),而不是每个客户端的固定事务数。 -t-T 互斥。

  • -v_—​vacuum-all_ #

    • 在运行测试之前对所有四个标准表进行 Vacuum。如果没有 -n-v ,pgbench 将对 pgbench_tellerspgbench_branches 表进行 Vacuum,并将截断 pgbench_history

  • —​aggregate-interval=_seconds_ #

    • 聚合间隔的长度(以秒为单位)。仅可与 -l 选项一起使用。使用此选项,日志将包含按间隔划分的摘要数据,如下所述。

  • —​failures-detailed #

  • —​log-prefix=_prefix_ #

    • 设置 &#8212;&#8203;log 创建的日志文件的 filename 前缀。默认值为 pgbench_log

  • —​max-tries=_number_of_tries_ #

    • 对具有序列化/死锁错误的事务启用重试,并设置这些尝试的最大次数。此选项可以与 &#8212;&#8203;latency-limit 选项结合使用,该选项限制所有事务尝试的总时间;此外,如果没有 &#8212;&#8203;latency-limit&#8212;&#8203;time ,则不能使用无限次数的尝试( &#8212;&#8203;max-tries=0 )。默认值为 1,且不会重试具有序列化/死锁错误的事务。有关重试此类事务的更多信息,请参阅 Failures and Serialization/Deadlock Retries

  • —​progress-timestamp #

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

  • —​random-seed=__seed #

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

    • 显式设置种子允许准确重现 pgbench 运行,只要涉及到随机数。由于随机状态由每个线程管理,这意味着对于相同的调用,如果每个线程有一个客户端且没有外部或数据依赖关系,则 pgbench 运行完全相同。从统计学的角度来看,精确重现运行是不好的,因为它可以掩盖性能差异或不恰当地提高性能,例如,访问与之前运行相同的页面。但是,它对调试也可能很有帮助,例如重新运行导致错误的棘手案例。请谨慎使用。

  • —​sampling-rate=_rate_ #

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

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

  • —​show-script=__scriptname #

    • 在 stderr 上显示内置脚本 scriptname 的实际代码,然后立即退出。

  • —​verbose-errors #

    • 打印所有错误和故障(未重试的错误)的消息,包括超过了哪个重试限制以及对于序列化/死锁故障超出了多少。(请注意,在这种情况下,输出可能会显著增加。)。有关更多信息,请参阅 Failures and Serialization/Deadlock Retries

Common Options

pgbench 还接受以下常见的连接参数命令行参数:

  • -h hostname—​host=hostname #

    • 数据库服务器的主机名

  • -p port—​port=port #

    • 数据库服务器端口号

  • -U login—​username=login #

    • 用户连接名

  • -V_—​version_ #

    • 打印 pgbench 版本并退出。

  • -?_—​help_ #

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

Exit Status

成功运行将退出状态 0。退出状态 1 表示静态问题,例如无效的命令行选项或应永远不会发生的内部错误。在启动基准测试时发生的早期错误,例如初始连接失败,也会以状态 1 退出。运行期间发生的错误(例如数据库错误或脚本中的问题)将导致退出状态 2。在后一种情况下,pgbench 将打印部分结果。

Environment

  • PGDATABASE_PGHOST_PGPORT__PGUSER #

    • Default connection parameters.

此实用工具与大多数其他 PostgreSQL 实用工具类似,它使用 libpq 所支持的环境变量(请参阅 Section 34.15 )。

环境变量 PG_COLOR 指定是否在诊断消息中使用颜色。可能的值是 alwaysautonever

Notes

What Is the “Transaction” Actually Performed in pgbench?

pgbench 根据从指定列表中随机选取的测试脚本执行。脚本可能包含使用 -b 指定的内置脚本和使用 -f 指定的用户提供的脚本。为每个脚本指定 @ 后指定的相对权重,以更改其选择概率。默认权重是 1 。权重为 0 的脚本将被忽略。

默认内置事务脚本(也随 -b tpcb-like 一起调用)在随机选取的 aidtidbiddelta 上为每个事务发出七个命令。场景源于 TPC-B 基准测试,但实际上并不是 TPC-B,因此得名。

如果您选择内置 simple-update (也为 -N ),那么步骤 4 和 5 将不会包含在事务中。这样可以避免这些表上的更新争用,但也会让测试用例与 TPC-B 相似度更低。

如果您选择内置 select-only (也为 -S ),那么只发出了 SELECT

Custom Scripts

通过使用从文件( -f 选项)读取的事务脚本替换上述默认事务脚本,pgbench 可以支持运行自定义基准测试场景。在这种情况下,“事务”被记作一次执行脚本文件。

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

Note

在 PostgreSQL 9.6 之前,脚本文件中的 SQL 命令以新行结尾,因此它们无法跨行继续。现在,分号用来 required 分隔连续的 SQL 命令(尽管如果 SQL 命令后面是元命令,则不需要分号)。如果您需要创建一个同时适用于 pgbench 旧版本和新版本的脚本文件,请务必将每个 SQL 命令写在以分号结尾的单行上。

pgbench 脚本假设不包含不完整的 SQL 事务块。如果在运行时客户端在未完成最后一个事务块的情况下到达脚本末尾,那么将中止事务块。

脚本文件有一个简单的变量替换工具。变量名称必须包含字母(包括非拉丁字母)、数字和下划线,其首字母不得是数字。可以通过上面解释的命令行 -D 选项,或下面解释的元命令来设置变量。除了 -D 命令行选项预设的任何变量,还有一些预先自动设置的变量,如 Table 293 中所列。使用 -D 指定的这些变量值优先于自动预设值。设置后,可以通过编写 :__variablename 将变量值插入 SQL 命令中。当运行多于一个客户端会话时,每个会话都有自己的一组变量。pgbench 支持在一个语句中最多使用 255 个变量。

Table 293. pgbench Automatic Variables

Variable

Description

client_id

识别客户端会话的唯一数字(从 0 开始)

default_seed

默认情况下在散列和伪随机排列函数中使用的种子

random_seed

随机生成器种子(除非使用 -D 覆盖)

scale

current scale factor

脚本文件元命令以反斜杠 ( \ ) 开头,并且通常一直延伸到该行末尾,但可以通过编写反斜杠-换行符来继续到其他行。对元命令提供的参数由空格分隔。支持以下元命令:

  • \gset [_prefix ]_ \aset [_prefix ]_ #

    • 这些命令可用来结束 SQL 查询,代替终止分号 ( ; )。

    • 当使用了 \gset 命令时,前面的 SQL 查询预计返回一行,其列存储到以列名为名的变量中,并且在提供的情况下以 prefix 为前缀。

    • 当使用了 \aset 命令时,所有合并的 SQL 查询(由 \; 分隔)的列都存储到以列名为名的变量中,并且在提供的情况下以 prefix 为前缀。如果查询没有返回任何行,则不会进行分配并且可测试变量是否存在以检测这种情况。如果查询返回多行,则保持最后的值。

    • \gset\aset 不能在管道模式中使用,因为在需要用到命令时查询结果尚不可用。

    • 下面的示例将第一个查询的最终帐户余额放入变量 abalance ,并将变量 p_twop_three 填入第三个查询中的整数。第二个查询的结果被丢弃。最后两个合并查询的结果存储在变量 fourfive 中。

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_ #

    • 这组命令实现可嵌套条件块,类似于 psql\if expression 。条件表达式与 \set 中的一样,其中非零值解释为真。

  • \set _varname expression_ #

    • 将变量 varname 设置为根据 expression 计算的值。表达式可能包含 NULL 常量、布尔常量 TRUEFALSE 、整数常量(如 5432 )、双精度常量(如 3.14159 )、对变量 :__variablenameoperators 的引用(具有其通常的 SQL 优先级和关联性)、 function calls 、SQL CASE generic conditional expressions 和括号。

    • 函数和大多数运算符在 NULL 输入上返回 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
  • \startpipeline_\endpipeline_ #

    • 这些命令限定了一系列 SQL 语句的开始和结束。在管道模式中,语句被发送到服务器,而无需等待前一条语句的结果。有关更多详细信息,请参见 Section 34.5 。管道模式需要使用扩展查询协议。

Built-in Operators

Table 294 中列出的算术、位运算、比较和逻辑运算符内置于 pgbench 中,并且可以用在 \set 中出现的表达式中。这些运算符按优先级顺序排列。除非另有说明,否则采用两个数字输入的运算符将在任一输入为双精度时生成双精度值,否则生成整数结果。

Table 294. pgbench Operators

Operator

Description

Example(s)

boolean OR booleanboolean 逻辑 OR 5 or 0TRUE

boolean AND booleanboolean 逻辑 AND 3 and 0FALSE

NOT booleanboolean 逻辑 NOT not falseTRUE

boolean _IS [NOT] (NULL

TRUE

FALSE)_ → boolean 布尔值测试 1 is nullFALSE

value _ISNULL

NOTNULL_ → boolean 空值测试 1 notnullTRUE

number = numberboolean 等于 5 = 4FALSE

number &lt;&gt; numberboolean 不等于 5 &lt;&gt; 4TRUE

number != numberboolean 不等于 5 != 5FALSE

number &lt; numberboolean 小于 5 &lt; 4FALSE

number &#8656; numberboolean 小于或等于 5 &#8656; 4FALSE

number &gt; numberboolean 大于 5 &gt; 4TRUE

number &gt;= numberboolean 大于或等于 5 &gt;= 4TRUE

integer _

_ integerinteger 按位 OR_1

2_ → 3

integer # integerinteger 按位异或 1 # 32

integer &amp; integerinteger 按位 AND 1 &amp; 31

~ integerinteger 按位 NOT ~ 1-2

integer &lt;&lt; integerinteger 左移位 1 &lt;&lt; 24

integer &gt;&gt; integerinteger 右移位 8 &gt;&gt; 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

Built-In Functions

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

Table 295. pgbench Functions

Function

Description

Example(s)

abs ( number ) → 与输入相同的类型绝对值 abs(-17)17

debug ( number ) → 与输入相同的类型将参数打印到 stderr,并返回该参数。 debug(5432.1)5432.1

double ( number ) → double 转换为双精度。 double(5432)5432.0

exp ( number ) → doublee 作为底数求 e 的幂 exp(1.0)2.718281828459045

greatest ( number [, &#8230;&#8203; ] ) → double 如果任何参数为双精度,则选择 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 hashhash_fnv1a(10, 5432)-7793829335365542153

hash_murmur2 ( value [, seed ] ) → integer 计算 MurmurHash2 hashhash_murmur2(10, 5432)-5817877081768721676

int ( number ) → integer 转换为整数。 int(5.4 + 3.8)9

leastnumber [, &#8230;&#8203; ] )→ double 如果任意参数是双精度,否则为 integer 在参数中选择最小值。 least(5, 4, 3, 2.1)2.1

lnnumber ) → double 自然对数 ln(2.718281828459045)1.0

modintegerinteger ) → integer 模数(余数) mod(54, 32)22

permuteisize [, seed ] ) → integer 在范围 [0, size) 内的 i 的排列值。这是 i (模数 size )在由 seed 参数化的整数的伪随机排列中新的位置,参见以下。 permute(0, 4)an integer between 0 and 3

pi () → double π 的近似值 pi()3.14159265358979323846

powxy ) → double powerxy ) → double x 提升到 y 的幂 pow(2.0, 10)1024.0

randomlbub ) → integer[lb, ub] 中计算一个均匀分布的随机整数。 random(1, 10)an integer between 1 and 10

random_exponentiallbubparameter ) → integer[lb, ub] 中计算一个指数分布的随机整数,参见以下。 random_exponential(1, 10, 3.0)an integer between 1 and 10

random_gaussianlbubparameter ) → integer[lb, ub] 中计算一个高斯分布的随机整数,参见以下。 random_gaussian(1, 10, 2.5)an integer between 1 and 10

random_zipfianlbubparameter ) → integer[lb, ub] 中计算一个齐夫分布的随机整数,参见以下。 random_zipfian(1, 10, 1.5)an integer between 1 and 10

sqrtnumber ) → double 平方根 sqrt(2.0)1.414213562

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

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

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

Note

在设计非均匀选择行的基准时,请注意所选的行可能与其他数据相关,例如序列的 ID 或物理行顺序,这可能会影响性能度量。

为了避免这种情况,您可能希望使用 permute 函数或具有类似效果的其他附加步骤来对所选行进行混洗并去除此类关联。

哈希函数 hashhash_murmur2hash_fnv1a 接受一个输入值和一个可选的种子参数。如果未提供种子,则使用 :default_seed 的值,除非由命令行 -D 选项设置,否则将随机初始化此值。

permute 接受一个输入值、一个大小和一个可选的种子参数。它将生成范围内整数的伪随机排列 [0, size) ,并返回排列值中输入值的位置。所选的排列由种子参数化,如果未指定,则默认为 :default_seed 。与哈希函数不同, permute 确保输出值中没有冲突或空位。区间外部的输入值将解释为模数大小。如果大小不为正,则函数将引发错误。 permute 可用于分散诸如 random_zipfianrandom_exponential 等非均匀随机函数的分布,以便绘制更多的值不会出现简单的相关性。例如,以下 pgbench 脚本模拟了社交媒体和博客平台中的可能真实工作负载,其中几个帐户会产生过多的负载:

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

在某些情况下,需要几个互不关联的不同分布,这时可选种子参数就派上用场了:

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

此脚本允许事务的每次迭代引用不同的随机选择的行。(此示例还显示了每个客户端会话拥有自己的变量的重要性,否则它们不会独立接触不同的行。)

Per-Transaction Logging

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

日志文件中的每一行都描述一个事务。它包含以下空格分隔的字段:

  • client_id

    • 识别运行了事务的客户端会话

  • transaction_no

    • 计算该会话已运行多少个事务

  • time

    • 事务的经过时间(以微秒计)

  • script_no

    • 识别用于事务的脚本文件(当使用 -f-b 指定多个脚本时非常有用)

  • time_epoch

    • 事务完成时间,以 Unix epoch 时间戳表示

  • time_us

    • 事务完成时间的几分之一秒部分(以微秒计)

  • schedule_lag

    • 事务启动延迟,即事务的预定启动时间与它实际启动的时间之间的差值(以微秒计)(仅当指定了 &#8212;&#8203;rate 时才显示)

  • retries

    • 事务期间序列化或死锁错误后的重试次数(仅当 &#8212;&#8203;max-tries 不等于 1 时才显示)

—​rate—​latency-limit 都使用时,跳过的事务的 time 将报告为 skipped 。如果事务以失败告终,其 time 将报告为 failed 。如果你使用 —​failures-detailed 选项,失败事务的 time 将报告为 serializationdeadlock (取决于失败类型,有关详情请参见 Failures and Serialization/Deadlock Retries )。

下面是一个在单客户端运行中生成的日志文件的片段:

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 选项来仅记录随机选择的交易样本。

Aggregated Logging

使用 —​aggregate-interval 选项,日志文件将使用不同的格式。每行日志描述一个聚合间隔。它包含以下空格分隔的字段:

  • interval_start

    • 间隔的开始时间,以 Unix 纪元时间戳形式

  • num_transactions

    • 间隔内的交易数量

  • sum_latency

    • sum of transaction latencies

  • sum_latency_2

    • 交易延迟的平方和

  • min_latency

    • minimum transaction latency

  • max_latency

    • maximum transaction latency

  • sum_lag

    • 交易开始延迟的总和(除非指定了 &#8212;&#8203;rate 否则为零)

  • sum_lag_2

    • 交易开始时间的平方总和(除非指定了 &#8212;&#8203;rate 否则为零)

  • min_lag

    • 最小交易开始延迟(除非指定了 &#8212;&#8203;rate 否则为零)

  • max_lag

    • 最大交易开始延迟(除非指定了 &#8212;&#8203;rate 否则为零)

  • skipped

    • 因为开始太晚而被跳过的交易数量(除非指定了 &#8212;&#8203;rate&#8212;&#8203;latency-limit 否则为零)

  • retried

    • 重试交易的数量(除非 &#8212;&#8203;max-tries 不等于一,否则为零)

  • retries

    • 序列化或死锁错误后的重试次数(除非 &#8212;&#8203;max-tries 不等于一,否则为零)

  • serialization_failures

    • 发生序列化错误且之后未重试的交易数量(除非指定了 &#8212;&#8203;failures-detailed 否则为零)

  • deadlock_failures

    • 发生死锁错误且之后未重试的交易数量(除非指定了 &#8212;&#8203;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

请注意,虽然普通(未聚合)日志格式显示了每笔交易中使用的脚本,但聚合格式没有显示。因此,如果您需要每个脚本的数据,则需要自行聚合数据。

Per-Statement Report

使用 -r 选项,pgbench 会针对每个语句收集以下统计信息:

仅当 —​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;

如果指定了多个脚本文件,所有统计信息将针对每个脚本文件单独报告。

请注意,收集用于每条语句延迟计算所需的额外时间信息会增加一些开销。这会降低平均执行速度,并降低计算出的 TPS。减速量会根据平台和硬件而有很大差异。对比启用和禁用延迟报告后的平均 TPS 值是衡量时间开销是否重大的好方法。

Failures and Serialization/Deadlock Retries

在执行 pgbench 时,有三种主要的错误类型:

如果出现严重错误,则将中止客户端运行;例如,与数据库服务器的连接丢失,或者未完成最后的事务即到达脚本结尾。此外,如果 SQL 或元命令的执行因序列化或死锁错误以外的原因而失败,客户端将被中止。否则,如果 SQL 命令因序列化或死锁错误而失败,则不会中止客户端。在这些情况下,将回滚当前事务,其中还包括将客户端变量设置回执行此事务前的样子(假设一个事务脚本仅包含一个事务;有关更多信息,请参阅 What Is the "Transaction" Actually Performed in pgbench? )。带有序列化或死锁错误的事务在回滚后将重复,直到成功完成或达到最大尝试次数(由 —​max-tries 选项指定)/ 最大重试时间(由 —​latency-limit 选项指定)/ 基准测试结束(由 —​time 选项指定)。如果最后一次试验运行失败,此事务将被报告为失败,但不会中止客户端,客户端将继续工作。

Note

如果不指定 —​max-tries 选项,则事务在序列化或死锁错误后绝不会重试,因为其默认值为 1。使用无限次数的尝试 ( —​max-tries=0 ) 和 —​latency-limit 选项仅限制最大尝试时间。您还可以使用 —​time 选项在无限次数尝试的情况下限制基准测试持续时间。

重复包含多个事务的脚本时要小心:脚本总是会完全重试,因此可能多次执行成功的事务。

重复带有 shell 命令的事务时要小心。与 SQL 命令的结果不同,shell 命令的结果不会回滚, \setshell 命令的变量值除外。

成功事务的延迟包括带有回滚和重试的整个事务执行时间。仅针对成功事务和命令测量延迟,而不针对失败事务或命令测量。

主要报告包含失败事务数。如果 —​max-tries 选项不等于 1,则主要报告还将包含与重试相关的统计数据:重新尝试的事务总数和重试总数。每脚本报告从主要报告继承所有这些字段。每条语句报告仅在 —​max-tries 选项不等于 1 时显示重试统计。

如果希望按基本类型在事务和汇总日志以及主要和每脚本报告中对故障进行分组,请使用 —​failures-detailed 选项。如果您还希望按类型区分所有错误和故障(不重试的错误),包括已超过的重试限制以及对于序列化/死锁故障超出的程度,请使用 —​verbose-errors 选项。

Table Access Methods

您可以为 pgbench 表指定 Table Access Method 。环境变量 PGOPTIONS 指定通过命令行传递给 PostgreSQL 的数据库配置选项(请参阅 Section 20.1.4 )。例如,可以使用以下方式指定 pgbench 创建的表的假定默认表访问方法,称为 wuzza

PGOPTIONS='-c default_table_access_method=wuzza'

Good Practices

使用 pgbench 生成毫无意义的数字非常容易。以下是一些帮助您获得有用结果的指南。

首先, never 相信仅运行几秒钟的任何测试。使用 -t-T 选项让运行时间至少持续几分钟,以便消除干扰因素。在某些情况下,您可能需要几个小时才能获得可再现的数字。最好尝试运行测试几次,以找出您的数字是否可重现。

对于默认的 TPC-B 类测试场景,初始化比例因子 ( -s ) 应至少与您打算测试的最大客户端数 ( -c ) 一样大;否则,您将主要测量更新争用。 pgbench_branches 表中只有 -s 行,并且每笔事务都希望更新其中一行,因此 -c 超过 -s 的值无疑会导致大量事务被阻止,等待其他事务。

默认测试场景对于表的初始化时间也相当敏感:表中累积的死行和死空间会改变结果。为理解结果,您必须跟踪更新总数以及何时发生真空。如果启用了自动真空,则会导致测量性能发生不可预测的变化。

pgbench 的一个限制是,它本身在尝试测试大量客户端会话时会成为瓶颈。可以通过在与数据库服务器不同的机器上运行 pgbench 来缓解此问题,尽管低网络延迟至关重要。甚至可以在同一数据库服务器上在多个客户端机器上并发运行多个 pgbench 实例。

Security

如果不可信用户可以访问尚未采用 secure schema usage pattern 的数据库,请勿在该数据库中运行 pgbench。pgbench 使用不合格的名称,不操纵搜索路径。