暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

PostgreSQL运维—pgbech基准测试

原创 李先生 2023-02-27
572

PostgreSQL运维—pgbech基准测试

pgbench

pgbench — 在PostgreSQL上运行基准测试工具

概要

pgbench -i[ option…] [ dbname]

pgbench[ option…] [ dbname]

描述

pgbench是一个在PostgreSQL上运行基准测试的简单程序。它反复运行相同的SQL命令序列,可能是在多个并发数据库会话中,然后计算平均事务速率(每秒事务数)。默认情况下,pgbench测试一个松散基于TPC-B的场景,每个事务涉及五个SELECT、UPDATE和INSERT命令。然而,通过编写自己的事务脚本文件来测试其他情况很容易。
pgbench的典型输出如下所示:

pgbench是一个在PostgreSQL上运行基准测试的简单程序。它在多个并发数据库会话中一遍又一遍地运行相同的 SQL 命令序列,然后计算平均事务率(每秒事务数)。默认情况下,pgbench测试一个基于 TPC-B 的场景,每个事务涉及五个SELECTUPDATEINSERT命令。然而,通过编写自己的事务脚本文件来测试其他情况也是非常容易的。

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 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 事务测试需要预先设置特定的表。应该使用 (initialize) 选项调用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您可以(并且,对于大多数目的,可能应该)使用(scale factor) 选项来增加行数。此时-F也可以使用 (fillfactor) 选项。

完成必要的设置后,您可以使用不包含的命令运行基准测试-i,即

pgbench [ options ] dbname
复制

在几乎所有情况下,您都需要一些选项来进行有用的测试。最重要的选项是-c(客户端数量)、-t(交易数量)、-T(时间限制)和-f(指定自定义脚本文件)。请参阅下面的完整列表。

选项

以下分为三个小节。在数据库初始化期间和运行基准测试时使用不同的选项,但有些选项在这两种情况下都很有用。

初始化选项

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

  • dbname

    指定要测试的数据库的名称。如果未指定,PGDATABASE则使用环境变量。如果未设置,则使用为连接指定的用户名。

  • -i --initialize

    需要调用初始化模式。

  • -I *init_steps* --init-steps=*init_steps*

    只执行一组选定的正常初始化步骤。*init_steps*指定要执行的初始化步骤,每个步骤使用一个字符。每个步骤都按指定的顺序调用。默认值为dtgvp. 可用的步骤是:d(降低)删除任何现有的pgbench表。t(创建表)创建标准pgbench场景使用的表,即pgbench_accountspgbench_branchespgbench_historypgbench_tellersgG(生成数据,客户端或服务器端)生成数据并将其加载到标准表中,替换任何已经存在的数据。使用g(客户端数据生成),数据在pgbench客户端生成,然后发送到服务器。这通过COPY. 使用导致日志记录在为表g生成数据时每 100,000 行打印一条消息。pgbench_accounts使用G(服务器端数据生成),只从pgbench客户端发送小查询,然后在服务器中实际生成数据。此变体不需要大量带宽,但服务器会做更多的工作。使用G会导致日志在生成数据时不打印任何进度消息。默认初始化行为使用客户端数据生成(相当于g)。v(真空)VACUUM在标准表上调用。p(创建主键)在标准表上创建主键索引。f(创建外键)在标准表之间创建外键约束。(请注意,默认情况下不执行此步骤。)

  • -F fillfactor --fillfactor=fillfactor

    使用给定的填充因子创建pgbench_accounts和表。默认值为 100。pgbench_tellers``pgbench_branches

  • -n --no-vacuum

    在初始化期间不执行抽真空。(此选项禁止v初始化步骤,即使它已在 中指定-I。)

  • -q --quiet

    将日志记录切换到安静模式,每 5 秒仅生成一条进度消息。默认日志记录每 100,000 行打印一条消息,通常每秒输出很多行(尤其是在良好的硬件上)。G如果在 中指定,此设置无效-I

  • -s scale_factor --scale=scale_factor

    乘以比例因子生成的行数。例如,将在表-s 100中创建 10,000,000 行。pgbench_accounts默认值为 1。当比例为 20,000 或更大时,用于保存帐户标识符(aid列)的列将切换为使用更大的整数(bigint),以便足够大以容纳帐户标识符的范围。

  • --foreign-keys

    在标准表之间创建外键约束。(此选项将f步骤添加到初始化步骤序列中,如果它尚不存在。)

  • --index-tablespace=*index_tablespace*

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

  • --partition-method=*NAME*

    使用方法创建分区pgbench_accounts表*NAME*。预期值为rangehash。此选项要求--partitions设置为非零。如果未指定,则默认为range.

  • --partitions=*NUM*

    为缩放的帐户数量创建一个分区大小几乎相等的分区pgbench_accounts表。*NUM*默认为0,表示不分区。

  • --tablespace=*tablespace*

    在指定的表空间中创建表,而不是在默认的表空间中。

  • --unlogged-tables

    将所有表创建为未记录的表,而不是永久表。

