pgbench

名称

pgbench -- 在PostgreSQL上运行一个基准测试

大纲

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

pgbench [option...] [dbname]

描述

pgbench是在PostgreSQL上运行基准测试的简单程序。 它一遍一遍的运行SQL命令的相同序列,可能是在多个并行的数据库会话上, 然后计算平均事务率(每秒钟事务数)。默认的,pgbench 测试基于TPC-B的宽松的情节,每个事务包括5个SELECTUPDATEINSERT命令。不过,通过写你自己的事务脚本文件,可以很容易的测试其他情况。

通常来自pgbench的输出看起来像:

transaction type: 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风格的事务测试需要事先设置好特定的表。pgbench 应该使用-i(初始化)选项调用,以创建和填充这些表。 (当你在测试一个自定义的脚本时,不需要这个步骤,但是需要做你的测试需要的步骤。) 初始化看起来像这样:

pgbench -i [ other-options ] dbname

这里的dbname是早已创建的要测试的数据库的名字。 (可能也需要-h-p和/或-U 选项来指定如何连接到数据库服务器。)

小心

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 的命令运行你的benchmark,也就是

pgbench [ options ] dbname

在几乎所有情况下,你都将需要一些选项来做一个有用的测试。 最重要的选项是-c(客户端的数量)、-t(事务的数量)、 -T(时间限制)和-f(声明一个自定义的脚本文件)。 见下文的完整列表。

选项

下面分成了三个章节:不同的选项在数据库初始化和运行benchmark时使用, 有些选项在两种情况下都使用。

初始化选项

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

-i
--initialize

要求调用初始化模式。

-F fillfactor
--fillfactor=fillfactor

用给定的填充因子创建pgbench_accountspgbench_tellerspgbench_branches表。缺省是100。

-n
--no-vacuum

在初始化之后不执行清理。

-q
--quiet

日志切换到安静模式,每5秒钟只产生一条进度消息。缺省的日志输出是每100000行一条消息, 通常每秒输出多行(特别是在好的硬件上)。

-s scale_factor
--scale=scale_factor

乘以比例因子生成的行数。例如,-s 100将在 pgbench_accounts表中创建10,000,000行。缺省是1。 当比例是20,000或更大时,用于保存计数标识符的字段(aid字段) 将切换到使用更大的整数(bigint),以足够保存计数标识符的范围。

--foreign-keys

在标准表之间创建外键约束。

--index-tablespace=index_tablespace

在指定的表空间中创建索引,而不是在缺省的表空间中。

--tablespace=tablespace

在指定的表空间中创建表,而不是在缺省的表空间中。

--unlogged-tables

创建所有的表为unlogged表,而不是永久表。

基准选项

pgbench接受下列的命令行基准参数:

-c clients
--client=clients

模拟客户端的数量,也就是并发数据库会话的数量。缺省是1。

-C
--connect

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

-d
--debug

打印调试输出。

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

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

-f filename
--file=filename

filename中读取事务脚本。见下文获取细节。 -N-S、和-f是互相排斥的。

-j threads
--jobs=threads

pgbench中工作线程的数量。在多CPU的机器上使用多个线程会很有帮助。 客户端的数量必须是线程数量的倍数,因为每个线程都有相同数量的客户端会话管理。 缺省是1。

-l
--log

记录每个事务写入日志文件的时间。见下文获取细节。

-M querymode
--protocol=querymode

提交查询到服务器使用的协议:

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

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

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

缺省是简单的查询协议。(参阅第 49 章获取更多信息。)

-n
--no-vacuum

运行测试时不执行清理。如果你正在运行一个不包含标准表pgbench_accountspgbench_branchespgbench_history、和 pgbench_tellers的自定义测试,那么该选项是必需的

-N
--skip-some-updates

不要更新pgbench_tellerspgbench_branches。 这将避免争用这些表,但是它使得测试用例更不像TPC-B。

-P sec
--progress=sec

sec秒显示一次进程报告。该报告包括自运行开始以来的时间, 自最后一次报告以来的tps,和自最后一次报告以来的事务延迟平均和标准偏差。 在节流模式下(-R),延迟是按照事务预约的启动时间计算的, 而不是事务实际开始的时间,因此它也包括平均进度延后时间。

-r
--report-latencies

在benchmark完成后报告每个命令的平均每语句延迟(从客户的角度看的执行时间)。 见下文获取细节。

-R rate
--rate=rate

以指定的速率执行事务,而不是越快运行越好(缺省)。 速率是以事务每秒给出的。如果目标速率超过了最大可能的速率, 那么速率限制将不会影响结果。

该速率是通过沿着泊松分布的规定时间线启动事务定向的。 预计的启动时间表基于客户端的第一次启动向前移动,而不是基于前一个事务的结束。 该方法意味着当事务经过它们的原计划结束时间时,后面的事务有可能再次追赶上来。

