他のバージョンの文書 11 | 10 | 9.6 | 9.5 | 9.4 | 9.3 | 9.2 | 9.1 | 9.0 | 8.4 | 8.3 | 8.2 | 8.1 | 8.0 | 7.4 | 7.3 | 7.2

pgbench

pgbenchPostgreSQLに対してベンチマーク試験を行う

概要

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

pgbench [option...] [dbname]

説明

pgbenchPostgreSQL上でベンチマーク試験を行う単純なプログラムです。 これは同一のSQLコマンドの並びを何度も実行します。複数の同時実行データベースセッションで実行することもできます。 そして、トランザクションの速度(1秒当たりのトランザクション数)の平均を計算します。 デフォルトでpgbenchは、1トランザクション当たり5つのSELECTUPDATEINSERTコマンドを含むおおよそTPC-Bに基いたシナリオを試験します。 しかし、独自のトランザクションスクリプトファイルを作成することで他の試験ケースを簡単に実行することができます。

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
tps = 85.184871 (including connections establishing)
tps = 85.296346 (excluding connections establishing)

最初の6行はいくつかの最重要パラメータの設定を表示しています。 次行が完了トランザクション数と予定トランザクション数です(後者は単なるクライアント数とクライアント毎のトランザクション数の積算結果です。) 実行が完了する前に失敗しない限りこれは等しくなります。 (-Tモードでは、トランザクションの実際の数が表示されます) 最後の2行は、データベースセッションを開始するための時間を含める場合と含めない場合の1秒当たりのトランザクション数を示します。

デフォルトのTPC-Bと似たトランザクション試験では、あらかじめ設定する特定のテーブルが必要です。 これらのテーブルを作成し、データを投入するためには、-i(初期化)オプションを付けてpgbenchを呼び出さなければなりません。 (独自スクリプトを試験する場合、この手順は必要ありません。 しかし代わりに試験に必要な何らかの設定を行わなければならないでしょう。) 初期化は以下のようになります。

pgbench -i [ other-options ] dbname

ここでdbnameは試験用に前もって作成されたデータベースの名前です。 (またデータベースサーバの接続方法を指定するために、-h-p-Uが必要になるかもしれません。)

注意

pgbench -iは4つのテーブルpgbench_accountspgbench_branchespgbench_historypgbench_tellersを作成します。 もしあればこうした名前のテーブルは破壊されます。 もし同じ名前のテーブルが存在する場合にはよく注意してください。

デフォルトの倍数の1では、テーブルは初期状態で以下の行数を含みます。

table                   # of rows
---------------------------------
pgbench_branches        1
pgbench_tellers         10
pgbench_accounts        100000
pgbench_history         0

-s(倍数)オプションを使用して行数を増加させることができます(また、ほとんどの目的ではおそらく増加させるべきです)。 また、-F (フィルファクタ)オプションをここで使用しても構いません。

一度この必要な設定を行った後、以下のように-iを持たないコマンドでベンチマークを行うことができます。

pgbench [ options ] dbname

ほとんどすべての場合、有用な試験とするためにいくつかのオプションが必要になります。 最重要オプションは-c(クライアント数)、-t(トランザクション数)、-T (制限時間)、-f(独自スクリプトファイルの指定)です。 以下の全一覧を参照してください。

オプション

以下では、データベース初期化時に使用されるオプション、ベンチマーク実行時に使用されるオプション、どちらの場合でも使われるオプションの3つに分けて説明します。

初期化用のオプション

pgbenchは以下の初期化用のコマンドライン引数を受け付けます。

-i
--initialize

初期化モードを呼び出すために必要です。

-I init_steps
--init-steps=init_steps

標準の初期化ステップの内、選択したものだけを実行します。 init_stepsでは、各ステップ毎に1文字を使って、実行する初期化ステップを指定します。 各ステップは指定した順で実行されます。 デフォルトはdtgvpです。 有効なステップは以下の通りです。

d (Drop)

既存のpgbenchのテーブルを全て削除します。