基准测试选项

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

  • -b scriptname[@weight] --builtin=scriptname[@weight]

    将指定的内置脚本添加到要执行的脚本列表中。可用的内置脚本有tpcb-likesimple-updateselect-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

    pgbench中的工作线程数。在多 CPU 机器上使用多个线程可能会有所帮助。客户端尽可能均匀地分布在可用线程中。默认值为 1。

  • -l --log

    将有关每个事务的信息写入日志文件。详情见下文。

  • -L limit --latency-limit=limit

    持续时间超过*limit毫秒的事务将被单独计算和报告,如late*。当使用节流 ( --rate=...) 时,延迟超过*limitms 的事务,因此没有希望满足延迟限制,根本不会发送到服务器。它们被单独计算和报告为已跳过*。

  • -M querymode --protocol=querymode

    用于向服务器提交查询的协议:simple: 使用简单的查询协议。extended: 使用扩展查询协议。prepared:使用带有准备好的语句的扩展查询协议。在该prepared模式下,pgbench重用从第二次查询迭代开始的 parse 分析结果,因此pgbench比其他模式运行得更快。默认为简单查询协议。

  • -n --no-vacuum

    在运行测试之前不要进行真空吸尘。如果您正在运行不包括标准表、、和的自定义测试场景,则此选项是必需的。pgbench_accounts``pgbench_branches``pgbench_history``pgbench_tellers

  • -N --skip-some-updates

    运行内置的简单更新脚本。的简写-b simple-update

  • -P sec --progress=sec

    每秒显示进度报告*sec*。该报告包括自运行开始以来的时间、自上次报告以来的 TPS 以及自上次报告以来的事务延迟平均值和标准偏差。在 throttling ( -R) 下,延迟是相对于事务计划开始时间计算的,而不是实际事务开始时间,因此它还包括平均计划延迟时间。

  • -r --report-latencies

    在基准测试完成后报告每个命令的平均每条语句延迟(从客户端的角度来看的执行时间)。详情见下文。

  • -R rate --rate=rate

    执行以指定速率为目标的事务,而不是尽可能快地运行(默认)。速率以每秒事务数给出。如果目标速率高于最大可能速率,则速率限制不会影响结果。通过沿着泊松分布的时间表时间线启动交易来确定费率。预期的开始时间安排会根据客户端首次启动的时间向前推进,而不是前一个事务结束的时间。这种方法意味着,当交易超过其最初的预定结束时间时,以后的交易可能会再次赶上。当节流处于活动状态时,运行结束时报告的事务延迟是根据计划的开始时间计算的,因此它包括每个事务必须等待前一个事务完成的时间。等待时间称为调度滞后时间,其平均值和最大值也分别报告。相对于实际事务开始时间的事务等待时间,即在数据库中执行事务所花费的时间,可以通过从报告的等待时间中减去调度滞后时间来计算。如果--latency-limit与 一起使用--rate,则事务可能会滞后很多,以至于在前一个事务结束时它已经超过延迟限制,因为延迟是从计划的开始时间计算的。此类事务不会发送到服务器,而是完全跳过并单独计算。高调度滞后时间表明系统无法以指定的速率处理事务,并使用选定的客户端和线程数。当平均事务执行时间长于每个事务之间的预定间隔时,每个后续事务将进一步落后,并且随着测试运行的时间越长,调度滞后时间将不断增加。发生这种情况时,您将不得不降低指定的交易率。

  • -s scale_factor --scale=scale_factor

    在pgbench的输出中报告指定的比例因子。使用内置测试,这不是必需的;将通过计算表中的行数来检测正确的比例因子pgbench_branches。但是,当仅测试自定义基准(-f选项)时,除非使用此选项,否则比例因子将报告为 1。

  • -S --select-only

    运行内置的仅选择脚本。的简写-b select-only

  • -t transactions --transactions=transactions

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

  • -T seconds --time=seconds

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

  • -v --vacuum-all

    在运行测试之前对所有四个标准表进行真空吸尘。没有-nnor -v,pgbench将清理pgbench_tellersandpgbench_branches表,并将 truncate 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就随机数而言,明确设置种子允许精确地重现运行。由于每个线程管理随机状态,这意味着pgbench如果每个线程有一个客户端并且没有外部或数据依赖关系,则相同调用的完全相同的运行。从统计的角度来看,准确地复制运行是一个坏主意,因为它可以隐藏性能可变性或过度提高性能,例如,通过访问与先前运行相同的页面。但是,它也可能对调试有很大帮助,例如重新运行导致错误的棘手案例。明智地使用。

  • --sampling-rate=*rate*

    将数据写入日志时使用的采样率,以减少生成的日志量。如果给出此选项,则仅记录指定部分的事务。1.0 表示将记录所有事务,0.05 表示仅记录 5% 的事务。记住在处理日志文件时要考虑采样率。例如,在计算 TPS 值时,您需要相应地乘以这些数字(例如,使用 0.01 采样率,您只能得到实际 TPS 的 1/100)。

  • --show-script=scriptname

    在 stderr 上显示内置脚本的实际代码*scriptname*,并立即退出。