当开启了节流时,在运行结束的事务延迟报告是从计划开始时间计算的, 所以它包括每个事务必须等待前一个事务完成的时间。 该等待时间称为进度滞后时间,并且分别报告它的平均值和最大值。 事务延迟对事务实际开始时间,也就是在数据库中执行事务花费的时间, 可以通过报告的延迟减去进度滞后时间计算得到。

高进度滞后时间表明系统不能以选定数量的客户端和进程、指定的速率处理事务。 当平均事务执行时间要比计划的每个事务之间的间隔长时, 那么每个成功的事务将在后面失败,并且测试运行的时间越长, 进度滞后时间将也越长。当发生这种情况时,你将必须降低指定的事务速率。

-s scale_factor
--scale=scale_factor

pgbench的输出中报告指定的比例因子。在内建的测试中,这不是必需的; 正确的比例因子将通过计数pgbench_branches表中的行数检测到。 不过,在测试自定义benchmark(-f选项)时,比例因子将报告为1,除非使用了该选项。

-S
--select-only

执行只有select的事务,替代类似TPC-B的测试。

-t transactions
--transactions=transactions

每个客户端运行的事务数量。缺省是10。

-T seconds
--time=seconds

运行测试这么多秒,而不是每客户端固定数量的事务。-t-T是互相排斥的。

-v
--vacuum-all

在运行测试之前清理四个标准表。既不用-n也不用-v, pgbench将清理pgbench_tellerspgbench_branches表, 截断pgbench_history表。

--aggregate-interval=seconds

汇总时间间隔的长度(以秒计)。可能只与-l选项一起使用, 日志包含每间隔的总结(事务的数量、最小/最大延迟和可用于方差估计的两个额外字段)。

目前在Windows上不支持这个选项。

--sampling-rate=rate

采样率,在写入数据到日志时使用,以减少生成日志的数量。如果给出了这个选项, 则只记录指定比例的事务。1.0意味着记录所有事务,0.05意味着只记录了5%的事务。

在处理日志文件时记得计算上采样率。例如,计算tps值时,需要乘以相应的数字 (比如,0.01的采样率,将只得到1/100的实际tps)。

公共选项

pgbench接受下列的命令行公共参数:

-h hostname
--host=hostname

数据库服务器的主机名

-p port
--port=port

数据库服务器的端口号

-U login
--username=login

要连接的用户名

-V
--version

打印pgbench的版本并退出。

-?
--help

显示关于pgbench命令行参数的帮助并退出。

注意

什么是在pgbench上实际执行的"事务"

缺省的事务脚本每事务发出7个命令:

  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;

如果你声明了-N,那么不包含步骤4和5。如果你声明了-S, 那么只发出SELECT

自定义脚本

pgbench支持运行自定义的benchmark脚本, 通过使用从文件中读取的事务脚本(-f选项) 替换缺省的事务脚本(上面描述了)。在这种情况下,一个"事务" 被认为是一个脚本文件的一次执行。你可以声明多个脚本(多个-f选项), 在这种情况下,每次客户端会话启动一个新的事务时随机选择一个脚本。

脚本文件的格式是每行一个SQL命令;不支持一行多个SQL命令。 忽略空行和以--开始的行。脚本文件行也可以是"元命令", 被pgbench本身解读,就像下面描述的那样。

脚本文件有一个简单的变量替换设施。变量可以通过命令行-D选项设置, 在上文中解释了,或者通过上文解释的元命令设置。除了预先通过-D 命令行选项设置的任何变量外,有一些变量是自动预设的,在 表 G-1中列出。 使用-D为这些变量指定的值优先于自动预设的值。 一旦设置,变量的值可以通过书写:variablename 插入到SQL命令中。当运行多于一个客户端会话时,每个会话都有它自己的变量设置。

表 G-1. 自动变量

变量描述
scale 当前的比例因子
client_id 唯一的编号识别客户端会话(从零开始)

脚本文件元命令以反斜杠(\)开始。元命令的参数通过空格分隔开。 支持这些元命令:

\set varname operand1 [ operator operand2 ]

设置变量varname为一个经过计算的整数值。 每个operand是一个整数常量或者一个:variablename 到一个有整数值的变量的引用。operator可以是 +, -, *, 或 /

例子:

\set ntellers 10 * :scale

\setrandom varname min max

设置变量varname为范围minmax (包含)之间的随机整数值。每个限制可以是一个整数常量或者一个 :variablename到一个有整数值的变量的引用。

例子:

\setrandom aid 1 :naccounts

\sleep number [ us | ms | s ]

导致脚本执行睡眠指定的持续时间,单位是微妙(us)、毫秒(ms) 或秒(s)。如果省略了单位,则缺省是秒。number 可以是一个整数常量或者一个:variablename 到一个有整数值的变量的引用。

例子:

\sleep 10 ms

\setshell varname command [ argument ... ]

设置变量varname为shell命令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

作为一个示例,内建的类TPC-B的事务的全部定义是:

