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 の実装をみてください。