PostgreSQL每日一贴-测试工具之pgbench

  名称

  pgbench -- 在PostgreSQL中执行基准线测试

  大纲

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

  pgbench [option...] [dbname]

  描述

  pgbench是一个用于在PostgreSQL数据库中运行基准测试的简单程序。pgbench在多个并发的数据库会话中反复运行一系列相同的SQL命令,并计算事务执行的平均速率(每秒执行的事务个数)。 pgbench默认测试的是一种基于TPC-B的松散的测试,即一个事务中包括5个SELECT,UPDATE和INSERT语句。同时允许基于开发者自己书写的事务脚本文件进行其他场景的测试。

  典型的pgbench输出如下:

  transactiontype: 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-like事务测试需要预先设置特定的表。pgbench调用-i(初始化)选项来创建和填充这些表。在使用一个定制化脚本测试时,不需要调用-i选项初始化,但是需要对这个定制化脚本需要的表进行初始化。初始化过程类似如下:

  pgbench -i [ other-options ] dbname

  dbname用于指定一个可以用来测试的数据库的数据库名称。你可能需要指定-h,-p,-U等参数来连接数据库服务器。

  注意

  pgbench -i命令会创建四个表(pgbench_accounts, pgbench_branches, pgbench_history, 和pgbench_tellers),创建这几个表会执行对应表的删除动作。执行前应确认对应数据库中是否已经有这些表,是否会误删除。

  按照默认的比例因子1,这些表按如下行数初始化:

  表名 行数

  ---------------------------------

  pgbench_branches 1

  pgbench_tellers 10

  pgbench_accounts 100000

  pgbench_history 0

  可以通过指定-s(比例因子)参数来增加初始化时各表的行数。-F(填充因子)参数此时也可以同时使用。

  做了必要的设置后,就可以使用如下命令进行基准测试(不需要再使用-i参数)

  pgbench [ options ] dbname

  在几乎所有情况下,您将需要一些选项做出有用的测试。最重要的选项是-c(客户端数量),-t(事务数量),-T(时间限制)和-f(定制脚本)

  具体参数见下文的完整列表。

  选项

  下面的选项分成三个不同类别,分别是使用在数据库初始化阶段,基准测试运行阶段或两个阶段都可以使用。

  初始化选项

  pgbench接受以下命令行参数:

  -i

  --initialize

  调用初始化模式.

  -F fillfactor

  --fillfactor=fillfactor

  使用给出的填充因子创建pgbench_accounts,pgbench_tellers,pgbench_branches等表.默认值是100.

  -n

  --no-vacuum

  初始化后不执行vacuum。

  -q

  --quiet

  日志模式切换为静默方式。即每五秒只打印一条进度信息,默认是每10万条记录打印一条日志,这样通常会有很多日志产生。

  -s scale_factor

  --scale=scale_factor

  生成的行数乘以比例因子。比如,-s 100会在pgbench_accounts表中将会生成1000万条记录。默认值是1。当指定的参数超过20000时,为了能够容纳账户标识符的范围,保存这些信息的列字段类型会调整为大整形。

  --foreign-keys

  在默认创建的表上创建外键。

  --index-tablespace=index_tablespace

  在指定的表空间上创建索引。

  --tablespace=tablespace

  在指定的表空间上创建默认表。

  --unlogged-tables

  Create all tables as unlogged tables, rather thanpermanent tables.(创建临时表?)

  基准测试选项

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

  -c clients

  --client=clients

  模拟的客户数量,具体指并发运行的数据库会话数。默认值为1。

  -C

  --connect

  每个事务都新创建一个连接来执行,而不是仅使用一个客户端连接。这个对于测试连接过载是有意义的。

  -d

  --debug

  打印调试信息

  -D varname=value

  --define=varname=value

  定义一个客户定制脚本适用的变量。允许多个-D选项。

  -f filename

  --file=filename

  从指定文件中读取事务脚本。具体参见下面的详细介绍。-N, -S和-f是互斥的。

  -j threads

  --jobs=threads

  指定工作线程数量。在多核场景下使用多于一个线程是有意义的。由于每个线程会被分配相同数量的客户端数目,所以客户端数量必须设置为线程数目的整数倍。该参数默认值为1。

  -l

  --log

  记录每个线程的耗时到日志文件中。详细内容见下文。

  -M querymode

  --protocol=querymode

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

  simple: 使用简单协议.extended: 使用扩展协议.prepared: 使用扩展协议with prepared statements.

  默认值为简单协议. (详细说明参考Chapter 49 。)

  -n

  --no-vacuum

  测试运行前不执行vacuum。如果运行的是一个不包含默认表( pgbench_accounts, pgbench_branches, pgbench_history和pgbench_tellers.

  )的定制测试时,这个选项是必要的。

  -N

  --skip-some-updates

  不更新表pgbench_tellers和pgbench_branches.设置后会避免更新这两个表。但是这样返会使该测试与TPC-B测试产生差异。

  -P sec

  --progress=sec

  每秒显示一次进度报告。报告包括已运行时间,上次报告到当前的tps、事务延迟时间、标准误差。节流(-R)模式下,误差是从事务计划开始时间计算,而不是实际的事务开始时间,因此,这个时间还包含了平均执行计划延迟时间。

  -r

  --report-latencies

  基准测试运行完成后报告每个命令的平均延迟时间(从客户角度看到的执行时间)。详细内容见下文。

  -R rate

  --rate=rate

  按执行速率执行事务,而不是尽可能快地运行(默认方式)。这个速率指定的是每秒的事务数。如果指定的速率比最大可能速率还大,则这个速率参数不会对结果产生影响。

  速率以沿着Poisson-distributed计划时间线开始的事务为目标。期待的开始时间基于客户第一次启动时间向前,而不是最后一个事务的完成时间。这种方法意味着当事务经过了原先的计划结束时间,后续的事务仍然有可能赶上

  The rate is targeted by starting transactions along aPoisson-distributed schedule time line. The expected start time schedule movesforward based on when the client first started, not when the previoustransaction ended. That approach means that when transactions go past theiroriginal scheduled end time, it is possible for later ones to catch up again.

  当节流开关是有效的,则运行结束时报告的事务时延是从计划开始时间计算的,因此这个时间是包含了每个事务等待前一个事务完成的这段时间的。等待时间又称为计划延迟时间,并且它的平均时间和最大时间也会被分别输出。因此基于实际事务开始时间的事务时延,也就是,在数据库中执行事务实际花费的时间,可以用减去时延报告中的计划延迟时间来计算。

  一个高的计划延迟时间表明系统无法以指定的速率、客户端数量和线程数来处理事务。当平均事务执行时间比事务执行间隔时间要长时,每个连续的事务将会下跌,测试的时间越长,计划延迟时间将会持续越长。当这些事情发生时,你需要降低定制的事务速率。

  -s scale_factor

  --scale=scale_factor

  在pgbench报告中输出定制的比例因子。内置测试不需要设置这个参数。通过计算在pgbench_branches表中的行数,正确的比例因子将会被检测到。但是,如果通过-f运行客户定制的基准测试,比例因子会被报告为1,除非使用了这个参数。

  -S

  --select-only

  运行select-only事务测试而不是TPC-B-like测试。

  -t transactions

  --transactions=transactions

  每个客户端运行的事务数量,默认值为10.

  -T seconds

  --time=seconds

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

  -v

  --vacuum-all

  运行测试前先对四张基准表做vacuum。如果不指定-n也不执行-v,默认对表pgbench_tellers和pgbench_branches做vacuum并将pgbench_history表truncate掉。

  --aggregate-interval=seconds

  汇总时间间隔长度。只有-l和这个参数同时使用才有效。日志包含每个间隔的概要(事务数量,最大最小时延和两个额外的用于方差估计的字段)

  --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中运行的所谓的“事务”是什么?

  默认的事务脚本在每个事务中执行如下七条语句:

  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;

  如果你指定了-N,第4步和第5步不会执行。如果指定了-S,则只有select语句会执行。

  客户化定制脚本

  通过使用-f参数读取文件替换掉默认的事务脚本,pgbench可以实现客户化基准测试场景。在这种情况下,一个“事务”指的是一个SQL脚本文件。你甚至可以指定多个SQL脚本(多个-f参数),在这种情况下,每次客户端会话会随机选择一个脚本开始一个新事务。

  脚本文件格式是每一条SQL命令独占一行。一行多个SQL命令是不支持的。空行和以--开头的行会被忽略。脚本中还可以录入由pgbench解释的"元命令",具体描述见下文:

  pgbench有一个简单的脚本文件的变量替换功能。变量可以使用上面提到的-D参数设置,也可以使用pgbench元命令设置(下面会详细介绍)。除了通过-D参数设置的变量,还有一些变量是默认设置的,列举在了表G-1中。-D设置的变量优先于预设变量。一旦设置后,一个变量的值就可以通过:变量名称的方式插入到一个SQL命令中。当超过一个会话运行时,每个会话都可以有自己的一组变量。

  表G-1.自动变量

  变量名称

  描述

  scale

  当前的比例因子

  client_id

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

  脚本文件元命令以(\)开始。元命令参数由空格分隔。支持如下元命令:

  \set varname operand1 [ operator operand2 ]

  设置变量varname整数值计算公式。每个操作数可以是一个整数常量或是用:variablename引用的一个是整数类型的变量。操作符可以是+、-、*、/。

  例:

  \set ntellers 10 * :scale

  \setrandom varname min max

  设置变量varname为一个在min和max之间的随机整数。min和max可以是一个整数常量或是用: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 的开头增加一个额外的:。

  例e:

  \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-like事务的完整定义如下:

  \setnbranches :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-interval,pgbench会将每个事务消耗的时间写入到一个日志文件中。这个日志文件会被命名为pgbench.nnn,nnn是pgbench进程的PID,如果通过-j指定的参数大于等于2,每个pgbench进程会有多个工作线程。每个工作线程都会有单独的日志文件。第一个工作线程的日志文件名称与进程的日志文件名称相同,其他的日志文件会被命名为pgbench_log.nnn.mmm,mmm是一个从1开始的序号。

  log的格式如下:

  client_id transaction_notime file_notime_epoch time_us[schedule_lag]

  time 是事务消耗的全部时间,单位是ms。

  file_no用于说明使用的是哪个脚本文件(在使用-f指定了多个脚本文件时是有用的)。

  time_epoch/time_us是用于展示事务完成的UNIX时间戳和一个毫秒偏移量(适用于创建一个ISO 8601与分数秒时间戳)。

  schedule_lag是事务计划开始时间和事务实际开始时间的时间差。这个值只有在使用--rate参数时才会有。

  样例输出:

  0 199 2241 0 1175850568 995598

  0 200 2465 0 1175850568998079

  0 201 2513 0 1175850569 608

  0 202 2038 0 1175850569 2663

  当长时间运行测试时,会执行大量的事务,此时日志文件会变得很大。--sampling-rate参数可以被用来指定只记录一定比例的随机事务样本执行情况。

  聚合的日志

  指定--aggregate-interval参数,日志使用稍微不同的格式:

  interval_start num_of_transactionslatency_sum latency_2_sum min_latencymax_latency [lag_sum lag_2_sum min_lag max_lag]

  interval_start 是间隔开始时间(UNIX时间戳)

  num_of_transactions 是这段时间间隔中执行的事务个数。

  latency_sum 是时延总和(可以通过这个值计算平均时延)

  接下来的两个字段用于方差估计。latency_sum是时延总和,latency_2_sum是 a sum of 2nd powers of latencies。

  最后两个字段是min_latency (这段时间中的最小时延值)、max_latency(这段时间中的最大时延值)。

  在时间间隔中提交的事务才会被统计。最后四个字段lag_sum,lag_2_sum, min_lag, and max_lag只有在使用了--rate时才会显示。这几个时间值是从每个事务等待前一个完成时开始计算,也就是每个事务计划开始时间和实际开始时间之间的差异。

  样例输出:

  13458285015601 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会收集每个客户端的每条sql语句的执行时间。基准测试完成后,依赖于每个语句的时延,会输出这些统计项的平均值。

  对于默认的事务脚本,输出类似如下:

  startingvacuum...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_accountsWHERE 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-like测试场景,初始化的比例因子(-s)大小至少要和你设置的客户端数量(-c)相等,否则会有大量时间消耗在更新争用上。pgbench_branches表中只有-s行,并且每个事务都会尝试更新这样行中一行,因此-c值超过-s值将导致大量事务等待其他事务完成。

  默认的测试场景也很敏感,这是由于从表初始化开始表中累积的dead记录和dead空间导致的。要理解对应的结果你必须跟踪更新总数和什么时候会启动vacuum。如果启用了autovacuum就会导致不可预知的测量性能变化。

  pgbench的一个限制是当试图测试大量的客户端会话时,pgbench本身会成为瓶颈。这可以通过在非数据库服务器的机器上运行pgbench来保证,同时要保证pgbench运行的机器和数据库服务器之间是低网络延迟的。同样,同时在不同机器上(非数据库服务器)运行多个pgbench实例也是有效的。

上一篇:Postgresql 或GreenPlum 查询结果部分字段转json格式并保留字段名(row_to_json)


下一篇:PostgreSQL 系统监控大全