t (create Tables)

標準のpgbenchシナリオで使われるテーブル、すなわちpgbench_accountspgbench_branchespgbench_historyおよびpgbench_tellersを作成します。

g (Generate data)

データを生成し、既存データを置き換えて、標準の各テーブルに読み込みます。

v (Vacuum)

標準の各テーブルに対してVACUUMを実行します。

p (create Primary keys)

標準の各テーブルにプライマリキーインデックスを作成します。

f (create Foreign keys)

標準のテーブル間に外部キー制約を作成します。 (このステップはデフォルトでは実行されないことに注意してください)

-F fillfactor
--fillfactor=fillfactor

指定したフィルファクタでpgbench_accountspgbench_tellerspgbench_branchesテーブルを作成します。 デフォルトは100です。

-n
--no-vacuum

初期化でバキュームを実行しません。 (このオプションは-Iで指定されていたとしても初期化ステップvを抑止します。)

-q
--quiet

ログ処理を、5秒に1つの進行メッセージのみを生成する静寂モードに切り替えます。 デフォルトのログ処理では、10000行毎にメッセージを1つ出力し、(特に優れたハードウェアでは)1秒当たりに多くのメッセージを出力します。

-s scale_factor
--scale=scale_factor

この倍率で生成される行数を積算します。 例えば、-s 100pgbench_accountsテーブルに10,000,000行を生成することを意味します。 デフォルトは1です。 この倍率が20000以上になると、アカウント識別子の範囲を保持できる程度に大きくなるように、アカウント識別子を保持するために使用される列(aid列)はより大きな整数(bigint)を使用するように切り替わります。

--foreign-keys

標準テーブル間で外部キー制約を作成します。 (このオプションは初期化ステップの並びに、もし無かったならfステップを追加します。)

--index-tablespace=index_tablespace

デフォルトのテーブル空間ではなく、指定したテーブル空間の中にインデックスを作成します。

--tablespace=tablespace

デフォルトのテーブル空間ではなく、指定したテーブル空間の中にテーブルを作成します。

--unlogged-tables

永続テーブルではなくログを取らないテーブルとしてテーブルを作成します。

ベンチマーク用オプション

pgbenchは以下のベンチマーク用コマンドライン引数を受け付けます。

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

指定の組み込みスクリプトを実行されたスクリプトのリストに追加します。 オプションで@の後に整数のweight(重み)をつけることで、そのスクリプトが選ばれる確率を調整することができます。 指定しなかった場合は1に設定されます。 利用可能な組み込みのスクリプトは、tpcb-likesimple-updateselect-onlyです。 組み込みの名前の曖昧な接頭辞も受け付けられます。 特別な名前listを使うと、組み込みスクリプトのリストを表示して、即座に終了します。

-c clients
--client=clients

模擬するクライアント数、つまり、同時に実行されるデータベースセッション数です。 デフォルトは1です。

-C
--connect

各クライアントセッションが一度だけ接続を確立するのではなく、各トランザクションが新しい接続を確立します。 これは接続オーバーヘッドを測定する場合に有用です。

-d
--debug

デバッグ用出力を表示します。

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

独自スクリプト(後述)で使用される変数を定義します。 複数の-Dオプションを使用することができます。

-f filename[@weight]
--file=filename[@weight]

filenameから読み取ったトランザクションスクリプトを実行されたスクリプトのリストに追加します。 オプションで@の後に整数のweight(重み)をつけることで、そのテストが選ばれる確率を調整することができます。 詳細は後で説明します。

-j threads
--jobs=threads

pgbench内のワーカスレッド数です。 複数のスレッドを使用することはマルチCPUマシンで有用になります。 クライアントは利用可能なスレッドの間でできる限り均等に分散されます。 デフォルトは1です。

-l
--log

各トランザクションに関する情報をログファイルに書き出します。 後で詳細を説明します。

-L limit
--latency-limit=limit

limitミリ秒以上続くトランザクションが計数され、別途遅いトランザクションとして報告されます。