\set nbranches :scale
\set ntellers 10 * :scale
\set naccounts 100000 * :scale
\setrandom aid 1 :naccounts
\setrandom bid 1 :nbranches
\setrandom tid 1 :ntellers
\setrandom delta -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-intervalpgbench将每个事务花费的时间写入到日志文件中。 日志文件将命名为pgbench_log.nnn, 这里的nnn是pgbench进程的PID。如果-j 选项是2或更高,则创建多个工作线程,每个都将拥有它自己的日志文件。 第一个工作线程的日志文件名字和标准的单个工作线程情况相同。 其他工作线程的日志文件将命名为pgbench_log.nnn.mmm, 这里的mmm是每个工作线程的序列号,起始值为1。

日志格式是:

client_id transaction_no time file_no time_epoch time_us

这里的time是事务运行的总时间,以毫秒计, file_no标识出使用哪个脚本文件(在使用-f 声明了多个脚本时有用),time_epoch/time_us 是一个UNIX纪元格式的时间戳和以毫秒计的时间偏移(适用于创建一个以分数计的ISO 8601的时间戳), 显示事务是何时完成的。最后一个字段,schedule_lag, 是事务的预定启动时间和实际启动时间之间的不同,以毫秒计。 它只在使用了--rate时出现。

这里是示例输出:

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

在可以处理大量事务的硬件上运行一个长文本时,日志文件会变得非常大。 --sampling-rate选项可以用来只记录一个随机的事务样本。

聚合的日志

带有--aggregate-interval选项,日志会使用一个稍微不同的格式:

interval_start num_of_transactions latency_sum latency_2_sum min_latency max_latency

这里的interval_start是间隔的开始(UNIX纪元格式的时间戳), num_of_transactions是间隔内事务的数量,latency_sum 是延迟的总和(这样你可以非常容易的计算平均延迟)。下列的两个字段对于方差估计是有帮助的- latency_sum是延迟的总和,latency_2_sum是延迟的二次方的总和。 最后两个字段是min_latency-间隔的最小延迟,max_latency- 间隔的最大延迟。当事务已经提交了时,计入间隔。最后四个字段, lag_sumlag_2_summin_lagmax_lag,只在使用了--rate时出现。它们从每个事务必须等待前一个事务完成的时间计算得来, 也就是,每个事务的预定启动时间和实际启动时间之间的不同。

这里是一个示例输出:

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收集每个客户端执行的每个语句的事务时间。 然后报告一个平均值,在benchmark完成之后,当做每个语句延迟的参考。

对于缺省的脚本,输出看起来像这样:

starting vacuum...end.
transaction type: 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
tps = 618.764555 (including connections establishing)
tps = 622.977698 (excluding connections establishing)
statement latencies in milliseconds:
        0.004386        \set nbranches 1 * :scale
        0.001343        \set ntellers 10 * :scale
        0.001212        \set naccounts 100000 * :scale
        0.001310        \setrandom aid 1 :naccounts
        0.001073        \setrandom bid 1 :nbranches
        0.001005        \setrandom tid 1 :ntellers
        0.001078        \setrandom delta -5000 5000
        0.326152        BEGIN;
        0.603376        UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;
        0.454643        SELECT abalance FROM pgbench_accounts WHERE aid = :aid;
        5.528491        UPDATE pgbench_tellers SET tbalance = tbalance + :delta WHERE tid = :tid;
        7.335435        UPDATE pgbench_branches SET bbalance = bbalance + :delta WHERE bid = :bid;
        0.371851        INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP);
        1.212976        END;

如果声明了多个脚本文件,那么为每个脚本文件单独报告平均值。

请注意,收集额外的时间信息需要每语句延迟计算加上一些开销。 这将减缓平均执行速度并降低计算的TPS。降低的值根据平台和硬件会有很大的变化。 启用和不启用延迟报告来计算TPS的平均值,是测量时间开销是否重要的一个很好的方法。

好的实践

使用pgbench可以非常简单的生成完全毫无意义的数字。 这里有一些指导,帮助你获取有用的结果。

首先,决不相信任何只运行几秒钟的测试。使用-t-T选项使测试最少运行几分钟,以平均噪音。 在某些情况下,你可能需要几小时才能获得可再生的数字。 尝试让测试多运行几次是一个好的想法,以找出你的数字是否再生了。

对于缺省的类TPC-B测试情况,初始化比例因子(-s) 应该至少和你要测试的最大客户端数量(-c)一样大; 否则你大多会测量到更新争用。在pgbench_branches表中只有-s行, 并且每个事务都想更新它们中的一条,所以-c值大于-s时, 将会毫无疑问的导致大量事务锁住,等待其他事务。

缺省的测试情节也对表被初始化了多久很敏感:表中死行的积累和死区会改变结果。 要理解结果,你必须跟踪更新的总数量和何时发生了清理。如果启用了自动清理, 那么它会导致在测量的性能中有不可预知的改变。

pgbench的一个限制是在尝试测试大量的客户端会话时,它本身会成为瓶颈。 这个可以通过在不同的机器上从数据库服务器运行pgbench来减轻, 尽管少量的网络延迟是必然的。在几个客户端机器上,对同一个数据库服务器, 同时运行几个pgbench实例可能也有帮助,