↑↓ 选择↵ 打开⌫ 切换范围完整搜索

PG.CENTER 连接 PostgreSQL 文档、百科与生态知识。由 Pigsty 维护。

支持中的版本: 当前版本 (18) / 17 / 16 / 15 / 14
开发中的版本: 19 / 20devel
已结束支持的版本: 13 / 12 / 11 / 10 / 9.6 / 9.5 / 9.4 / 9.3 / 9.2 / 9.1 / 9.0 / 8.4 / 8.3
历史版本。 PostgreSQL 13 已结束支持。 2025-11-13. 请参阅 当前版本手册.

pgbench

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

前六行报告了一些最重要的参数设置。下一行报告已完成的事务数和预期的事务数(后者就是客户端数与每个客户端的事务数的乘积);除非运行在完成前失败,否则这两个数应该相等。(在 -T 模式下,只打印实际的事务数。)最后两行报告每秒事务数,分别计入和不计入启动数据库会话的时间。

默认的类 TPC-B 事务测试要求预先建立特定的表。应使用 -i(初始化)选项调用 pgbench 来创建并填充这些表。(测试自定义脚本时不需要这一步,但需要自行完成测试所需的准备工作。)初始化命令如下:

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(fillfactor)选项。

完成必要的准备后,就可以使用不带 -i 的命令运行基准测试,也就是:

pgbench [ options ] dbname

几乎在所有情况下,都需要附加一些选项才能得到有意义的测试。最重要的选项是 -c(客户端数)、-t(事务数)、-T(时间限制)以及 -f(指定自定义脚本文件)。完整列表见下文。

选项

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

初始化选项

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

dbname

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

-i
--initialize

进入初始化模式所必需。

-I init_steps
--init-steps=init_steps

仅执行选定的常规初始化步骤。init_steps 指定要执行的初始化步骤,每个步骤用一个字符表示。各步骤按照指定顺序调用。默认值为 dtgvp。可用步骤如下:

d(删除)

删除任何现有的 pgbench 表。

t(创建表)

创建标准 pgbench 场景使用的表,即 pgbench_accounts、pgbench_branches、pgbench_history 和 pgbench_tellers。

g 或 G(在客户端或服务器端生成数据)

生成数据并将其装载到标准表中,替换其中任何已有数据。

使用 g(客户端生成数据)时,数据由 pgbench 客户端生成,再通过 COPY 发送到服务器,因此会大量占用客户端/服务器带宽。使用 g 时,在为 pgbench_accounts 表生成数据的过程中,每生成 100,000 行会输出一条日志消息。

使用 G(服务器端生成数据)时,pgbench 客户端只发送较小的查询,随后实际数据在服务器端生成。这种方式不需要大量带宽,但服务器会承担更多工作。使用 G 时,生成数据期间不会打印任何进度消息。

默认初始化行为使用客户端生成数据(等同于 g)。

v(清理)

在标准表上调用 VACUUM。

p(创建主键)

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

f(创建外键)

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

-F fillfactor
--fillfactor=fillfactor

以给定的 fillfactor 创建 pgbench_accounts、pgbench_tellers 和 pgbench_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 表。预期值为 range 或 hash。此选项要求 --partitions 设置为非零值。若未指定,默认为 range。

--partitions=NUM

创建带有 NUM 个大小近乎相等分区的 pgbench_accounts 分区表,这些分区用于容纳按比例扩展后的账户数。默认值为 0,表示不分区。

--tablespace=tablespace

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

--unlogged-tables

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

基准测试选项

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

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

将指定的内置脚本添加到待执行脚本列表中。可用的内置脚本包括:tpcb-like、simple-update 和 select-only。也接受内置名称的无歧义前缀。使用特殊名称 list 时,会显示内置脚本列表并立即退出。

可选地,可在@后写一个整数权重,以调整此脚本相对于其他脚本的选中概率。默认权重为 1。详情请参见下文。

-c clients
--client=clients