制限が使用されると(--rate=...)、limitミリ秒以上遅延がスケジュールされたトランザクションは遅延制限を満たす可能性がないため、サーバに送信されることは決してありません。 これらのトランザクションは計数され、別途スキップされたとして報告されます。

-M querymode
--protocol=querymode

サーバへ問い合わせを送信するために使用するプロトコルです。

  • simple: 簡易問い合わせプロトコルを使用します。

  • extended: 拡張問い合わせプロトコルを使用します。

  • prepared: プリペアドステートメントを伴う拡張問い合わせプロトコルを使用します。

デフォルトは簡易問い合わせプロトコルです。 (詳しい情報は第53章を参照してください)

-n
--no-vacuum

試験を実行する前にバキュームを行いません。 pgbench_accountspgbench_branchespgbench_historypgbench_tellers標準テーブルを含まない独自試験シナリオを実行する場合、このオプションは必要です。

-N
--skip-some-updates

組み込みのsimple-update(単純な更新)のスクリプトを実行します。 -b simple-updateの短縮形です。

-P sec
--progress=sec

sec秒毎の進捗レポートを表示します。 レポートには起動からの経過時間、前回レポート時からのTPS、前回レポート時からのトランザクションの平均待ち時間、標準偏差を含んでいます。 (-R)オプションによる制限下では、待ち時間はトランザクションの実開始時間ではなく、予定開始時間で算出されていますので、平均予定遅延時間が含まれています。

-r
--report-latencies

ベンチマーク完了後の各コマンドにおけるステートメント毎の平均レイテンシ(クライアントから見た実行時間)を報告します。詳しくは下を参照してください。

-R rate
--rate=rate

トランザクションを可能な限り高速(デフォルト)で実行するのではなく、指定された目標レートで実行します。 レートは1秒あたりのトランザクション数で与えられます。目標レートが実施可能な最大レートを越えている場合、レート制限は結果に影響を与えません。

レートはトランザクションの開始予定タイムラインがポアソン分布に沿う事を目標としています。 期待される開始時刻の予定は、前トランザクションの終了時ではなくクライアントの初期起動時に基づいて動かします。 このアプローチはトランザクションがオリジナルの終了予定時刻を過ぎた場合でも、後でまた追い付けることを意味します。

制限がアクティブになると、実行終了時に報告されるトランザクション待ち時間は、予定開始時刻から計算されるので、 各トランザクションが前トランザクションの終了を待たねばならなかった時間を含んでいます。 この待ち時間はスケジュールラグタイムと呼ばれ、平均と最大値も別々に報告されます。 実トランザクション開始時刻についてのトランザクション待ち時間、つまりデータベース内でトランザクションの実行に要した時間は、報告された待ち時間からスケジュールラグタイムを減算することで算出することができます。

--latency-limit--rateと一緒に指定された場合、トランザクションは、先行するトランザクションが終了した際にすでに遅延制限を超えていて、非常に遅れてしまうことがあり得ます。 そのようなトランザクションはサーバに送信さることなくスキップされ、別途カウントされます。

スケジュールラグタイムの高い値は、システムが選択されたクライアント数とスレッド数で、指定されたレートでトランザクションを処理できなかったことを示しています。 トランザクションの平均実行時間が各トランザクション間で予定されていた間隔より長い場合、各逐次トランザクションは更に遅くなり、 スケジュールラグタイムはテスト実行がより長く増加し続けます。 これが起こる場合、指定トランザクションレートを減らす必要があります。

-s scale_factor
--scale=scale_factor

pgbenchの出力で指定した倍率を報告します。 これは組み込みの試験では必要ありません。 正確な倍率がpgbench_branchesテーブルの行数を数えることで検出されます。 しかし、独自ベンチマーク(-fオプション)のみを試験している場合、このオプションを使用しない限り、倍率は1として報告されます。

-S
--select-only

組み込みのselect-only(SELECTのみ)のスクリプトを実行します。 -b select-onlyの短縮形です。

