Super Smackの使い方

MySQL用のベンチマークツールSuper Smackの使い方について書きます。

# これは何？
かなり古くからあるベンチマークツールです。[mysqlslap](https://dev.mysql.com/doc/refman/5.6/ja/mysqlslap.html)のようにシンプルなクエリを並列で大量に投げるような負荷を掛けられます。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/ にいってみるとすでにページは存在せず、以下のフォークが一応最新のソースかなぁ…という感じでした。

[tmountain/Super-Smack](https://github.com/tmountain/Super-Smack)

**が**、 既存のバグがあってそのままではCentOS6/64bit動かないので、さらにフォークして修正。

[winebarrel/super-smack](https://github.com/winebarrel/super-smack)

さらに、[rpm](https://github.com/winebarrel/super-smack/releases)を作成しました。

# 使い方

[Employees Sample Database](https://dev.mysql.com/doc/employee/en/employees-installation.html)をターゲットとしたテストで使い方を説明します。

## ディクショナリソースの作成

上記、rpmをインストールすると`/var/smack-data/`というディレクトリが作成されます。ディクショナリの元となるデータをそこに配置します。

```sh
ruby -e 'puts (10001..499999).sort_by { rand }.join("\n")' > /var/smack-data/emp_no.dat
```
```bash
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); # 実行回数:=引数２
  smacker1.create_threads($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); # 実行回数:=引数２
  smacker1.create_threads($1); # スレッド数:=引数１
  smacker1.connect();
  smacker1.unload_query_barrel();
  smacker1.collect_threads();
  smacker1.disconnect();
}
```

## 実行してみる

`super-smack`コマンドでテストを実行。

```bash
super-smack test-file 10 1000 # 10スレッドで1000回実行
```

対象のDBには以下のようなクエリが流れます。

```sql
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
```

# 参考リンク
* [supersmackを使ったMysqlベンチマーク - teketeke_55の日記](http://teketeke55.hatenablog.com/entry/2013/02/21/174003)
* [MySQL Server 5.0 サイジング情報](http://h50146.www5.hp.com/products/software/oe/linux/summary/mwtech/files/mysql-50-sizing-rhel4-v15.pdf)