模拟的客户端数量,也就是并发数据库会话的数量。默认值为 1。

-C
--connect

为每个事务建立一个新连接,而不是仅在每个客户端会话中执行一次。这对于测量连接开销很有用。

-d
--debug

打印调试输出。

-D varname=value
--define=varname=value

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

-f filename[@weight]
--file=filename[@weight]

将从 filename 读取的事务脚本添加到要执行的脚本列表中。

可选地,可在@后写一个整数权重,以调整此脚本相对于其他脚本的选中概率。默认权重为 1。(如果脚本文件名本身包含@字符,可追加一个权重以消除歧义,例如 filen@me@1。)详情见下文。

-j threads
--jobs=threads

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

-l
--log

将每个事务的信息写入日志文件。详情见下文。

-L limit
--latency-limit=limit

持续时间超过 limit 毫秒的事务会被单独计数和报告,称为 late。

使用限流(--rate=...)时,若某个事务落后于计划时间超过 limit 毫秒,从而已经不可能满足延迟限制,则它根本不会被发送到服务器。此类事务会被单独计数并报告为 skipped。

-M querymode
--protocol=querymode

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

  • simple:使用简单查询协议。

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

  • prepared:使用带有预备语句的扩展查询协议。

在 prepared 模式下,pgbench 从第二次查询迭代开始复用解析分析结果,因此 pgbench 比其他模式运行得更快。

默认值是简单查询协议。(更多信息见第 52 章。)

-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)时,延迟是相对于事务计划开始时间计算的,而不是实际开始时间,因此其中也包含平均计划滞后时间。

-r
--report-latencies

在基准测试完成后,报告每条命令的平均语句延迟(从客户端视角看到的执行时间)。详情见下文。

-R rate
--rate=rate

以指定速率执行事务,而不是像默认行为那样尽可能快地运行。速率以每秒事务数表示。如果目标速率高于可达到的最大速率,则速率限制不会影响结果。

该速率通过让事务沿着一条符合泊松分布的时间线启动来实现。预期开始时间表是根据客户端首次启动的时间向前推进的,而不是根据前一个事务结束的时间。这意味着,当某些事务超过其原定结束时间时,后续事务仍有可能重新赶上计划。

启用限流后,运行结束时报告的事务延迟是从计划开始时间计算的,因此它包含每个事务等待前一个事务完成的时间。这段等待时间称为计划滞后时间,其平均值和最大值也会单独报告。若要得到相对于事务实际开始时间的延迟,也就是事务在数据库中实际执行所花费的时间,可以用报告中的延迟减去计划滞后时间。

如果同时使用 --latency-limit 和 --rate,一个事务可能会落后太多,以至于在前一个事务结束时已经超过了延迟限制,因为延迟是从计划开始时间计算的。这样的事务不会发送到服务器,而是完全跳过并单独计数。

较高的计划滞后时间表明,在所选客户端数和线程数下,系统无法以指定速率处理事务。当平均事务执行时间长于事务之间的计划间隔时,后续事务会不断进一步落后,而计划滞后时间也会随着测试持续时间增加。在这种情况下,需要降低指定的事务速率。

-s scale_factor
--scale=scale_factor

在 pgbench 输出中报告指定的比例因子。对于内置测试,这没有必要;系统会通过统计 pgbench_branches 表中的行数来检测正确的比例因子。但在只测试自定义基准(-f 选项)时,除非使用此选项,否则比例因子会被报告为 1。

-S
--select-only

运行内置的 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 选项一起使用。使用此选项时,日志包含每个间隔的摘要数据,如下所述。

--log-prefix=prefix

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

--progress-timestamp

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

--random-seed=seed

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

显式设置种子可以在随机数层面精确复现一次 pgbench 运行。由于随机状态按线程管理,这意味着对于相同的调用,如果每个线程只有一个客户端,且不存在外部依赖或数据依赖,那么 pgbench 运行可以完全一致。从统计角度看,精确复现实验并不是好主意,因为它可能掩盖性能波动,甚至不恰当地提高性能,例如反复命中与前一次运行相同的页面。不过,这对调试也可能非常有帮助,例如重现会触发错误的棘手案例。请谨慎使用。