-t transactions
--transactions=transactions

各クライアントが実行するトランザクション数です。 デフォルトは10です。

-T seconds
--time=seconds

クライアントあたりのトランザクション数を固定で指定するよりも長くテストを実行したい場合、ここに指定した秒数でテストを実行します。 -t-Tは互いに排他的です。

-v
--vacuum-all

試験前に4つの標準テーブルすべてをバキュームします。 -n-vもなければ、pgbenchはpgbench_tellerspgbench_branchesテーブルをバキュームし、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を得るだけです。)

共通オプション

pgbenchは以下の共通コマンドライン引数を受け付けます。

-h hostname
--host=hostname

データベースサーバのホスト名

-p port
--port=port

データベースサーバのポート番号

-U login
--username=login

接続ユーザ名

-V
--version

pgbenchのバージョンを表示し、終了します。

-?
--help

pgbenchのコマンドライン引数の説明を表示し、終了します。

注釈

pgbenchで実際に実行されるトランザクションは何か?

pgbenchは指定したリストからランダムに選択したテストスクリプトを実行します。 これには-bの組み込みスクリプトと-fのユーザ定義カスタムスクリプトが含まれます。 各スクリプトには@の後に指定される相対的な重みを与えることができ、それが選ばれる確率を変更することができます。 デフォルトの重みは1です。 重みが0のスクリプトは無視されます。

デフォルトの組み込みトランザクションスクリプト(-b tpcb-likeとすることでも実行されます)は、aidtidbiddeltaからランダムに選択され、トランザクション毎に7つのコマンドを発行します。 このシナリオは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の組み込みを選択した(あるいは-Nを指定した)場合、第4ステップと第5ステップはトランザクションに含まれません。 これにより、これらのテーブルに対する更新の競合を避けられますが、テストケースはさらにTPC-Bらしくなくなります。

select-onlyの組み込みを選択した(あるいは-Sを指定した)場合、SELECTのみが発行されます。

独自スクリプト

pgbenchは、ファイルから読み込んだトランザクションスクリプト(-fオプション)でデフォルトのトランザクションスクリプト(上述)を置き換えて独自のベンチマークシナリオを実行する機能をサポートします。 この場合、トランザクションはスクリプトファイルの1回の実行として数えられます。

スクリプトファイルにはセミコロンで終了するSQLコマンドが1つ以上含まれます。 空行および--から始まる行は無視されます。 スクリプトファイルの行には、pgbench自身が解釈するメタコマンド(後述)も記述することができます。

注記

PostgreSQLの9.6より前では、スクリプトファイル内のSQLコマンドは改行で終了しており、そのため行をまたがって継続することができませんでした。 これからは連続するSQLコマンドを区切るためにセミコロンが必要です(ただし、SQLコマンドの後にメタコマンドが続く場合は、セミコロンは必要ありません)。 pgbenchの古いバージョンと新しいバージョンの両方で動作するスクリプトを作る必要があるなら、各SQLコマンドを1行で書き、終わりにセミコロンを付けるようにしてください。

スクリプトファイル向けの簡単な変数置換機能があります。 変数名は文字(非ラテン文字を含む)、数字およびアンダースコアで構成されていなければなりません。 上で説明したように変数を-Dコマンドラインオプションで設定することができます。 また、後で説明するようにメタコマンドで設定することもできます。 -Dコマンドラインオプションで設定された変数の他に、表 241に記載されているように、自動的に設定される変数がいくつかあります。 -D を使ってこれらの変数に設定された値は、自動設定の値より優先されます。 一度設定すると、変数の値は、:variablenameと書かれてSQLコマンドに挿入されます。 1つ以上のクライアントセッションが実行される場合、セッション毎に独自の変数群を持ちます。

表241 自動変数

変数説明
client_id クライアントセッションを識別する一意の数値(ゼロから始まる)
default_seed デフォルトでハッシュ関数で使われるシード
random_seed ランダムジェネレータのシード(-Dで上書きされていないなら)
scale 現在のスケールファクタ