常用选项

pgbench还接受以下用于连接参数的常用命令行参数:

  • -h hostname --host=hostname

    数据库服务器的主机名

  • -p port --port=port

    数据库服务器的端口号

  • -U login --username=login

    要连接的用户名

  • -V --version

    打印pgbench版本并退出。

  • -? --help

    显示有关pgbench命令行参数的帮助,然后退出。

退出状态

成功运行将以状态 0 退出。退出状态 1 表示静态问题,例如无效的命令行选项。运行过程中的错误,例如数据库错误或脚本中的问题,将导致退出状态 2。在后一种情况下,pgbench将打印部分结果。

环境

  • PGDATABASE PGHOST PGPORT PGUSER

    默认连接参数。

与大多数其他PostgreSQL实用程序一样,此实用程序使用libpq支持的环境变量。

环境变量PG_COLOR指定是否在诊断消息中使用颜色。可能的值为always和。auto``never

笔记

pgbench中实际执行的“事务”是什么?

pgbench执行从指定列表中随机选择的测试脚本。脚本可能包括用 指定的内置脚本和用 指定的-b用户提供的脚本-f。每个脚本可以被赋予在an之后指定的相对权重@,以改变其选择概率。默认权重为1。权重为 的脚本将0被忽略。

默认的内置事务脚本(也使用 调用-b tpcb-like)在每个事务中随机选择aidtidbid发出七个命令delta。该场景受到 TPC-B 基准测试的启发,但实际上并不是 TPC-B,因此得名。

  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;

如果您选择simple-update内置 (also -N),则第 4 步和第 5 步不包含在事务中。这将避免这些表上的更新争用,但它使测试用例更不像 TPC-B。

如果选择select-only内置 (also -S),则仅SELECT发出 。

自定义脚本

pgbench-f通过使用从文件读取的事务脚本(选项)替换默认事务脚本(如上所述)来支持运行自定义基准测试场景。在这种情况下,“事务”算作脚本文件的一次执行。

脚本文件包含一个或多个以分号结尾的 SQL 命令。空行和以 开头--的行将被忽略。脚本文件还可以包含“元命令”,由pgbench本身解释,如下所述。

在PostgreSQL 9.6 之前,脚本文件中的 SQL 命令由换行符终止,因此它们不能跨行继续。现在需要一个分号来分隔连续的 SQL 命令(尽管如果 SQL 命令后跟一个元命令,则不需要分号)。如果您需要创建一个适用于新旧版本pgbench的脚本文件,请确保将每个 SQL 命令写在以分号结尾的单行上。