--sampling-rate=rate

写入日志时使用的采样率,用于减少生成的日志量。如果给定此选项,则只记录指定比例的事务。1.0 表示记录全部事务,0.05 表示只记录 5% 的事务。

处理日志文件时,记得把采样率考虑进去。例如,计算 TPS 值时,需要按采样率对数字进行相应换算(例如采样率为 0.01 时,只能得到实际 TPS 的 1/100)。

--show-script=scriptname

将内置脚本 scriptname 的实际代码输出到 stderr,然后立即退出。

公共选项

pgbench 还接受以下用于连接参数的通用命令行参数:

-h hostname
--host=hostname

数据库服务器的主机名。

-p port
--port=port

数据库服务器的端口号。

-U login
--username=login

连接时使用的用户名。

-V
--version

打印 pgbench 版本并退出。

-?
--help

显示 pgbench 命令行参数的帮助信息并退出。

退出状态

成功运行会以状态 0 退出。退出状态 1 表示静态问题,例如无效的命令行选项。运行过程中出现的错误,例如数据库错误或脚本中的问题,会导致退出状态 2。在后一种情况下,pgbench 会打印部分结果。

环境

PGDATABASE
PGHOST
PGPORT
PGUSER

默认连接参数。

与大多数其他 PostgreSQL 工具一样,此工具也使用 libpq 支持的环境变量(见第 33.14 节)。

环境变量 PG_COLOR 指定是否在诊断消息中使用颜色。可能的值为 always、auto 和 never。

注解

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

pgbench 会从指定列表中随机选取测试脚本来执行。这些脚本既可以是用 -b 指定的内置脚本,也可以是用 -f 指定的用户脚本。每个脚本都可以在其后加上一个以@引出的相对权重,以改变其被选中的概率。默认权重为 1。权重为 0 的脚本会被忽略。

默认的内置事务脚本(也可通过 -b tpcb-like 调用)会针对随机选取的 aid、tid、bid 和 delta 在每个事务中发出七条命令。该场景受 TPC-B 基准启发,但并不是真正的 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 步。这会避免在这些表上发生更新争用,但也会让该测试场景更不像 TPC-B。

如果选择 select-only 内置脚本(也就是 -S),则只执行 SELECT。

自定义脚本

pgbench 支持自定义基准测试场景:只需用从文件中读取的事务脚本(-f 选项)替换默认事务脚本(见上文)即可。在这种情况下,一个“事务”就表示脚本文件的一次执行。

脚本文件包含一个或多个以分号结束的 SQL 命令。空行以及以--开头的行会被忽略。脚本文件还可以包含“元命令”,它们由 pgbench 自身解释,详见下文。

注意

在 PostgreSQL 9.6 之前,脚本文件中的 SQL 命令以换行结束,因此不能跨行。现在连续 SQL 命令之间必须用分号分隔(如果 SQL 命令后面跟着一个元命令,则不需要分号)。如果需要创建一个既能在旧版也能在新版 pgbench 下工作的脚本文件,务必将每个 SQL 命令写在单独一行,并以分号结束。

脚本文件提供了一种简单的变量替换机制。变量名必须由字母(包括非拉丁字母)、数字和下划线组成,并且首字符不能是数字。如上所述,变量可以用命令行 -D 选项设置,也可以用下文介绍的元命令设置。除了通过 -D 命令行选项预先设置的变量之外,还有少量变量会被自动预设,列在表 273 中。如果使用 -D 为这些变量指定值,则会覆盖自动预设值。一旦设置好变量,就可以在 SQL 命令中写:variablename 来插入其值。当运行多个客户端会话时,每个会话都有自己的变量集合。pgbench 在单条语句中最多支持 255 次变量使用。