スクリプトファイルのメタコマンドはバックスラッシュ(\)から始まり、通常は行末まで続きますが、バックスラッシュと改行を書くことで、追加の行に続けることができます。 メタコマンドへの引数は空白文字で区切られます。 以下のメタコマンドがサポートされています。

\if expression
\elif expression
\else
\endif

このコマンド群はpsql\if expressionと似た、入れ子にできる条件ブロックを実現します。 条件式は\setと同じで、非ゼロ値は真と解釈されます。

\set varname expression

varname変数をexpressionから計算された値に設定します。 式(expression)には、NULL定数、真理値定数のTRUEFALSE5432のような整数の定数、3.14159のような倍精度実数の定数、変数を参照する :variablename、通常のSQLの優先度と結合規則での演算子関数呼び出し、SQLのCASE一般条件式および括弧を含むことができます。

関数と大部分の演算子はNULL入力にNULLを返します。

条件の用途では非ゼロの数値はTRUE、ゼロ数値とNULLFALSEです。

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 ... ]

commandシェルコマンドを指定のargumentで実行した結果をvarname変数に設定します。 このコマンドは標準出力を通して整数値を返さなければなりません。

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

組み込み演算子

表 242に載っている算術、ビットごと、比較、論理の演算子はpgbenchに組み込まれていて、\setの式で使用することができます。

表242 pgbenchの演算子(優先度が低い順)

演算子説明結果
OR論理or5 or 0TRUE
AND論理and3 and 0FALSE
NOT論理notnot falseTRUE
IS [NOT] (NULL|TRUE|FALSE)値のテスト1 is nullFALSE
ISNULL|NOTNULLNULLテスト1 notnullTRUE
=等価5 = 4FALSE
<>不等5 <> 4TRUE
!=不等5 != 5FALSE
<より小さい5 < 4FALSE
<=以下5 <= 4FALSE
>より大きい5 > 4TRUE
>=以上5 >= 4TRUE
|整数のビット毎OR1 | 23
#整数のビット毎XOR1 # 32
&整数のビット毎AND1 & 31
~整数のビット毎NOT~ 1-2
<<整数のビット毎の左シフト1 << 24
>>整数のビット毎の右シフト8 >> 22
+加算5 + 49
-減算3 - 2.01.0
*乗算5 * 420
/除算(整数切捨て)5 / 31
%剰余3 % 21
-反転- 2.0-2.0

組み込み関数

表 243に示す関数はpgbenchに組み込まれており、\setに現れる式の中で使うことができます。

表243 pgbenchの関数

関数戻り型説明結果
abs(a)aと同じ絶対値abs(-17)17
debug(a)aと同じastderrに出力し、aを返すdebug(5432.1)5432.1
double(i)double倍精度実数にキャストするdouble(5432)5432.0
exp(x)double指数exp(1.0)2.718281828459045
greatest(a [, ... ] )aのどれかがdoubleならdouble、そうでなければinteger引数の中で最大の値greatest(5, 4, 3, 2)5
hash(a [, seed ] )integerhash_murmur2()の別名hash(10, 5432)-5817877081768721676
hash_fnv1a(a [, seed ] )integerFNV-1a hashhash_fnv1a(10, 5432)-7793829335365542153
hash_murmur2(a [, seed ] )integerMurmurHash2 hashhash_murmur2(10, 5432)-5817877081768721676
int(x)integer整数にキャストするint(5.4 + 3.8)9
least(a [, ... ] )aのどれかがdoubleならdouble、そうでなければinteger引数の中で最小の値least(5, 4, 3, 2.1)2.1
ln(x)double自然対数ln(2.718281828459045)1.0
mod(i, j)integer剰余mod(54, 32)22
pi()double定数PI(円周率)の値pi()3.14159265358979323846
pow(x, y), power(x, y)doubleexponentiationpow(2.0, 10), power(2.0, 10)1024.0
random(lb, ub)integer[lb, ub]内の一様分布の整数の乱数random(1, 10)110の間の整数
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)110の間の整数
random_zipfian(lb, ub, parameter)integer[lb, ub]内のジップ分布のランダム整数、後述random_zipfian(1, 10, 1.5)110の間の整数
sqrt(x)double平方根sqrt(2.0)1.414213562