脚本文件有一个简单的变量替换工具。变量名必须由字母(包括非拉丁字母)、数字和下划线组成,且第一个字符不是数字。变量可以通过-D上面解释的命令行选项设置,也可以通过下面解释的元命令设置。除了-D命令行选项预设的任何变量外,还有一些自动预设的变量,如下表所示。为这些变量指定的值-D优先于自动预设。设置后,变量的值可以通过写入 插入到 SQL 命令中:variablename。当运行多个客户端会话时,每个会话都有自己的一组变量。pgbench在一个语句中最多支持 255 个变量使用。

pgbench 自动变量

多变的 描述
client_id 标识客户端会话的唯一编号(从零开始)
default_seed 默认情况下用于散列和伪随机排列函数的种子
random_seed 随机生成器种子(除非用 覆盖-D
scale 当前比例因子

脚本文件元命令以反斜杠 ( ) 开头\,通常延伸到行尾,尽管它们可以通过编写反斜杠返回继续到其他行。元命令的参数由空格分隔。支持这些元命令:

  • \gset [prefix] \aset [prefix]

    这些命令可以用来结束SQL查询,代替终止分号(;)。
    使用\gset命令时,前面的SQL查询将返回一行,其中的列存储在以列名命名的变量中,如果提供了前缀,则以前缀作为前缀。
    使用\aset命令时,所有组合的SQL查询(以\;分隔)将其列存储到以列名命名的变量中,如果提供了前缀,则使用前缀作为前缀。如果查询不返回任何行,则不进行赋值,可以测试变量是否存在以检测这一点。如果查询返回多行,则保留最后一个值。
    \gset和\aset不能在管道模式下使用,因为在命令需要查询结果时,查询结果还不可用。
    下面的示例将第一个查询中的决算余额放入变量abalance,并用第三个查询中的整数填充变量p_2和p_3。第二个查询的结果将被丢弃。最后两个组合查询的结果存储在变量4和变量5中。

  • \if expression

  • \elif expression

  • \else

  • \endif

这组命令实现了可嵌套的条件块,类似于psql’s \if expression。条件表达式与带有 的条件表达式相同\set,非零值被解释为真。

  • \set varname expression

将变量设置*varname为从 计算的值expression。表达式可能包含NULL常量、布尔常量TRUEFALSE整数常量,例如5432双精度常量3.14159、对变量的引用:variablename*、具有通常 SQL 优先级和关联性的运算符、函数调用、SQLCASE通用条件表达式和括号。

函数和大多数运算符NULLNULL输入时返回。

出于条件目的,非零数值是TRUE,零数值NULLFALSE

过大或过小的整数和双精度常量,以及整数算术运算符(+、和)会在溢出时引发错误-*``/

当没有为 a 提供 finalELSE子句时CASE,默认值为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*具有整数值的变量的引用。例子:\睡眠 10 毫秒

  • \setshell *varname* *command* [ *argument* ... ]

    使用给定的 (s)将变量设置*varname为 shell 命令的结果。该命令必须通过其标准输出返回一个整数值。commandargumentcommand每个argument都可以是文本常量或:variablename对变量的引用。如果要使用以argument冒号开头的,请在 . 开头写一个额外的冒号argument*。例子:\setshell variable_to_be_assigned 命令 literal_argument :variable ::literal_starting_with_colon

  • \shell *command* [ *argument* ... ]

    与 相同\setshell,但命令的结果被丢弃。例子:\shell 命令 literal_argument : 变量 ::literal_starting_with_colon

  • \startpipeline \endpipeline

    这些命令分隔 SQL 语句管道的开始和结束。在管道模式下,语句被发送到服务器而不等待先前语句的结果。管道模式需要使用扩展查询协议。

内置运算符

下表中列出的算术、按位、比较和逻辑运算符内置在pgbench中,可以在出现在\set. 运算符按优先级递增的顺序列出。除非另有说明,如果任一输入为 double,则接受两个数字输入的运算符将产生一个 double 值,否则它们将产生一个整数结果。

pgbench 运算符

操作员描述例子)
boolean OR *boolean*→*boolean*逻辑或5 or 0TRUE
boolean AND *boolean*→*boolean*逻辑与3 and 0FALSE
NOT *boolean*→*boolean*逻辑非not falseTRUE
boolean IS [NOT] (NULL|TRUE|FALSE)*boolean*布尔值测试1 is nullFALSE
value ISNULL|NOTNULL*boolean*无效测试1 notnullTRUE
number = *number*→*boolean*平等的5 = 4FALSE
number <> *number*→*boolean*不相等5 <> 4TRUE
number != *number*→*boolean*不相等5 != 5FALSE
number < *number*→*boolean*少于5 < 4FALSE
number <= *number*→*boolean*小于或等于5 <= 4FALSE
number > *number*→*boolean*比…更棒5 > 4TRUE
number >= *number*→*boolean*大于或等于5 >= 4TRUE
integer | *integer*→*integer*按位或1 | 23
integer # *integer*→*integer*按位异或1 # 32
integer & *integer*→*integer*按位与1 & 31
~ *integer*→*integer*按位非~ 1-2
integer << *integer*→*integer*按位左移1 << 24
integer >> *integer*→*integer*按位右移8 >> 22
number + *number*→*number*添加5 + 49
number - *number*→*number*减法3 - 2.01.0
number * *number*→*number*乘法5 * 420
number / *number*→*number*除法(如果两个输入都是整数,则将结果截断为零)5 / 31
integer % *integer*→*integer*模数(余数)3 % 21
- *number*→*number*否定- 2.0-2.0

