MySQL用のベンチマークツールSuper Smackの使い方について書きます。
これは何?
かなり古くからあるベンチマークツールです。mysqlslapのようにシンプルなクエリを並列で大量に投げるような負荷を掛けられます。mysqlslapとは異なって、自分でテストシナリオを定義するのが特徴です。
もともとJeremy D. Zawodnyという方が開発していたようなのですが
http://jeremy.zawodny.com/mysql/super-smack/
NOTICE
I have not had the time to maintain or support super-smack in recent months. Tony Bourke has taken over as the maintainer. The new home for super-smack is http://vegan.net/tony/supersmack/
とのことで、http://vegan.net/tony/supersmack/ にいってみるとすでにページは存在せず、以下のフォークが一応最新のソースかなぁ…という感じでした。
が、 既存のバグがあってそのままではCentOS6/64bit動かないので、さらにフォークして修正。
さらに、rpmを作成しました。
使い方
Employees Sample Databaseをターゲットとしたテストで使い方を説明します。
ディクショナリソースの作成
上記、rpmをインストールすると/var/smack-data/
というディレクトリが作成されます。ディクショナリの元となるデータをそこに配置します。
ruby -e 'puts (10001..499999).sort_by { rand }.join("\n")' > /var/smack-data/emp_no.dat
gen-data -n 500000 -f %14-14s > /var/smack-data/name.dat
gen-dataはSuper Smackに付属しているデータ生成ツールです。
テストケースの作成
テストケースは以下のような書き方になります。
dictionary "emp_no"
{
type "rand";
source_type "file";
source "emp_no.dat";
delim ",";
file_size_equiv "0";
}
dictionary "name"
{
type "rand";
source_type "file";
source "name.dat";
delim ",";
file_size_equiv "0";
}
query "select_by_emp_no"
{
query "select * from employees where emp_no = '$emp_no'"; # クォートで囲まないとパースされない
type "select_index";
has_result_set "y";
parsed "y";
}
query "update_by_emp_no"
{
query "update employees set first_name = '$name' where emp_no = '$emp_no'";
type "update_index";
has_result_set "y";
parsed "y";
}
client "smacker1"
{
user "scott";
pass "tiger";
host "127.0.0.1";
db "employees";
port "3306";
query_barrel "2 select_by_emp_no 1 update_by_emp_no";
}
main
{
smacker1.init();
smacker1.set_num_rounds($2); # 実行回数:=引数2
smacker1.create_threads($1); # スレッド数:=引数1
smacker1.connect();
smacker1.unload_query_barrel();
smacker1.collect_threads();
smacker1.disconnect();
}
まず、SQLに埋め込む値用のディクショナリを定義します。
dictionary "emp_no"
{
type "rand";
source_type "file";
source "emp_no.dat";
delim ",";
file_size_equiv "0";
}
dictionary "name"
{
type "rand";
source_type "file";
source "name.dat";
delim ",";
file_size_equiv "0";
}
そして、参照と更新のクエリを定義。
query "select_by_emp_no"
{
query "select * from employees where emp_no = '$emp_no'"; # クォートで囲まないとパースされない
type "select_index";
has_result_set "y";
parsed "y";
}
query "update_by_emp_no"
{
query "update employees set first_name = '$name' where emp_no = '$emp_no'";
type "update_index";
has_result_set "y";
parsed "y";
}
parsed "y";
とすると、$変数にディクショナリが生成した文字列が埋め込まれます。変数はシングルクォートで囲われていないとパースされないので注意してください。
クエリを実行するクライアントを定義します。
client "smacker1"
{
user "scott";
pass "tiger";
host "127.0.0.1";
db "employees";
port "3306";
query_barrel "2 select_by_emp_no 1 update_by_emp_no";
}
query_barrel
は実行回数で、selectを2回・updateを1回、各クライアントで実行します。
最後に、テストの動作を定義します。
main
{
smacker1.init();
smacker1.set_num_rounds($2); # 実行回数:=引数2
smacker1.create_threads($1); # スレッド数:=引数1
smacker1.connect();
smacker1.unload_query_barrel();
smacker1.collect_threads();
smacker1.disconnect();
}
実行してみる
super-smack
コマンドでテストを実行。
super-smack test-file 10 1000 # 10スレッドで1000回実行
対象のDBには以下のようなクエリが流れます。
select * from employees where emp_no='208643'
select * from employees where emp_no='141638'
update employees set first_name='whddtxlpcswrkb' where emp_no = '190331'
select * from employees where emp_no='391207'
select * from employees where emp_no='453798'
update employees set first_name='udmhjefpsxbppr' where emp_no = '169886'
テストが終わると結果出力。
Query Barrel Report for client smacker1
connect: max=36ms min=3ms avg= 18ms from 10 clients
Query_type num_queries max_time min_time q_per_s
select_index 20000 1 0 4168.93
update_index 10000 3 0 2084.47