On this page
Caution
pgbench
pgbench —在 PostgreSQL 上运行基准测试
Synopsis
pgbench
-i
[ option
...] [ dbname
]
pgbench
[ option
...] [ dbname
]
Description
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)
前六行报告一些最重要的参数设置。下一行报告已完成和打算进行的 Transaction 数量(后者仅是 Client 数量与每个 ClientTransaction 数量的乘积);除非运行在完成前失败,否则它们将相等。 (在-T
模式下,仅打印实际的事务数.)最后两行报告每秒的事务数,计算有无开始数据库会话的时间。
默认的类似 TPC-B 的事务测试需要预先设置特定的表。应该使用-i
(初始化)选项调用 pgbench 来创建和填充这些表。 (在测试自定义脚本时,不需要此步骤,但是需要执行测试所需的任何设置.)初始化看起来像:
pgbench -i [ other-options ] dbname
其中* dbname
*是已创建的要测试的数据库的名称。(您可能还需要-h
,-p
和/或-U
选项来指定如何连接到数据库服务器.)
Warning
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
(填充因子)选项。
完成必要的设置后,您可以使用不包含-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_accounts
,pgbench_branches
,pgbench_history
和pgbench_tellers
。
- 创建标准 pgbench 场景使用的表,即
g
(生成数据)- 生成数据并将其加载到标准表中,替换现有数据。
v
(真空)- 在标准表上调用
VACUUM
。
- 在标准表上调用
p
(创建主键)- 在标准表上创建主键索引。
f
(创建外键)- 在标准表之间创建外键约束。 (请注意,默认情况下不执行此步骤.)
-F
fillfactor
--fillfactor=
fillfactor
- 使用给定的 fillfactor 创建
pgbench_accounts
,pgbench_tellers
和pgbench_branches
表。默认值为 100.
- 使用给定的 fillfactor 创建
-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-like
,simple-update
和select-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_accounts
,pgbench_branches
,pgbench_history
和pgbench_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.
- 在 pgbench 的输出中报告指定的比例因子。对于内置测试,这是没有必要的。通过计算
-S
--select-only
- 运行内置的全选脚本。
-b select-only
的简写。
- 运行内置的全选脚本。
-t
transactions
--transactions=
transactions
- 每个 Client 端运行的事务数。默认值是 10.
-T
seconds
--time=
seconds
- 运行测试持续这么长时间,而不是每个 Client 端进行固定数量的事务。
-t
和-T
是互斥的。
- 运行测试持续这么长时间,而不是每个 Client 端进行固定数量的事务。
-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
运行。由于每个线程都 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
调用)在随机选择的aid
,tid
,bid
和delta
上为每个事务发出七个命令。该方案的灵感来自 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.自动变量
Variable | Description |
---|---|
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
常数,布尔常数TRUE
和FALSE
,整数常数(例如5432
),双常数(例如3.14159
),对变量:
*variablename
*,operators的引用以及它们通常的 SQL 优先级和关联性function calls,SQL CASE 通用条件表达式和括号。
- 将变量*
函数和大多数运算符在NULL
Importing 上返回NULL
。
出于条件目的,非零数值是TRUE
,零数值和NULL
是FALSE
。
如果没有为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 运算符的优先级提高
Operator | Description | Example | Result |
---|---|---|---|
OR |
logical or | 5 or 0 |
TRUE |
AND |
logical and | 3 and 0 |
FALSE |
NOT |
logical not | not false |
TRUE |
IS [NOT] (NULL|TRUE|FALSE) |
value tests | 1 is null |
FALSE |
ISNULL|NOTNULL |
null tests | 1 notnull |
TRUE |
= |
is equal | 5 = 4 |
FALSE |
<> |
不相等 | 5 <> 4 |
TRUE |
!= |
不相等 | 5 != 5 |
FALSE |
< |
lower than | 5 < 4 |
FALSE |
<= |
低于或等于 | 5 <= 4 |
FALSE |
> |
greater than | 5 > 4 |
TRUE |
>= |
大于或等于 | 5 >= 4 |
TRUE |
| |
整数按位或 | 1 | 2 |
3 |
# |
整数按位异或 | 1 # 3 |
2 |
& |
整数按位与 | 1 & 3 |
1 |
~ |
整数按位非 | ~ 1 |
-2 |
<< |
整数左移 | 1 << 2 |
4 |
>> |
整数按位右移 | 8 >> 2 |
2 |
+ |
addition | 5 + 4 |
9 |
- |
subtraction | 3 - 2.0 |
1.0 |
* |
multiplication | 5 * 4 |
20 |
/ |
除(整数将结果截断) | 5 / 3 |
1 |
% |
modulo | 3 % 2 |
1 |
- |
opposite | - 2.0 |
-2.0 |
Built-In Functions
Table 243中列出的函数内置在 pgbench 中,可以在\set中出现的表达式中使用。
表 243. pgbench 函数
Function | Return Type | Description | Example | Result |
---|---|---|---|---|
abs(a) |
与* a *相同 |
absolute value | abs(-17) |
17 |
debug(a) |
与* a *相同 |
打印* a 到 stderr,然后返回 a * |
debug(5432.1) |
5432.1 |
double(i) |
double | 投双 | double(5432) |
5432.0 |
exp(x) |
double | exponential | exp(1.0) |
2.718281828459045 |
greatest(a [, ... ] ) |
如果有任何* a *为 double,则为 double,否则为整数 |
参数中的最大值 | greatest(5, 4, 3, 2) |
5 |
hash(a [, seed ] ) |
integer | hash_murmur2() 的别名 |
hash(10, 5432) |
-5817877081768721676 |
hash_fnv1a(a [, seed ] ) |
integer | FNV-1a hash | hash_fnv1a(10, 5432) |
-7793829335365542153 |
hash_murmur2(a [, seed ] ) |
integer | MurmurHash2 hash | hash_murmur2(10, 5432) |
-5817877081768721676 |
int(x) |
integer | 转换为 int | int(5.4 + 3.8) |
9 |
least(a [, ... ] ) |
如果有任何* a *为 double,则为 double,否则为整数 |
参数中的最小值 | least(5, 4, 3, 2.1) |
2.1 |
ln(x) |
double | natural logarithm | ln(2.718281828459045) |
1.0 |
mod(i, j) |
integer | modulo | mod(54, 32) |
22 |
pi() |
double | 常数 PI 的值 | pi() |
3.14159265358979323846 |
pow(x, y), power(x, y) |
double | exponentiation | pow(2.0, 10) , power(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(x) |
double | square root | sqrt(2.0) |
1.414213562 |
random
函数使用均匀分布生成值,即所有值均以相等的概率绘制在指定范围内。 random_exponential
,random_gaussian
和random_zipfian
函数需要一个附加的 double 参数,该参数确定分布的精确形状。
- 对于指数分布,*
parameter
通过在parameter
*处截断快速递减的指数分布,然后投影到边界之间的整数上来控制分布。确切地说,
f(x)= exp(-参数*(x-最小)/(max-最小 1))/(1-exp(-参数))
然后以f(i) - f(i + 1)
的概率得出* min
和 max
*之间的值i
1 *。
直观地,* 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
*之间的值i
1 。直观地, 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.0
到5.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
倍,依此类推。
哈希函数hash
,hash_murmur2
和hash_fnv1a
接受 Importing 值和可选的种子参数。如果未提供种子,则使用:default_seed
的值,除非通过命令行-D
选项设置,否则它将随机初始化。哈希函数可用于分散随机函数(例如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;
该脚本允许事务的每次迭代引用不同的随机选择的行。 (此示例还显示了为什么每个 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 使用非限定名称,并且不操纵搜索路径。