内置函数

列出的函数内置于pgbench中,并且可以用于出现在\set.

pgbench 函数

功能描述例子)
abs( number) → 与输入相同的类型绝对值abs(-17)17
debug( number) → 与输入相同的类型将参数打印到stderr,并返回参数。debug(5432.1)5432.1
double( number) →double投加倍。double(5432)5432.0
exp( number) →double指数(e提高到给定的幂)exp(1.0)2.718281828459045
greatest( number[ ,... ] ) →double如果任何参数是双精度的,否则integer选择参数中的最大值。greatest(5, 4, 3, 2)5
hash( value[ ,seed ] ) →integer这是 的别名hash_murmur2hash(10, 5432)-5817877081768721676
hash_fnv1a( value[ ,seed ] ) →integer计算FNV-1a 哈希–Noll–Vo_hash_function)。hash_fnv1a(10, 5432)-7793829335365542153
hash_murmur2( value[ ,seed ] ) →integer计算MurmurHash2 哈希。hash_murmur2(10, 5432)-5817877081768721676
int( number) →integer转换为整数。int(5.4 + 3.8)9
least( number[ ,... ] ) →double如果任何参数是双精度的,否则integer选择参数中的最小值。least(5, 4, 3, 2.1)2.1
ln( number) →double自然对数ln(2.718281828459045)1.0
mod( integer, integer) →integer模数(余数)mod(54, 32)22
permute( i, size[, seed] ) →integer的置换值*i,在范围内[0, size)。这是i(模size) 在整数 的伪随机排列中的新位置0...size-1,由 参数化seed*,见下文。permute(0, 4)an integer between 0 and 3
pi() →doubleπ的近似值pi()3.14159265358979323846
pow( x, y) →double``power( x, y) →doublex提升到权力ypow(2.0, 10)1024.0
random( lb, ub) →integer计算 中的均匀分布的随机整数[lb, ub]random(1, 10)an integer between 1 and 10
random_exponential( lb, ub, parameter) →integer计算 中的指数分布随机整数[lb, ub],见下文。random_exponential(1, 10, 3.0)an integer between 1 and 10
random_gaussian( lb, ub, parameter) →integer计算 中的高斯分布随机整数[lb, ub],见下文。random_gaussian(1, 10, 2.5)an integer between 1 and 10
random_zipfian( lb, ub, parameter) →integer在 中计算 Zipfian 分布的随机整数[lb, ub],见下文。random_zipfian(1, 10, 1.5)an integer between 1 and 10
sqrt( number) →double平方根sqrt(2.0)1.414213562