random関数は一様分布を使って値を生成します。 つまり、すべての値は指定された範囲内で同じ確率で発生します。 random_exponentialrandom_gaussian、および、random_zipfian関数は追加の倍精度実数のパラメータを必要とし、それによって分布の正確な形が決まります。

  • 指数分布では、parameterが分布を制御します。 急速に減少する指数分布をparameterで切り捨て、境界範囲内の整数に射影します。 正確には、以下の式に従います。


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

    これにより、minmaxの間(両端を含む)の間の値if(i) - f(i + 1)の確率で生成されます。

    直感的には、parameterが大きければ、minに近い値が発生する確率が高くなり、maxに近い値が発生する確率が低くなります。 parameterが0に近ければ、発生の分布はより平ら(より一様)になります。 大雑把に分布を近似すると、minに近い最頻の1%の範囲の値は、parameter%の割合で発生します。 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)

    とすると、minmaxの間(両端を含む)の値iが発生する確率はf(i + 0.5) - f(i - 0.5)になります。 直感的には、parameterが大きくなれば、間隔の中間に近い値になる確率が高く、また、minmaxの境界に近い値になる確率は低くなります。 約67%の値は、中間の1.0 / parameterの範囲、つまり平均値から0.5 / parameterの範囲から、また95%は中間の2.0 / parameterの範囲、つまり平均値から1.0 / parameterの範囲に発生します。 例えばparameterが4.0なら、67%の値は間隔の中間の4分の1(1.0/4.0)から(つまり3.0 / 8.0から5.0 / 8.0まで)、95%は間隔の中間の半分(2.0 / 4.0)から(2番目と3番目の四分位)から発生します。 ボックス=ミュラーの変換のパフォーマンスのため、最小のparameterは2.0です。

  • random_zipfianは近似範囲のジフ分布を生成します。 (0, 1)のparameterに対して、 「Quickly Generating Billion-Record Synthetic Databases」Jim Gray(SIGMOD 1994)から近似アルゴリズムがとられています。 (0, 1000)のparameterに対しては、 「Non-Uniform Random Variate Generation」Luc Devroye(Springer 1986, p. 550-551)に基づく棄却法が用いられます。 パラメータの値が1.0のときには分布は定義されません。 パラメータ値が1.0を超えて1.0に近く小さい範囲に対しての関数の性能は劣ります。

    parameterはどれほど歪んだ分布かを定義します。 より大きいparameterほど、より高頻度に区間の始点に近い値が描かれます。 より0に近いparameterは、より平坦(より均一)な出力分布です。 範囲が1から始まるとして、kを描く確率とk+1を描く確率の比が((k+1)/k)**parameterという分布になります。 例えば、random_zipfian(1, ..., 2.5)は、値12の約(2/1)**2.5 = 5.66倍高い頻度で生成し、値23の約(3/2)**2.5 = 2.76倍高い頻度で生成し、以下同様に続きます。

ハッシュ関数hashhash_murmur2およびhash_fnv1aは入力値とオプションシードパラメータを受け付けます。 シードが与えられなかった場合、:default_seedの値が使われます。これは、コマンドライン-Dオプションで設定されない限りランダムに初期化されたものです。 ハッシュ関数はrandom_zipfianrandom_exponentialなどのランダム関数の分布を散らすのに使用できます。 例えば、以下のpgbenchスクリプトは起こりえる現実世界のソーシャルメディアとブログプラットフォームに対する僅かなアカウントしか過大な負荷をかけないという典型的な負荷をシミュレートします。

\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 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;