表 273. pgbench 自动变量

变量简介
client_id 标识客户端会话的唯一编号(从零开始)
default_seed 默认在 hash 函数中使用的种子
random_seed 随机数生成器种子(除非被 -D 覆盖)
scale 当前比例因子

脚本文件中的元命令以反斜线(\)开头,通常延伸到行尾,不过也可以通过写反斜线换行继续到后续行。元命令的参数以空白分隔。支持的元命令如下:

\gset [prefix] \aset [prefix] #

这些命令可用于结束 SQL 查询,以代替结尾分号(;)。

使用\gset 命令时,前面的 SQL 查询预期返回一行,其各列会被存入以列名命名的变量;如果提供了 prefix,则会加上该前缀。

使用\aset 命令时,所有组合 SQL 查询(由\;分隔)的列都会被存入以列名命名的变量中;如果提供了 prefix,则会加上该前缀。如果查询不返回任何行,则不会进行赋值,可以通过测试变量是否存在来检测这种情况。如果查询返回多行,则保留最后一个值。

以下示例将第一个查询的最终账户余额存入变量 abalance,并将变量 p_two 和 p_three 中填入第三个查询中的整数。第二个查询的结果会被丢弃。最后两个组合查询的结果会被存入变量 four 和 five。

UPDATE pgbench_accounts
  SET abalance = abalance + :delta
  WHERE aid = :aid
  RETURNING abalance \gset
-- 两个查询的组合
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 常量、布尔常量 TRUE 和 FALSE、5432 这样的整数常量、3.14159 这样的双精度常量、对变量的引用:variablename、操作符(保留其通常的 SQL 优先级和结合性)、函数调用、SQL CASE 通用条件表达式以及括号。

函数和大部分操作符在 NULL 输入上会返回 NULL。

就条件判断而言,非零数值为 TRUE,零值和 NULL 为 FALSE。

过大或过小的整数和双精度常量,以及整数算术操作符(+、-、* 和/)都会在溢出时引发错误。

在没有为 CASE 提供最终的 ELSE 子句时,默认值是 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 ... ]

将变量 varname 设置为 shell 命令 command 在给定 argument 参数下的结果。该命令必须通过标准输出返回一个整数值。

command 和每个 argument 都可以是文本常量,也可以是引用某个变量的:variablename。如果要使用以冒号开头的 argument,请在 argument 开头再写一个冒号。

示例:

\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

内置操作符

表 274 中列出的算术、按位、比较和逻辑操作符都内置于 pgbench 中,可用于\set 中的表达式。这些操作符按优先级从低到高列出。除非另有说明,接受两个数字输入的操作符只要任一输入为双精度,就会产生双精度结果;否则产生整数结果。

表 274. pgbench 操作符

操作符

描述

示例

boolean OR boolean → boolean

逻辑或

5 or 0 → TRUE

boolean AND boolean → boolean

逻辑与

3 and 0 → FALSE

NOT boolean → boolean

逻辑非

not false → TRUE

boolean IS [NOT] (NULL|TRUE|FALSE) → boolean

布尔值测试

1 is null → FALSE

value ISNULL|NOTNULL → boolean

空值测试

1 notnull → TRUE

number = number → boolean

等于

5 = 4 → FALSE

number <> number → boolean

不等于

5 <> 4 → TRUE

number != number → boolean

不等于

5 != 5 → FALSE

number < number → boolean

小于

5 < 4 → FALSE

number <= number → boolean

小于等于

5 <= 4 → FALSE

number > number → boolean

大于

5 > 4 → TRUE

number >= number → boolean

大于等于

5 >= 4 → TRUE

integer | integer → integer

按位或

1 | 2 → 3

integer # integer → integer

按位异或

1 # 3 → 2

integer & integer → integer

按位与

1 & 3 → 1

~ integer → integer

按位非

~ 1 → -2

integer << integer → integer

按位左移

1 << 2 → 4

integer >> integer → integer

