go-sysbench
go-sysbench は Go 言語で書かれた、sysbench のクローンです。複雑なカスタムシナリオを sysbench より簡単に作れることを目的として作りました。
sysbench はシンプルで、非常に使いやすいベンチマークツールで、長年愛用してきました。Lua 言語でカスタムシナリオを書くこともできます。
ただ、Lua 言語で複雑なカスタムシナリオを書くのは難しいと感じてました(Lua 言語に慣れていないということもあると思います)。
そこで、使い勝手や出力は sysbench のまま、より柔軟性の高い Go言語で sysbench のクローンを作りました。
$ go-sysbench --help
Usage:
go-sysbench [options]... [oltp_read_only|oltp_read_write] [prepare|run]
Application Options:
--threads= number of threads to use (default: 1)
--events= limit for total number of events (default: 0)
--time= limit for total execution time in seconds (default: 10)
--report-interval= periodically report intermediate statistics with a specified interval in seconds. 0 disables intermediate reports (default: 0)
--histogram=[on|off] print latency histogram in report (default: off)
--percentile= percentile to calculate in latency statistics (1-100) (default: 95)
--tables= number of tables (default: 1)
--table_size= number of rows per table (default: 10000)
--table-size= alias of --table_size
--db-driver=[mysql|pgsql|spanner] specifies database driver to use (default: mysql)
--db-ps-mode=[auto|disable] prepared statements usage mode (default: auto)
--version show version
MySQL:
--mysql-host= MySQL server host (default: localhost)
--mysql-port= MySQL server port (default: 3306)
--mysql-user= MySQL user (default: sbtest)
--mysql-password= MySQL password [$MYSQL_PWD]
--mysql-db= MySQL database name (default: sbtest)
--mysql-ssl=[on|off] use SSL connections (default: off)
--mysql-ignore-errors= list of errors to ignore, or "all" (default: 1213,1020,1205)
PostgreSQL:
--pgsql-host= PostgreSQL server host (default: localhost)
--pgsql-port= PostgreSQL server port (default: 5432)
--pgsql-user= PostgreSQL user (default: sbtest)
--pgsql-password= PostgreSQL password [$PGPASSWORD]
--pgsql-db= PostgreSQL database name (default: sbtest)
--pgsql-ssl=[on|off] use SSL connections (default: off)
--pgsql-ignore-errors= list of errors to ignore, or "all" (default: 40P01,23505,40001)
Spanner:
--spanner-project= Spanner Google Cloud project name
--spanner-instance= Spanner instance id
--spanner-db= Spanner database name (default: sbtest)
Help Options:
-h, --help Show this help message
今回修正した内容
go-sysbench の初期実装は、シナリオとベンチマーカー本体のロジックが十分整理できておらず、少しカスタムしにくい実装でした。
今回、インターフェイスを整理して、完全に独立したライブラリとして使えるようにしました。
使い方
github.com/samitani/go-sysbench を import して、計測したい処理だけ記述すればOKです。
Init()でDBの接続など、準備を行い、Event()関数に計測したい処理を記述します。Event()関数は Read/Write/Others/IgnoreError 数を返す必要があります、これらの数は集計され、ベンチマーク結果に出力されます。Event()関数が1回実行されると、1回トランザクションが成功したとみなされます(結果のtransactionsがインクリメントされます)。
package main
import (
"context"
"fmt"
"os"
"database/sql"
_ "github.com/go-sql-driver/mysql"
"github.com/samitani/go-sysbench"
)
type CustomBenchmark struct {
db *sql.DB
}
// when Runner.Prepare(), Runner.Run() is called, Init() is called once in advance.
func (b *CustomBenchmark) Init(ctx context.Context) error {
db, err := sql.Open("mysql", "root:password@/my_database")
if err != nil {
return err
}
err = db.Ping()
if err != nil {
return err
}
b.db = db
return nil
}
// when Runner.Prepare(), Runner.Run() is called, Done() is called once at the end.
func (b *CustomBenchmark) Done() error {
b.db.Close()
return nil
}
// when Runner.Prepare() is called, Prepare() is called once.
func (b *CustomBenchmark) Prepare(ctx context.Context) error {
// nothing to do
return nil
}
// when Runner.Run() is called, PreEvent() is called once before event loop.
func (b *CustomBenchmark) PreEvent(ctx context.Context) error {
// nothing to do
return nil
}
// when Runner.Run() is called, Event() is called in a loop
func (b *CustomBenchmark) Event(ctx context.Context) (numReads, numWrites, numOthers, numIgnoredErros uint64, err error) {
var readCount uint64 = 0
var writeCount uint64 = 0
// something you want to measure
for i := 0; i < 5; i++ {
rows, err := b.db.QueryContext(ctx, "SELECT NOW()")
if err != nil {
return readCount, 0, 0, 0, err
}
defer rows.Close()
// fetch rows from server
for rows.Next() {
}
readCount = readCount + 1
}
return readCount, writeCount, 0, 0, nil
}
func main() {
bench := &CustomBenchmark{}
r := sysbench.NewRunner(&sysbench.RunnerOpts{
Threads: 10,
Events: 0,
Time: 60,
ReportInterval: 1,
Histogram: "on",
Percentile: 95,
}, bench)
if err := r.Run(); err != nil {
fmt.Println(err)
os.Exit(1)
}
}
そして、結果は見慣れた sysbench の形式で出力されます。
$ ./main
Running the test with following options:
Number of threads: 10
Report intermediate results every 1 second(s)
[ 1s ] thds: 10 tps: 5072.00 qps: 25360.00 (r/w/o: 25360.00/0.00/0.00) lat (ms,95%): 2.91 err/s 0.00 reconn/s: N/A
[ 2s ] thds: 10 tps: 5162.00 qps: 25810.00 (r/w/o: 25810.00/0.00/0.00) lat (ms,95%): 2.91 err/s 0.00 reconn/s: N/A
[ 3s ] thds: 10 tps: 5180.00 qps: 25900.00 (r/w/o: 25900.00/0.00/0.00) lat (ms,95%): 2.86 err/s 0.00 reconn/s: N/A
[ 4s ] thds: 10 tps: 5387.00 qps: 26935.00 (r/w/o: 26935.00/0.00/0.00) lat (ms,95%): 2.76 err/s 0.00 reconn/s: N/A
[ 5s ] thds: 10 tps: 5340.00 qps: 26700.00 (r/w/o: 26700.00/0.00/0.00) lat (ms,95%): 2.86 err/s 0.00 reconn/s: N/A
[ 6s ] thds: 10 tps: 5199.00 qps: 25995.00 (r/w/o: 25995.00/0.00/0.00) lat (ms,95%): 2.86 err/s 0.00 reconn/s: N/A
[ 7s ] thds: 10 tps: 5422.00 qps: 27110.00 (r/w/o: 27110.00/0.00/0.00) lat (ms,95%): 2.76 err/s 0.00 reconn/s: N/A
[ 8s ] thds: 10 tps: 5193.00 qps: 25965.00 (r/w/o: 25965.00/0.00/0.00) lat (ms,95%): 2.86 err/s 0.00 reconn/s: N/A
[ 9s ] thds: 10 tps: 5283.00 qps: 26415.00 (r/w/o: 26415.00/0.00/0.00) lat (ms,95%): 2.81 err/s 0.00 reconn/s: N/A
<snip>
Latency histogram (values are in milliseconds)
value ------------- distribution ------------- count
0.127 | 1
0.312 | 1
0.318 | 1
0.448 | 1
0.546 | 1
0.576 | 1
SQL statistics:
queries performed:
read: 1572640
write: 0
other: 0
total: 1572640
transactions: 314535 (5242.20 per sec.)
queries: 1572640 (26210.44 per sec.)
ignored errors: 0 (0.00 per sec.)
reconnects: N/A (N/A per sec.)
General statistics:
total time: 60.0005s
total number of events: 314535
Latency (ms):
min: 0.13
avg: 1.91
max: 8.12
95th percentile: 2.86
sum: 599624.51
Threads fairness (Event distribution by threads):
events (avg/stddev): 31453.5000/69.05
execution time (avg/stddev): 59.9625/0.00
より、実用的な例は oltp_read_only, oltp_read_write の実装をみてください。
![MySQL運用・管理[実践]入門 〜安全かつ高速にデータを扱う内部構造・動作原理を学ぶ MySQL運用・管理[実践]入門 〜安全かつ高速にデータを扱う内部構造・動作原理を学ぶ](https://m.media-amazon.com/images/I/51+wlcyvx4L._SL500_.jpg)