random函数使用均匀分布生成值,即所有值都以相等的概率在指定范围内绘制。random_exponential和函数需要一个额外的双参数random_gaussianrandom_zipfian确定分布的精确形状。

  • 对于指数分布,parameter通过在 处截断快速减小的指数分布parameter,然后投影到边界之间的整数来控制分布。准确地说,与

    f(x) = exp(-parameter * (x - min) / (max - min + 1)) / (1 - exp(-parameter))
    复制

    然后以概率绘制*i介于minmax*之间的值: f(i) - f(i + 1)

    直观地说, 越大*parameter,接近的值被访问的频率越高,接近的值min被访问的频率越低max。越接近 0 parameter,访问分布越平坦(更均匀)。分布的粗略近似是,范围内最频繁的 1% 值,接近,在% 的时间内min被绘制。parameterparameter*值必须严格为正。

  • -parameter对于高斯分布,区间映射到在左侧和+parameter右侧截断的标准正态分布(经典钟形高斯曲线) 。区间中间的值更有可能被绘制。准确地说,ifPHI(x)是标准正态分布的累积分布函数,均值mu定义为(max + min) / 2.0,其中

    f(x) = PHI(2.0 * parameter * (x - mu) / (max - min + 1)) / (2.0 * PHI(parameter) - 1)
    复制

    然后用概率f(i+0.5)-f(i-0.5)绘制最小值和最大值之间的值i。直观地说,parameter越大,越频繁地绘制接近区间中间的值,越不频繁地绘制接近最小和最大边界的值。大约67%的值取自中间1 /parameter,即平均值附近的0.5个/parameter,中间2个/parameter中的95%个,这是平均值附近的相对1个/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越大,越频繁地绘制距离间隔开始越近的值。分布是这样的,假设范围从1开始,绘制k与绘制k+1的概率之比是((k+1)/k)** parameter。例如,random_zipfian(1,…,2.5)生成值1的频率大约是2的(2/1)** 2.5=5.66倍,其本身生成的频率是3的(3/2)** 2.5=2.76倍,依此类推。

    pgbench的实现基于“非均匀随机变量生成”,Luc Devroye,p。550-551,Springer 1986。由于该算法的限制,该*parameter*值被限制在 [1.001, 1000] 范围内。

在设计非均匀选择行的基准时,请注意,选择的行可能与其他数据相关,例如序列中的 ID 或物理行排序,这可能会影响性能测量。

为避免这种情况,您可能希望使用该permute函数或具有类似效果的其他一些附加步骤来打乱所选行并删除此类相关性。

散列函数hashhash_murmur2hash_fnv1a接受输入值和可选的种子参数。如果未提供种子,:default_seed则使用 的值,除非由命令行-D选项设置,否则将随机初始化。

permute接受输入值、大小和可选的种子参数。它生成范围内整数的伪随机排列[0, size),并返回排列值中输入值的索引。选择的排列由种子参数化:default_seed,如果未指定,则默认为 。与散列函数不同,permute确保输出值中没有冲突或漏洞。区间外的输入值以大小为模进行解释。如果大小不是正数,该函数会引发错误。permute可用于分散非均匀随机函数的分布,例如random_zipfianorrandom_exponential以便更频繁地绘制的值不是平凡相关的。例如,下面的pgbench脚本模拟了社交媒体和博客平台可能的现实世界工作负载,其中一些帐户会产生过多的负载:

\set size 1000000 \set r random_zipfian(1, :size, 1.07) \set k 1 + permute(:r, :size)
复制

在某些情况下,需要几个彼此不相关的不同分布,这时可选的种子参数就派上用场了:

\set k1 1 + permute(:r, :size, :default_seed + 123) \set k2 1 + permute(:r, :size, :default_seed + 321)
复制

类似的行为也可以近似为hash

\set size 1000000 \set r random_zipfian(1, 100 * :size, 1.07) \set k 1 + abs(hash(:r)) % :size
复制

但是,由于hash会产生冲突,因此某些值将无法到达,而其他值将比原始分布的预期更频繁。

例如,内置的类 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;
复制

此脚本允许事务的每次迭代引用不同的、随机选择的行。(这个例子还说明了为什么每个客户端会话都有自己的变量很重要——否则它们不会独立地接触不同的行。)