按位右移

8 >> 2 → 2

number + number → number

加法

5 + 4 → 9

number - number → number

减法

3 - 2.0 → 1.0

number * number → number

乘法

5 * 4 → 20

number / number → number

除法(如果两个输入都是整数,则将结果向零截断)

5 / 3 → 1

integer % integer → integer

模(余数)

3 % 2 → 1

- number → number

取相反数

- 2.0 → -2.0


内置函数

表 275 中列出的函数都内置于 pgbench,可用于\set 中的表达式。

表 275. pgbench 函数

函数

描述

示例

abs ( number ) → 与输入类型相同

绝对值

abs(-17) → 17

debug ( number ) → 与输入类型相同

将参数打印到 stderr,并返回参数。

debug(5432.1) → 5432.1

double ( number ) → double

转换为 double。

double(5432) → 5432.0

exp ( number ) → double

指数函数(e 的给定次幂)

exp(1.0) → 2.718281828459045

greatest ( number [, ... ] ) → 若任一参数为 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 hash。

hash_fnv1a(10, 5432) → -7793829335365542153

hash_murmur2 ( value [, seed ] ) → integer

计算 MurmurHash2 hash。

hash_murmur2(10, 5432) → -5817877081768721676

int ( number ) → integer

转换为 integer。

int(5.4 + 3.8) → 9

least ( number [, ... ] ) → 若任一参数为 double,则为 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

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] 中的 Zipfian 分布随机整数,详见下文。

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

    则 min 和 max 之间(含边界)的值 i 会以 f(i) - f(i + 1) 的概率被抽中。

    直观地说,parameter 越大,越靠近 min 的值越容易被抽到,而越靠近 max 的值越不容易被抽到。parameter 越接近 0,分布就越平坦(也就越均匀)。对这种分布的一个粗略近似是:范围内出现频率最高的 1% 的值,即最靠近 min 的那些值,大约会占到 parameter% 的抽样次数。parameter 必须严格大于 0。

  • 对于高斯分布,该区间会映射到一个标准正态分布(经典钟形高斯曲线),并在左侧 -parameter 和右侧+parameter 处截断。区间中部的值更容易被抽到。准确地说,如果 PHI(x) 是标准正态分布的累积分布函数,均值 mu 定义为(max + min) / 2.0,则有


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

    则 min 和 max(包含边界)之间的值 i 被抽中的概率为:f(i + 0.5) - f(i - 0.5)。直观地说,parameter 越大,越靠近区间中间的值被抽到的频率越高,而越靠近 min 和 max 边界的值被抽到的频率越低。大约 67% 的值会落在区间中部 1.0 / parameter 这一段内,也就是均值两侧各占区间长度 0.5 / parameter 的范围内;约 95% 的值会落在区间中部 2.0 / parameter 这一段内,也就是均值两侧各占区间长度 1.0 / parameter 的范围内。例如,如果 parameter 为 4.0,则 67% 的值会落在区间中间四分之一(1.0 / 4.0)内,也就是从 3.0 / 8.0 到 5.0 / 8.0;95% 的值会落在区间中间一半(2.0 / 4.0)内,也就是第二和第三四分位。允许的最小 parameter 值为 2.0。

  • random_zipfian 会生成一个有界的 Zipfian 分布。parameter 定义该分布的倾斜程度。parameter 越大,越靠近区间起始处的值被抽到的频率就越高。这种分布满足:假设范围从 1 开始,抽到 k 与抽到 k+1 的概率之比为 ((k+1)/k)**parameter。例如,random_zipfian(1, ..., 2.5) 生成值 1 的频率大约是生成 2 的(2/1)**2.5 = 5.66 倍,而生成 2 的频率又大约是生成 3 的(3/2)**2.5 = 2.76 倍,依此类推。

    pgbench 的实现基于《Non-Uniform Random Variate Generation》,Luc Devroye,Springer 1986,第 550-551 页。受该算法限制,parameter 的取值范围被限制在 [1.001, 1000]。

