pgbench基准测试
1.pgbench概要
pgbench是一种在PostgreSQL上运行基准测试的简单程序。它可能在并发的数据库会话中一遍一遍地运行相同序列的 SQL 命令,并且计算平均事务率(每秒的事务数)。
默认情况下,pgbench会测试一种基于 TPC-B 但是要更宽松的场景,其中在每个事务中涉及五个SELECT、UPDATE以及INSERT命令。
但是,通过编写自己的事务脚本文件很容易用来测试其他情况。
2.pgbench的初始化
pgbench -i [option...] [dbname]
初始化选项:
dbname
指定要测试的数据库的名称。 如果这个没有被指定,将使用环境变量PGDATABASE。 如果那个也没有设定,将使用指定要连 接的用户名称。
-i, --initialize
要求调用初始化模式。
-I, --init-steps=[dtgGvpf]+ (default "dtgvp")
只执行选出的一组普通初始化步骤。init_steps指定要被执行的初始化步骤,每一个步骤使用一个字符代表。每一个步骤都以 指定的顺序被调用。默认是dtgvp。可用的步骤是:
d 删除任何已有的pgbench表。
t 创建标准pgbench场景使用的表,即pgbench_accounts、pgbench_branches、pgbench_history以及 pgbench_tellers。
g或G 生成数据并且装入到标准的表中,替换掉已经存在的任何数据。
使用 g(客户端数据生成),数据在 pgbench 客户端生成,然后发送到服务器。 这通过 COPY 广泛使用客户端/ 服务器带宽。使用 g 会导致日志记录每 100,000 行打印一条消息,同时为 pgbench_accounts 表生成数据。
使用G(服务器端数据生成),仅从pgbench客户端发送少量查询,然后在服务器中实际生成数据。 此变体不需要 大量带宽,但服务器将完成更多工作。 使用G会导致日志记录在生成数据时不打印任何进度消息。
默认的初始化行为使用客户端数据生成(相当于g)。
v 在标准的表上调用VACUUM。
p 在标准的表上创建主键索引。
f 在标准的表之间创建外键约束(注意这一步默认不会被执行)。
-F, --fillfactor=NUM
用给定的填充因子创建表pgbench_accounts、pgbench_tellers以及pgbench_branches。默认是100。
-n, --no-vacuum
在初始化期间不执行清理(这个选项会抑制v初始化步骤,即便在-I中指定了该步骤)。
-q, --quiet
把记录切换到安静模式,只是每 5 秒产生一个进度消息。默认的记录会每 100,000 行打印一个消息,这经常会在每秒钟输出很多 行(特别是在好的硬件上)。
-s, --scale=NUM
将生成的行数乘以比例因子。例如,-s 100将在pgbench_accounts表中创建 10,000,000 行。默认为 1。当比例为 20,000 或 更高时,用来保存账号标识符的列(aid列)将切换到使用更大的整数(bigint),这样才能足以保存账号标识符。
--foreign-keys
在标准的表之间创建外键约束(如果f在初始化步骤序列中不存在,这个选项会把它加入)。
--index-tablespace=TABLESPACE
在指定的表空间而不是默认表空间中创建索引。
--partition-method=(range|hash)
使用range或hash方法创建一个分区的pgbench_accounts表。此选项要求 --partitions 设置为非零。 如果未指定,默认 range。
--partitions=NUM
创建一个分区pgbench_accounts表,其中NUM分区的大小与按比例缩放的帐户数几乎相等。默认为 0,表示没有分区。
--tablespace=TABLESPACE
在指定的表空间而不是默认表空间中创建表。
--unlogged-tables
把所有的表创建为非日志记录表而不是永久表。
#创建四个表,如果同名表已经存在会被先删除
pgbench_accounts #账户表
pgbench_branches #支行表
pgbench_history #历史信息表
pgbench_tellers #出纳表
#在默认的情况下 -s ‘比例因子’为 1,这些表初始包含的行数为:
table # of rows
pgbench_branches 1
pgbench_tellers 10
pgbench_accounts 100000
pgbench_history 0
3.pgbench基准测试
pgbench [option...] [dbname]
基准选项:
-b, --builtin=NAME[@W]
把指定的内建脚本加入到要被执行的脚本列表中。 可用的内建脚本有:tpcb-like、simple-update和select-only。 这里也接 受内建名称无歧义的前缀缩写。 如果用上特殊的名字list,将会显示内建脚本的列表并且立刻退出。
可选的,在@后面写一个整数权重以调整选择这个脚本而不是其它的概率。 默认的权重为1。
-f, --file=FILENAME[@W]
把一个从filename读到的事务脚本加入到被执行的脚本列表中。
可选的,在@后写一个整数权重以调整选择这个脚本而非其他的概率。 默认权重为1。 (要使用包含@字符的脚本文件名,附加权重 在后面以避免歧义,例如filen@me@1。)
-N, --skip-some-updates
运行内建的简单更新脚本。这是-b simple-update的简写。
-S, --select-only
执行内建的只有选择的脚本。是-b select-only简写形式。
-c, --client=NUM
模拟的客户端数量,也就是并发数据库会话数量。默认为 1。
-C, --connect
为每一个事务建立一个新连接,而不是只为每个客户端会话建立一个连接。这对于度量连接开销有用。
-D, --define=VARNAME=VALUE
定义一个由自定义脚本使用的变量。允许多个-D选项。
-j, --jobs=NUM
pgbench中的工作者线程数量。在多 CPU 机器上使用多于一个线程会有用。客户端会尽可能均匀地分布到可用的线程上。 默认为 1。
-l, --log
把与每一个事务相关的信息写到一个日志文件中。
-L, --latency-limit=NUM
对持续超过 NUM 毫秒的事务进行独立的计数和报告, 这些事务被认为是迟到(late)了的事务。
-M, --protocol=simple|extended|prepared
要用来提交查询到服务器的协议:
simple:使用简单查询协议。
extended使用扩展查询协议。
prepared:使用带预备语句的扩展查询语句。
-n, --no-vacuum
在运行测试前不进行清理。如果你在运行一个不包括标准的表pgbench_accounts、 pgbench_branches、pgbench_history pgbench_tellers的自定义测试场景时,这个选项是必需的。
-P, --progress=NUM
每NUM秒显示进度报告。该报告包括运行了多长时间、从上次报告以来的 tps 以及从上次报告以来事务延迟的平均值和标准偏差。 如果低于限流值(-R),延迟会相对于事务预定的开始时间(而不是实际的事务开始时间)计算,因此其中也包括了平均调度延迟 时间。
-r, --report-per-command
在基准结束后,报告平均的每个命令的每语句等待时间(从客户端的角度来说是执行时间)。
-R, --rate=NUM
按照指定的速率执行事务而不是尽可能快地执行(默认行为)。该速率以 tps(每秒事务数)形式给定。如果目标速率高于最大 可能速率,则该速率限制不会影响结果。
-s, --scale=NUM
在pgbench的输出中报告指定的比例因子。对于内建测试,这并非必需;正确的比例因子将通过对pgbench_branches表中的行 计数来检测。不过,当只测试自定义基准(-f选项)时,比例因子将被报告为 1(除非使用了这个选项)。
-t, --transactions=NUM
每个客户端运行的事务数量。默认为 10。
-T, --time=NUM
运行测试这么多秒,而不是为每个客户端运行固定数量的事务。-t和-T是互斥的。
-v, --vacuum-all
在运行测试前清理所有四个标准的表。在没有用-n以及-v时, pgbench将清理pgbench_tellers 和pgbench_branches表,并且 截断pgbench_history。
--aggregate-interval=NUM
聚集区间的长度(单位是秒)。仅可以与-l选项一起使用。通过这个选项,日志会包含针对每个区间的概要数据。
--failures-detailed report the failures grouped by basic types
--log-prefix=PREFIX
设置--log创建的日志文件的文件名前缀。默认是pgbench_log。
--max-tries=NUM max number of tries to run transaction (default: 1)
--progress-timestamp
当显示进度(选项-P)时,使用一个时间戳(Unix 时间)取代从运行开始的秒数。单位是秒,在小数点后是毫秒精度。这可以 有助于比较多种工具生成的日志。
--random-seed=SEED
设置随机数生成器种子。为系统的随机数生成器提供种子,然后随机数生成器会产生一个初始生成器状态序列,每一个线程一个 状态。
--sampling-rate=NUM
采样率,在写入数据到日志时被用来减少日志产生的数量。如果给出这个选项,只有指定比例的事务被记录。1.0 表示所有事务都 将被记录,0.05 表示只有 5% 的事务会被记录。
--show-script=NAME
在 stderr 上显示内置脚本 scriptname 的实际代码,并立即退出。
--verbose-errors print messages of all errors
普通选项:
-d, --debug 打印调试输出。
-h, --host=HOSTNAME 数据库服务器的主机名
-p, --port=PORT 数据库服务器的端口号
-U, --username=USERNAME 要作为哪个用户连接
-V, --version 打印pgbench版本并退出
-?, --help 显示有关pgbench命令行参数的信息,并且退出
4.pgbench内建脚本
{
"tpcb-like",
"<builtin: TPC-B (sort of)>",
"\\set aid random(1, " CppAsString2(naccounts) " * :scale)\n"
"\\set bid random(1, " CppAsString2(nbranches) " * :scale)\n"
"\\set tid random(1, " CppAsString2(ntellers) " * :scale)\n"
"\\set delta random(-5000, 5000)\n"
"BEGIN;\n"
"UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;\n"
"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
"UPDATE pgbench_tellers SET tbalance = tbalance + :delta WHERE tid = :tid;\n"
"UPDATE pgbench_branches SET bbalance = bbalance + :delta WHERE bid = :bid;\n"
"INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP);\n"
"END;\n"
},{
"simple-update",
"<builtin: simple update>",
"\\set aid random(1, " CppAsString2(naccounts) " * :scale)\n"
"\\set bid random(1, " CppAsString2(nbranches) " * :scale)\n"
"\\set tid random(1, " CppAsString2(ntellers) " * :scale)\n"
"\\set delta random(-5000, 5000)\n"
"BEGIN;\n"
"UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;\n"
"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
"INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP);\n"
"END;\n"
},{
"select-only",
"<builtin: select only>",
"\\set aid random(1, " CppAsString2(naccounts) " * :scale)\n"
"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
}
5.pgbench典型输出格式
前六行报告一些最重要的参数设置。 接下来的行报告完成的事务数以及预期的事务数(后者就是客户端数量与每个客户端事务数的乘积),除非运行在完成之前失败,这些值应该是相等的(在-T模式中,只有实际的事务数会被打印出来)。 最后一行报告每秒的事务数。
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
latency average = 11.013 ms
latency stddev = 7.351 ms
initial connection time = 45.758 ms
tps = 896.967014 (without initial connection time)
6.pgbench使用建议
1. 多运行几次测试是一个好主意,这样可以看看你的数字是不是可以重现。
2. 注意自动清理进程autovacuum对测试的影响。
3. 初始化的比例因子(-s)应该至少和你想要测试的最大客户端数量一样大(-c),否则你将主要在度量更新争夺。因为在pgbench_branches表中只有-s行,并且每个事务都想更新其中之一,因此-c值超过-s将毫无疑问地导致大量事务被阻塞来等待其他事务。
4. pgbench的一个限制是在尝试测试大量客户端会话时,它自身可能成为瓶颈。可以通过在数据库服务器之外的一台机器上运行pgbench来缓解,不过必须是具有低网络延迟的机器。甚至可以在多个客户端机器上针对同一个数据库服务器并发地运行多个pgbench实例。
7.pgbench示例
7.1初始化
create user pgbench with password '12345'; --创建压测用户
create database pgbench owner pgbench; --创建压测数据库
pgbench -i -U pgbench -s 5 pgbench --初始化,将在pgbench_accounts表中创建 500,000 行。
[postgres@localhost ~]$ pgbench -i -U pgbench -s 5 pgbench
dropping old tables...
NOTICE: table "pgbench_accounts" does not exist, skipping
NOTICE: table "pgbench_branches" does not exist, skipping
NOTICE: table "pgbench_history" does not exist, skipping
NOTICE: table "pgbench_tellers" does not exist, skipping
creating tables...
generating data (client-side)...
500000 of 500000 tuples (100%) done (elapsed 0.54 s, remaining 0.00 s)
vacuuming...
creating primary keys...
done in 1.00 s (drop tables 0.00 s, create tables 0.00 s, client-side generate 0.56 s, vacuum 0.07 s, primary keys 0.35 s).
pgbench=> select table_schema,table_name,(select reltuples from pg_class p where p.relname=t.table_name) as table_rows from information_schema.tables t where table_schema='public';
table_schema | table_name | table_rows
--------------+------------------+------------
public | pgbench_accounts | 500000
public | pgbench_branches | 5
public | pgbench_history | 0
public | pgbench_tellers | 50
(4 rows)
7.2.简单示例
#基准测试1,并行工作线程数2,客户端数量4,每客户端事务数60
[postgres@localhost ~]$ pgbench -U pgbench -r -j2 -c4 -t60 pgbench
pgbench (15.1)
starting vacuum...end.
transaction type: <builtin: TPC-B (sort of)>
scaling factor: 5
query mode: simple
number of clients: 4
number of threads: 2
maximum number of tries: 1
number of transactions per client: 60
number of transactions actually processed: 240/240
number of failed transactions: 0 (0.000%)
latency average = 2.867 ms
initial connection time = 36.855 ms
tps = 1395.056849 (without initial connection time)
statement latencies in milliseconds and failures:
0.001 0 \set aid random(1, 100000 * :scale)
0.000 0 \set bid random(1, 1 * :scale)
0.000 0 \set tid random(1, 10 * :scale)
0.000 0 \set delta random(-5000, 5000)
0.162 0 BEGIN;
0.234 0 UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;
0.528 0 SELECT abalance FROM pgbench_accounts WHERE aid = :aid;
0.452 0 UPDATE pgbench_tellers SET tbalance = tbalance + :delta WHERE tid = :tid;
0.536 0 UPDATE pgbench_branches SET bbalance = bbalance + :delta WHERE bid = :bid;
0.331 0 INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP);
0.574 0 END;
#基准测试2,并行工作线程数2,客户端数量10,运行时间1分钟
[postgres@localhost ~]$ pgbench -U pgbench -r -j2 -c10 -T10 pgbench
pgbench (15.1)
starting vacuum...end.
transaction type: <builtin: TPC-B (sort of)>
scaling factor: 5
query mode: simple
number of clients: 10
number of threads: 2
maximum number of tries: 1
duration: 10 s
number of transactions actually processed: 13766
number of failed transactions: 0 (0.000%)
latency average = 7.201 ms
initial connection time = 102.340 ms
tps = 1388.699741 (without initial connection time)
statement latencies in milliseconds and failures:
0.001 0 \set aid random(1, 100000 * :scale)
0.000 0 \set bid random(1, 1 * :scale)
0.000 0 \set tid random(1, 10 * :scale)
0.000 0 \set delta random(-5000, 5000)
0.435 0 BEGIN;
0.377 0 UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;
1.275 0 SELECT abalance FROM pgbench_accounts WHERE aid = :aid;
1.204 0 UPDATE pgbench_tellers SET tbalance = tbalance + :delta WHERE tid = :tid;
1.973 0 UPDATE pgbench_branches SET bbalance = bbalance + :delta WHERE bid = :bid;
0.591 0 INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP);
1.338 0 END;
7.3.自定义示例
#自定义脚本示例
vim custom.sql
\set naccounts 100000 * :scale
\set aid random( 1 , :naccounts )
SELECT abalance FROM pgbench_accounts WHERE aid = :aid;
#压测
[postgres@localhost ~]$ pgbench -U pgbench -r -j2 -c10 -T100 -n -f /opt/custom.sql pgbench
pgbench (15.1)
transaction type: /opt/custom.sql
scaling factor: 1
query mode: simple
number of clients: 10
number of threads: 2
maximum number of tries: 1
duration: 100 s
number of transactions actually processed: 1316079
number of failed transactions: 0 (0.000%)
latency average = 0.759 ms
initial connection time = 97.781 ms
tps = 13173.154850 (without initial connection time)
statement latencies in milliseconds and failures:
0.000 0 \set naccounts 100000 * :scale
0.000 0 \set aid random( 1 , :naccounts )
0.756 0 SELECT abalance FROM pgbench_accounts WHERE aid = :aid;
#指定权重
#把指定的内建脚本加入到要执行的脚本列表中。@之后是一个可选的整数权重,它允许调节抽取该脚本的可能性。如果没有指定,它会被设置为1。如把 simple-update权重设为3, select-only权重设为6, tpcb-like权重设为1
[postgres@localhost ~]$ pgbench -U pgbench -M simple -r -j8 -c10 -T100 -b simple-update@3 -b select-only@6 -b tpcb-like@1 -n pgbench
pgbench (15.1)
transaction type: multiple scripts
scaling factor: 5
query mode: simple
number of clients: 10
number of threads: 8
maximum number of tries: 1
duration: 100 s
number of transactions actually processed: 417195
number of failed transactions: 0 (0.000%)
latency average = 2.395 ms
initial connection time = 99.770 ms
tps = 4175.336992 (without initial connection time)
SQL script 1: <builtin: simple update>
- weight: 3 (targets 30.0% of total)
- 125817 transactions (30.2% of total, tps = 1259.191444)
- number of failed transactions: 0 (0.000%)
- latency average = 4.332 ms
- latency stddev = 2.337 ms
- statement latencies in milliseconds and failures:
0.001 0 \set aid random(1, 100000 * :scale)
0.000 0 \set bid random(1, 1 * :scale)
0.000 0 \set tid random(1, 10 * :scale)
0.000 0 \set delta random(-5000, 5000)
0.653 0 BEGIN;
0.275 0 UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;
1.040 0 SELECT abalance FROM pgbench_accounts WHERE aid = :aid;
0.828 0 INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP);
1.534 0 END;
SQL script 2: <builtin: select only>
- weight: 6 (targets 60.0% of total)
- 249944 transactions (59.9% of total, tps = 2501.469167)
- number of failed transactions: 0 (0.000%)
- latency average = 0.721 ms
- latency stddev = 0.485 ms
- statement latencies in milliseconds and failures:
0.001 0 \set aid random(1, 100000 * :scale)
0.721 0 SELECT abalance FROM pgbench_accounts WHERE aid = :aid;
SQL script 3: <builtin: TPC-B (sort of)>
- weight: 1 (targets 10.0% of total)
- 41434 transactions (9.9% of total, tps = 414.676381)
- number of failed transactions: 0 (0.000%)
- latency average = 6.601 ms
- latency stddev = 3.234 ms
- statement latencies in milliseconds and failures:
0.001 0 \set aid random(1, 100000 * :scale)
0.000 0 \set bid random(1, 1 * :scale)
0.000 0 \set tid random(1, 10 * :scale)
0.000 0 \set delta random(-5000, 5000)
0.654 0 BEGIN;
0.276 0 UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;
1.044 0 SELECT abalance FROM pgbench_accounts WHERE aid = :aid;
0.917 0 UPDATE pgbench_tellers SET tbalance = tbalance + :delta WHERE tid = :tid;
1.326 0 UPDATE pgbench_branches SET bbalance = bbalance + :delta WHERE bid = :bid;
0.915 0 INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP);
1.467 0 END;