每笔交易记录

使用-l选项(但不使用–aggregate interval选项),pgbench会将每个事务的信息写入日志文件。日志文件将被命名为prefix。nnn,其中前缀默认为pgbench_log,nnn是pgbench进程的PID。可以使用–log prefix选项更改前缀。如果-j选项为2或更高,因此有多个工作线程,每个工作线程都有自己的日志文件。第一个worker将使用与标准单个worker案例中相同的名称作为其日志文件。其他工作人员的其他日志文件将命名为prefix。nnn。mmm,其中mmm是每个工作者的序列号,从1开始。日志的格式为:

client_id transaction_no time script_no time_epoch time_us[ ]
 schedule_lag 
复制

where*client_id指示哪个客户端会话运行了事务,transaction_no计算该会话运行了多少事务,time是总经过的事务时间(以微秒为单位),script_no标识使用了哪个脚本文件(当使用-for指定多个脚本时很有用-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=100and--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 毫秒)超过了 5 毫秒的限制。接下来的两个事务被跳过了,因为它们甚至在开始之前就已经迟到了。

在可以处理大量事务的硬件上运行长时间测试时,日志文件会变得非常大。该--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_lagsum_lag_2min_lagmax_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选项,pgbench会收集每个客户端执行的每个语句的已用事务时间。然后,它会在基准测试完成后报告这些值的平均值,称为每个语句的延迟。

对于默认脚本,输出将类似于以下内容:

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 = 10.870 ms
latency stddev = 7.341 ms
initial connection time = 30.954 ms
tps = 907.949122 (without initial connection time)
statement latencies in milliseconds:
    0.001  \set aid random(1, 100000 * :scale)
    0.001  \set bid random(1, 1 * :scale)
    0.001  \set tid random(1, 10 * :scale)
    0.000  \set delta random(-5000, 5000)
    0.046  BEGIN;
    0.151  UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;
    0.107  SELECT abalance FROM pgbench_accounts WHERE aid = :aid;
    4.241  UPDATE pgbench_tellers SET tbalance = tbalance + :delta WHERE tid = :tid;
    5.245  UPDATE pgbench_branches SET bbalance = bbalance + :delta WHERE bid = :bid;
    0.102  INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP);
    0.974  END;
复制

如果指定了多个脚本文件,则会分别报告每个脚本文件的平均值。

请注意,收集每个语句延迟计算所需的额外时间信息会增加一些开销。这将减慢平均执行速度并降低计算的 TPS。减速量因平台和硬件而异。比较启用和不启用延迟报告的平均 TPS 值是衡量时序开销是否显着的好方法。

良好实践

使用pgbench生成完全无意义的数字非常容易。以下是一些指导方针,可帮助您获得有用的结果。

首先,永远不要相信任何只运行几秒钟的测试。使用-t-T选项使运行至少持续几分钟,以平均噪音。在某些情况下,您可能需要数小时才能获得可重现的数字。尝试运行几次测试以了解您的数字是否可重现是个好主意。

对于默认的类 TPC-B 测试场景,初始化比例因子 ( -s) 应至少与您打算测试的最大客户端数量 ( -c) 一样大;否则,您将主要衡量更新争用。表中只有-spgbench_branches,每个事务都想更新其中的一个,因此-c超过的值-s无疑会导致大量事务阻塞等待其他事务。

默认测试场景对表初始化后的时间也非常敏感:表中死行和死空间的累积会改变结果。要了解结果,您必须跟踪更新的总数以及何时进行清理。如果启用了 autovacuum,则可能会导致测量性能发生不可预测的变化。

pgbench的一个限制是,当尝试测试大量客户端会话时,它本身可能成为瓶颈。这可以通过在与数据库服务器不同的机器上运行pgbench来缓解,尽管低网络延迟将是必不可少的。在多台客户端机器上针对同一个数据库服务器同时运行多个pgbench实例甚至可能很有用。

安全

如果不受信任的用户可以访问未采用安全模式使用模式的数据库,请不要在该数据库中运行pgbench 。pgbench使用非限定名称并且不操纵搜索路径。

「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论