Hash 函数 hash、hash_murmur2 和 hash_fnv1a 接受一个输入值和一个可选的种子参数。如果未提供种子,则使用 :default_seed 的值,该值随机初始化,除非通过命令行 -D 选项设置。Hash 函数可以用来打散随机函数的分布,例如 random_zipfian 或 random_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;

该脚本允许事务的每次迭代都引用不同的随机选中行。(这个示例也说明了为什么每个客户端会话都必须拥有自己的变量 — 否则它们就无法彼此独立地访问不同的行。)

逐事务日志记录

使用 -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 表示运行该事务的客户端会话,transaction_no 记录该会话已运行的事务数,time 是事务经过的总时间,单位为微秒,script_no 标识使用的脚本文件(在通过 -f 或 -b 指定多个脚本时很有用),而 time_epoch/time_us 分别是 Unix 纪元时间戳和以微秒计的偏移量(适合用来生成带小数秒的 ISO 8601 时间戳),表示事务完成的时间。schedule_lag 字段是事务计划开始时间与实际开始时间之间的差值,单位为微秒。它仅在使用 --rate 选项时出现。如果同时使用 --rate 和 --latency-limit,则被跳过事务的 time 将报告为 skipped。

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

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 ms)超过了 5 ms 限制。接下来的两个事务被跳过,因为它们在开始之前就已经迟到了。

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

聚合日志记录

使用 --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 选项时出现。它们提供各事务等待前一事务完成的时间统计,即各事务计划开始时间与实际开始时间之间的差值。最后一个字段 skipped 仅在还使用 --latency-limit 选项时出现。它记录因开始时间过晚而被跳过的事务数。每个事务都计入其提交时所在的时间区间。

下面是一些输出示例:

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

请注意,普通(未聚合)日志格式会显示每个事务所使用的脚本,而聚合格式不会。因此,如果需要按脚本区分的数据,就必须自行聚合。

逐语句延迟

使用 -r 选项时,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
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;

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

注意,为逐语句延迟计算收集额外的计时信息会带来一定开销。这会拖慢平均执行速度,并降低计算出的 TPS。减速幅度在很大程度上取决于平台和硬件。比较启用和未启用延迟报告时的平均 TPS 值,是判断这一计时开销是否显著的好方法。

良好实践

很容易用 pgbench 得出完全没有意义的数字。下面给出一些有助于获得有用结果的准则。

首先,绝不要相信任何只运行了几秒钟的测试。使用 -t 或 -T 选项让测试至少持续几分钟,以便平滑掉噪声。在某些情况下,可能需要数小时才能得到可复现的结果。一个好做法是把同一测试运行几次,看看结果是否可复现。

对于默认的类 TPC-B 测试场景,初始化比例因子(-s)应至少与计划测试的最大客户端数(-c)一样大;否则,测到的主要将是更新争用。pgbench_branches 表中只有 -s 行,而每个事务都要更新其中一行,因此 -c 超过 -s 时,必然会有大量事务阻塞等待其他事务。

默认测试场景还会对表初始化后的时间长短非常敏感:表中死元组和无效空间的累积会改变结果。要理解这些结果,必须跟踪更新总数以及何时发生清理。如果启用了自动清理,它可能会给测得的性能带来不可预测的变化。

pgbench 的一个局限是:在尝试测试大量客户端会话时,它自己也可能成为瓶颈。可以通过在与数据库服务器不同的机器上运行 pgbench 来缓解这一点,不过网络延迟必须足够低。甚至可以在多台客户端机器上同时运行多个 pgbench 实例,对同一台数据库服务器施压。

安全性

如果不受信任的用户能够访问尚未采用模式的安全使用方式的数据库,就不要在该数据库中运行 pgbench。pgbench 使用非限定名称,并且不会更改搜索路径。

报告文档问题

阅读 上游文档. 反馈更正前请先核对 当前版本手册.