このスクリプトにより、トランザクションを繰り返す度に異なる、ランダムに選ばれた行を参照することができます。 (この例はまた、各クライアントセッションがなぜ独自の変数を持つことが重要なのかも表しています。 これがないと、異なる行を独立して参照することができないのです。)

トランザクション毎のログ処理

--aggregate-intervalオプションなしで)-lオプションを使用すると、pgbenchは各トランザクションに関する情報をログファイルに書き出します。 ログファイルの名前はprefix.nnnで、prefixのデフォルトはpgbench_lognnnpgbenchプロセスの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の両方のオプションを使用した時は、スキップされたトランザクションのtimeskippedとして表示されます。

単一クライアントでの実行で生成されたログファイルの一部を示します。

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ミリ秒)が5ミリ秒を越えており、遅れています。 次の2つのトランザクションは、開始する前にすでに遅れてしまっているため、スキップされています。

大量のトランザクションを処理することができるハードウェアで長時間試験を実行する場合、ログファイルは非常に大きくなる可能性があります。 --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_of_transactionsはインターバル内のトランザクション数です。 latency_sumはインターバル内のトランザクションレイテンシの総和です。 sum_latency_2はインターバル内のトランザクションレイテンシの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 = 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 pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;
        0.454  SELECT abalance FROM pgbench_accounts WHERE aid = :aid;
        5.528  UPDATE pgbench_tellers SET tbalance = tbalance + :delta WHERE tid = :tid;
        7.335  UPDATE pgbench_branches SET bbalance = bbalance + :delta WHERE bid = :bid;
        0.371  INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP);
        1.212  END;

複数のスクリプトファイルが定義された場合、平均値はそれぞれのスクリプトファイル毎に分けて報告されます。

ステートメント毎のレイテンシを計算するために必要となる、追加のタイミング情報を収集することは、オーバーヘッドが加わることに注意してください。 これは平均実行速度を遅くし、計測TPSを小さくするでしょう。 低下量はプラットフォームとハードウェアに依存して著しく変わります。 レイテンシの報告を有効にする、有効にしないで平均TPS値を比較することは、タイミング・オーバーヘッドが顕著かどうかを測定するには良い方法です。

優れた実践

まったく無意味な数値を生み出すようにpgbenchを使用することは非常に簡単です。 以下に有意な結果を生み出す手助けとなるガイドラインをいくつか示します。

まず第一に、数秒で終わる試験を決して信用しないでください。 -tまたは-Tオプションを使って、雑音を取り除くために、少なくとも数分試験にかかるようにしてください。 再現可能な数値を得るために数時間必要になる場合もあります。 数回試験を繰り返し、数値が再現できるかどうか確認することを勧めます。

デフォルトのTPC-Bのような試験シナリオでは、初期倍率(-s)を試験予定のクライアント数(-c)の最大値と同程度にしなければなりません。 pgbench_branchesテーブルには-s行しかありません。 また、全トランザクションはその内の1つを更新しようとします。 ですので、-c値を-sより大きくすると、他のトランザクションを待機するためにブロックされるトランザクションが多くなることは間違いありません。

デフォルトの試験シナリオはまた、テーブルを初期化してからの経過時間に非常に敏感です。 テーブル内の不要行や不要空間の累積により結果が変わります。 結果を理解するためには、更新された行数とバキューム時期を把握する必要があります。 自動バキュームが有効な場合、性能を測定する上で結果は予測できないほど変わる可能性があります。

pgbenchの制限は、多くのクライアントセッションを試験しようとする際にpgbench自身がボトルネックになる可能性があることです。 これは、データベースサーバとは別のマシンでpgbenchを実行することで緩和させることが可能です。 しかし、多少のネットワーク遅延が重要です。 同一データベースサーバに対し複数のクライアントマシンから複数のpgbenchインスタンスを同時に実行することが有用かもしれません。

セキュリティ

安全なスキーマの利用パターンを適用していないデータベースに信頼できないユーザがアクセス可能な場合、そのデータベースでpgbenchを実行しないでください。 pgbenchは修飾していない名前を使っており、またサーチパスを操作していません。