ltbench — 在LightDB上运行一个基准测试
ltbench
-i
[option
...] [dbname
]
ltbench
[option
...] [dbname
]
ltbench是一种在LightDB上运行基准测试的简单程序。它可能在并发的数据库会话中一遍一遍地运行相同序列的 SQL 命令,并且计算平均事务率(每秒的事务数)。默认情况下,ltbench会测试一种基于 TPC-B 但是要更宽松的场景,其中在每个事务中涉及五个SELECT
、UPDATE
以及INSERT
命令。但是,通过编写自己的事务脚本文件很容易用来测试其他情况。
ltbench的典型输出像这样:
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)
前六行报告一些最重要的参数设置。
接下来的行报告完成的事务数以及预期的事务数(后者就是客户端数量与每个客户端事务数的乘积),除非运行在完成之前失败,这些值应该是相等的(在-T
模式中,只有实际的事务数会被打印出来)。
最后一行报告每秒的事务数。
默认的类 TPC-B 事务测试要求预先设置好特定的表。可以使用-i
(初始化)选项调用ltbench来创建并且填充这些表(当你在测试一个自定义脚本时,你不需要这一步,但是需要按你自己的测试需要做一些设置工作)。初始化类似这样:
ltbench -i [other-options
]dbname
其中dbname
是要在其中进行测试的预先创建好的数据库的名称(你可能还需要-h
、-p
或-U
选项来指定如何连接到数据库服务器)。
ltbench -i
会创建四个表ltbench_accounts
、
ltbench_branches
、ltbench_history
以及ltbench_tellers
,如果同名表已经存在会被先删除。如果你已经有同名表,一定注意要使用另一个数据库!
在默认的情况下“比例因子”为 1,这些表初始包含的行数为:
table # of rows --------------------------------- ltbench_branches 1 ltbench_tellers 10 ltbench_accounts 100000 ltbench_history 0
你可以使用-s
(比例因子)选项增加行的数量。-F
(填充因子)选项也可以在这里使用。
一旦你完成了必要的设置,你就可以用不包括-i
的命令运行基准,也就是:
ltbench [options
]dbname
在近乎所有的情况中,你将需要一些选项来做一次有用的测试。最重要的选项是-c
(客户端数量)、
-t
(事务数量)、-T
(时间限制)以及-f
(指定一个自定义脚本文件)。完整的列表见下文。
下面分成三个部分。数据库初始化期间使用的选项和运行基准时会使用不同的选项,但也有一些选项在两种情况下都使用。
ltbench接受下列命令行初始化参数:
dbname
指定要测试的数据库的名称。
如果这个没有被指定,将使用环境变量LTDATABASE
。
如果那个也没有设定,将使用指定要连接的用户名称。
-i
--initialize
要求调用初始化模式。
-I init_steps
--init-steps=init_steps
只执行选出的一组普通初始化步骤。init_steps
指定要被执行的初始化步骤,每一个步骤使用一个字符代表。每一个步骤都以指定的顺序被调用。默认是dtgvp
。可用的步骤是:
d
(删除)删除任何已有的ltbench表。
t
(创建表)
创建标准ltbench场景使用的表,即ltbench_accounts
、ltbench_branches
、ltbench_history
以及ltbench_tellers
。
g
或G
(生成数据、客户端或服务器端)生成数据并且装入到标准的表中,替换掉已经存在的任何数据。
使用 g
(客户端数据生成),数据在 ltbench
客户端生成,然后发送到服务器。 这通过 COPY
广泛使用客户端/服务器带宽。 使用 g
会导致日志记录每 100,000 行打印一条消息,同时为 ltbench_accounts
表生成数据。
使用G
(服务器端数据生成),仅从ltbench
客户端发送少量查询,然后在服务器中实际生成数据。 此变体不需要大量带宽,但服务器将完成更多工作。
使用G
会导致日志记录在生成数据时不打印任何进度消息。
默认的初始化行为使用客户端数据生成(相当于g
)。
v
(清理)
在标准的表上调用VACUUM
。
p
(创建主键)在标准的表上创建主键索引。
f
(创建外键)在标准的表之间创建外键约束(注意这一步默认不会被执行)。
-F
fillfactor
--fillfactor=
fillfactor
用给定的填充因子创建表ltbench_accounts
、ltbench_tellers
以及ltbench_branches
。默认是100。
-n
--no-vacuum
在初始化期间不执行清理(这个选项会抑制v
初始化步骤,即便在-I
中指定了该步骤)。
-q
--quiet
把记录切换到安静模式,只是每 5 秒产生一个进度消息。默认的记录会每 100,000 行打印一个消息,这经常会在每秒钟输出很多行(特别是在好的硬件上)。
如果在 -I
中指定了 G
,则此设置无效。
-s
scale_factor
--scale=
scale_factor
将生成的行数乘以比例因子。例如,-s 100
将在ltbench_accounts
表中创建 10,000,000 行。默认为 1。当比例为 20,000 或更高时,用来保存账号标识符的列(aid
列)将切换到使用更大的整数(bigint
),这样才能足以保存账号标识符。
--foreign-keys
在标准的表之间创建外键约束(如果f
在初始化步骤序列中不存在,这个选项会把它加入)。
--index-tablespace=index_tablespace
在指定的表空间而不是默认表空间中创建索引。
--partition-method=NAME
使用 NAME
方法创建一个分区的 ltbench_accounts
表。 预期值为 range
或 hash
。 此选项要求 --partitions
设置为非零。 如果未指定,默认值为 range
。
--partitions=NUM
创建一个分区 ltbench_accounts
表,其中 NUM
分区的大小与按比例缩放的帐户数几乎相等。
默认为 0
,表示没有分区。
--tablespace=tablespace
在指定的表空间而不是默认表空间中创建表。
--unlogged-tables
把所有的表创建为非日志记录表而不是永久表。
ltbench接受下列命令行基准参数:
-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
ltbench中的工作者线程数量。在多 CPU 机器上使用多于一个线程会有用。客户端会尽可能均匀地分布到可用的线程上。默认为 1。
-l
--log
把与每一个事务相关的信息写到一个日志文件中。详见下文。
-L
limit
--latency-limit=
limit
对持续超过limit
毫秒的事务进行独立的计数和报告,
这些事务被认为是迟到(late)了的事务。
在使用限流措施时(--rate=...
),滞后于计划超过
limit
毫秒并且因此没有希望满足延迟限制的事务根本
不会被发送给服务器。这些事务被认为是被跳过(skipped)
的事务,它们会被单独计数并且报告。
-M
querymode
--protocol=
querymode
要用来提交查询到服务器的协议:
simple
:使用简单查询协议。
extended
使用扩展查询协议。
prepared
:使用带预备语句的扩展查询语句。
在prepared
模式中,ltbench重用从第二次查询迭代开始的语法分析结果,因此ltbench运行速度比其他模式快。
默认是简单查询协议(详见Chapter 51)。
-n
--no-vacuum
在运行测试前不进行清理。如果你在运行一个不包括标准的表ltbench_accounts
、
ltbench_branches
、ltbench_history
和
ltbench_tellers
的自定义测试场景时,这个选项是必需的。
-N
--skip-some-updates
运行内建的简单更新脚本。这是-b simple-update
的简写。
-P
sec
--progress=
sec
每sec
秒显示进度报告。该报告包括运行了多长时间、从上次报告以来的 tps 以及从上次报告以来事务延迟的平均值和标准偏差。如果低于限流值(-R
),延迟会相对于事务预定的开始时间(而不是实际的事务开始时间)计算,因此其中也包括了平均调度延迟时间。
-r
--report-latencies
在基准结束后,报告平均的每个命令的每语句等待时间(从客户端的角度来说是执行时间)。详见下文。
-R
rate
--rate=
rate
按照指定的速率执行事务而不是尽可能快地执行(默认行为)。该速率 以 tps(每秒事务数)形式给定。如果目标速率高于最大可能速率,则 该速率限制不会影响结果。
该速率的目标是按照一条泊松分布的调度时间线开始事务。期望的开始 时间表会基于客户端第一次开始的时间(而不是上一个事务结束的时 间)前移。这种方法意味着当事务超过它们的原定结束时间时,更迟的 那些有机会再次追赶上来。
当限流措施被激活时,运行结束时报告的事务延迟是从预订的开始时间计 算而来的,因此它包括每一个事务不得不等待前一个事务结束所花的时 间。该等待时间被称作调度延迟时间,并且它的平均值和最大值也会被 单独报告。关于实际事务开始时间的事务延迟(即在数据库中执行事务 所花的时间)可以用报告的延迟减去调度延迟时间计算得到。
如果把--latency-limit
和--rate
一起使用,
当一个事务在前一个事务结束时已经超过了延迟限制时,它可能会滞后
非常多,因为延迟是从计划的开始时间计算得来。这类事务不会被发送
给服务器,而是一起被跳过并且被单独计数。
一个高的调度延迟时间表示系统无法用选定的客户端和线程数按照指定 的速率处理事务。当平均的事务执行时间超过每个事务之间的调度间隔 时,每一个后续事务将会落后更多,并且随着测试运行时间越长,调度 延迟时间将持续增加。发生这种情况时,你将不得不降低指定的事务速率。
-s
scale_factor
--scale=
scale_factor
在ltbench的输出中报告指定的比例因子。对于内建测试,这并非必需;正确的比例因子将通过对ltbench_branches
表中的行计数来检测。不过,当只测试自定义基准(-f
选项)时,比例因子将被报告为 1(除非使用了这个选项)。
-S
--select-only
执行内建的只有选择的脚本。是-b select-only
简写形式。
-t
transactions
--transactions=
transactions
每个客户端运行的事务数量。默认为 10。
-T
seconds
--time=
seconds
运行测试这么多秒,而不是为每个客户端运行固定数量的事务。-t
和-T
是互斥的。
-v
--vacuum-all
在运行测试前清理所有四个标准的表。在没有用-n
以及-v
时,
ltbench将清理ltbench_tellers
和ltbench_branches
表,并且截断ltbench_history
。
--aggregate-interval=seconds
聚集区间的长度(单位是秒)。仅可以与-l
选项一起使用。通过这个选项,日志会包含针对每个区间的概要数据,如下文所述。
--log-prefix=prefix
设置--log
创建的日志文件的文件名前缀。默认是ltbench_log
。
--progress-timestamp
当显示进度(选项-P
)时,使用一个时间戳(Unix 时间)取代从运行开始的秒数。单位是秒,在小数点后是毫秒精度。这可以有助于比较多种工具生成的日志。
--random-seed=
seed
设置随机数生成器种子。为系统的随机数生成器提供种子,然后随机数生成器会产生一个初始生成器状态序列,每一个线程一个状态。seed
的值可以是:time
(默认值,种子基于当前时间)、rand
(使用一种强随机源,如果没有可用的源则失败)或者一个无符号十进制整数值。一个ltbench脚本中会显式(random...
函数)地或者隐式地(如--rate
使用随机数生成器调度事务)调用随机数生成器。在被明确设置时,用作种子的值会显示在终端上。
还可以通过环境变量LTBENCH_RANDOM_SEED
提供用于seed
的值。为了确保所提供的种子影响所有可能的使用,把这个选项放在第一位或者使用环境变量。
明确地设置种子允许准确地再生一个ltbench
运行,对随机数而言。因为随机状态是针对每个线程管理,这意味着如果每一个线程有一个客户端并且没有外部或者数据依赖,则对于一个相同的调用就会有完全相同的ltbench
运行。从一种统计的角度来看,再生运行不是什么好主意,因为它能隐藏性能可变性或者不正当地改进性能,即通过命中前一次运行的相同页面来改进性能。不过,它也可以对调试起到很大帮助作用,例如重新运行一种导致错误的棘手用例。请善用。
--sampling-rate=rate
采样率,在写入数据到日志时被用来减少日志产生的数量。如果给出这个选项,只有指定比例的事务被记录。1.0 表示所有事务都将被记录,0.05 表示只有 5% 的事务会被记录。
在处理日志文件时,记得要考虑这个采样率。例如,当计算TPS值时,你需要相应地乘以这个数字(例如,采样率是 0.01,你将只能得到实际TPS的 1/100)。
--show-script=
scriptname
在 stderr 上显示内置脚本 scriptname
的实际代码,并立即退出。
ltbench 还接受以下用于连接参数的常见命令行参数:
-h
hostname
--host=
hostname
数据库服务器的主机名
-p
port
--port=
port
数据库服务器的端口号
-U
login
--username=
login
要作为哪个用户连接
-V
--version
打印ltbench版本并退出。
-?
--help
显示有关ltbench命令行参数的信息,并且退出。
成功运行将以状态0退出。 退出状态为1表示静态问题,如无效的命令行选项。 运行过程中的错误,例如数据库错误或脚本中的问题将导致退出状态为2。 在后一种情况下,ltbench将打印部分结果。
LTDATABASE
LTHOST
LTPORT
LTUSER
默认连接参数。
此实用程序与大多数其他 LightDB 实用程序一样,使用 libpq 支持的环境变量(请参阅 Section 32.14)。
环境变量 LT_COLOR
指定是否在诊断消息中使用颜色。 可能的值是always
、auto
和
never
.
ltbench执行从指定列表中随机选中的测试脚本。
这个脚本包括带有-b
指定的内建脚本和带有-f
指定的用户提供的自定义脚本。
每一个脚本可以在其后用@
指定一个相对权重,这样可以更改该脚本的选择概率。
默认权重是1
。权重为0
的脚本会被忽略。
默认的内建事务脚本(也会被-b tpcb-like
调用)会在每个事务上发出七个从aid
、tid
、bid
和delta
中随机选择的命令。该场景来自于 TPC-B 基准,但并不是真正的 TPC-B,只是名字像而已。
BEGIN;
UPDATE ltbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;
SELECT abalance FROM ltbench_accounts WHERE aid = :aid;
UPDATE ltbench_tellers SET tbalance = tbalance + :delta WHERE tid = :tid;
UPDATE ltbench_branches SET bbalance = bbalance + :delta WHERE bid = :bid;
INSERT INTO ltbench_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
。
ltbench支持通过从一个文件中(-f
选项)读取事务脚本替换默认的事务脚本(如上文所述)来运行自定义的基准场景。在这种情况中,一个“事务”就是一个脚本文件的一次执行。
脚本文件包含一个或者多个被分号终结的 SQL 命令。空行以及以--
开始的行会被忽略。脚本文件也可以包含“元命令”,它会由ltbench自身解释,详见下文。
对脚本文件有一种简单的变量替换功能。变量名必须由字母(包括非拉丁字母)、数字以及下划线构成,并且首个字符不能是数字。
如上所述,变量可以用命令行的 -D
选项设置,或者按下文所说的使用元命令设置。
除了用-D
命令行选项预先设置的任何变量之外,还有一些被自动预先设置的变量,它们被列在Table 311中。
一个用-D
为这些变量值指定的值会优先于自动的预设值。
一旦被设置,可以在 SQL 命令中写:
variablename
来插入一个变量的值。
当运行多于一个客户端会话时,每一个会话拥有它自己的变量集合。
ltbench在一个语句中支持使用多达255个变量。
Table 311. ltbench 自动变量
变量 | 简介 |
---|---|
client_id | 标识客户端会话的唯一编号(从零开始) |
default_seed | 默认在哈希和伪随机置换函数中使用的种子 |
random_seed | 随机数生成器种子(除非用-D 重载) |
scale | 当前的缩放因子 |
脚本文件元命令以反斜线(\
)开始并且通常延伸到行的末尾,不过它们也能够通过写一个反斜线回车继续到额外行。一个元命令和它的参数用空白分隔。支持的元命令是:
\gset [prefix
]
\aset [prefix
]
这些命令可以用于结束 SQL 查询,代替终止分号 (;
)。
当使用\gset
命令时,前面的 SQL 查询预期返回一行,存储变量名的列在列名后面,如果已经提供的话,则以prefix
作为前缀。
使用\aset
命令时,所有组合的SQL查询(由\;
分隔)将其列存储到以列名命名的变量中,如果提供,则以前缀
作为前缀。如果查询不返回任何行,则不进行赋值,并且可以测试变量是否存在以检测这一点。如果查询返回多行,则保留最后一个值。
下面的示例将第一个查询中的最终帐户余额放入变量abalance
,
并且用第三个查询中的整数填充变量p_two
和p_three
。
第二个查询的结果将被丢弃。
最后两个组合查询的结果存储在变量four
和 five
中。
UPDATE ltbench_accounts SET abalance = abalance + :delta WHERE aid = :aid RETURNING abalance \gset -- compound of two queries 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
这一组命令实现了可嵌套的条件块,类似于ltsql
的\if
expression
。条件表达式与\set
的相同,非零值会被解释为真。
\set varname
expression
设置变量varname
为一个从expression
计算出的值。该表达式可以包含NULL
常量、布尔常量TRUE
和FALSE
、5432
这样的整数常量、3.14159
这样的double常量、对变量的引用:
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
... ]
用给定的argument
设置变量varname
为 shell 命令command
的结果。该命令必须通过它的标准输出返回一个整数值。
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
Table 312中列举的算数、按位、比较以及逻辑操作符都被编译到了ltbench中并且可以被用于\set
中出现的表达式中。
运算符以升序排列。 除非另有说明,如果任一输入为双精度,则采用两个数字输入的运算符将产生双精度值,否则产生整数结果。
Table 312. ltbench操作符
操作符 简介 例子 |
---|
逻辑或
|
逻辑与
|
逻辑非
|
布尔值测试
|
空测试
|
等于
|
不等于
|
不等于
|
小于
|
小于等于
|
大于
|
大于等于
|
按位或
|
按位异或
|
按位与
|
按位非
|
按位左移
|
按位右移
|
加
|
减
|
乘
|
除法(如果两个输入都是整数,则将结果截断为零)
|
模(余数)
|
取反
|
Table 313中列出的函数被编译在ltbench中,并且可能被用在出现于\set
的表达式中。
Table 313. ltbench 函数
函数 简介 例子 |
---|
绝对值
|
将参数打印到stderr,并返回参数。
|
造型成double。
|
指数(
|
选择参数中的最大值。
|
这是
|
计算 FNV-1a hash。
|
计算 MurmurHash2 hash.
|
造型成int。
|
选择参数中的最小值。
|
自然对数
|
模(余数)
|
π的近似值
|
|
计算
|
计算
|
计算
|
计算
|
平方根
|
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,访问分布会越平坦(更均匀)。该分布的粗近似值是范围中当时被抽取 parameter
% 次接近min
的最频繁的 1% 值。parameter
值必须严格为正。
对于高斯分布,区间被映射到一个在左边-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
越大,靠近区间终端的值会被越频繁地抽出,并且靠近上下界两端的值会被更少抽出。大约 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/1)**2.5 =
5.66
次,比 2
更频繁,它本身被产生(3/2)**2.5 = 2.76
次,
比3
更频繁,依此类推。
ltbench的实现是基于"Non-Uniform Random Variate Generation", Luc Devroye, p. 550-551,Springer 1986。
由于该算法的限制,parameter
值限制范围为[1.001, 1000]。
哈希函数 hash
、hash_murmur2
和 hash_fnv1a
接受一个输入值和一个可选的种子参数。
如果未提供种子,则使用 :default_seed
的值,除非通过命令行选项 -D
设置。
哈希函数可用于散列随机函数(例如 random_zipfian
或 random_exponential
)的分布。
例如,以下的 ltbench 脚本模拟了社交媒体和博客平台的可能真实工作负载,其中少数账户会产生过多的负载:
\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 ltbench_accounts SET abalance = abalance + :delta WHERE aid = :aid; SELECT abalance FROM ltbench_accounts WHERE aid = :aid; UPDATE ltbench_tellers SET tbalance = tbalance + :delta WHERE tid = :tid; UPDATE ltbench_branches SET bbalance = bbalance + :delta WHERE bid = :bid; INSERT INTO ltbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP); END;
这个脚本允许该事务的每一次迭代能够引用不同的、被随机选择的行(这个例子也展示了为什么让每一个客户端会话有其自己的变量很重要 — 否则它们不会独立地接触不同的行)。
通过-l
选项(但是没有--aggregate-interval
选项),ltbench把关于每个事务的信息写入到一个日志文件。该日志文件将被命名为
,其中prefix
.nnn
prefix
默认为ltbench_log
,而nnn
是ltbench进程的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
选项,ltbench收集每一个客户端执行的每一个语句花费的事务时间。然后在基准完成后,它会报告这些值的平均值,作为每个语句的延迟。
对于默认脚本,输出看起来会像这样:
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 ltbench_accounts SET abalance = abalance + :delta WHERE aid = :aid; 0.454 SELECT abalance FROM ltbench_accounts WHERE aid = :aid; 5.528 UPDATE ltbench_tellers SET tbalance = tbalance + :delta WHERE tid = :tid; 7.335 UPDATE ltbench_branches SET bbalance = bbalance + :delta WHERE bid = :bid; 0.371 INSERT INTO ltbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP); 1.212 END;
如果指定了多个脚本文件,会为每一个脚本文件单独报告平均值。
注意为每个语句的延迟计算收集额外的时间信息会增加一些负荷。这将拖慢平均执行速度并且降低计算出的 TPS。降低的总量会很显著地依赖于平台和硬件。对比使用和不适用延迟报告时的平均 TPS 值是评估时间开销是否明显的好方法。
很容易使用ltbench产生完全没有意义的数字。这里有一些指导可以帮你得到有用的结果。
排在第一位的是,永远不要相信任何只运行了几秒的测试。使用-t
或-T
选项让运行持续至少几分钟,这样可以用平均值去掉噪声。在一些情况中,你可能需要数小时来得到能重现的数字。多运行几次测试是一个好主意,这样可以看看你的数字是不是可以重现。
对于默认的类 TPC-B 测试场景,初始化的比例因子(-s
)应该至少和你想要测试的最大客户端数量一样大(-c
),否则你将主要在度量更新争夺。在ltbench_branches
表中只有-s
行,并且每个事务都想更新其中之一,因此-c
值超过-s
将毫无疑问地导致大量事务被阻塞来等待其他事务。
默认的测试场景也对表被初始化了多久非常敏感:表中死亡行和死亡空间的累积会改变结果。要理解结果,你必须跟踪更新的总数以及何时发生清理。如果开启了自动清理,它可能会在度量的性能上产生不可预估的改变。
ltbench的一个限制是在尝试测试大量客户端会话时,它自身可能成为瓶颈。这可以通过在数据库服务器之外的一台机器上运行ltbench来缓解,不过必须是具有低网络延迟的机器。甚至可以在多个客户端机器上针对同一个数据库服务器并发地运行多个ltbench实例。
如果不可信用户能够访问没有采用安全方案使用模式的数据库,不要在那个数据库中运行ltbench。ltbench使用非限定名称并且不会操纵搜索路径。