golangでStatementのOpen/Closeについて軽く調べた

目的

StatementはいつOpen/Closeすべきで、あるいはStatementをsql.Db.Prepareで生成せずにQueryを実行したほうが早かったりするのか、そのへんのことを計測してみようと思ったんだ。

検証コード

以下の様な感じ。もしかしたら変かもしれない。
ほんとはdeferでcloseする、とかそういうのは割愛

ddl

MySQLでやりました

CREATE TABLE `test`.`testtable` (
 `id` INT NOT NULL AUTO_INCREMENT,
 `nullable` VARCHAR(45) NULL,
 PRIMARY KEY (`id`));

ソースコード

package main

import (
    "database/sql"
    "log"
    "time"
    _ "github.com/go-sql-driver/mysql"
    "flag"
)

const QUERY  = `
    SELECT t.id, t.nullable FROM test.testtable t WHERE nullable is not null AND nullable like ?
`

func main() {
    var c = flag.Int("c", 1234, "help message for f")
    flag.Parse()
    db, err := sql.Open("mysql", "root:password@tcp(localhost:3306)/test")
    if err != nil {
        log.Fatalln(err)
    }
    defer db.Close()

    Exec(db, *c, "%")
}

func Exec(db *sql.DB, counts int, words string) {

    log.Printf("%d 回実行 開始", counts)

    start := time.Now();
    for i := 0; i < counts ; i++ {

        rows, err := db.Query(QUERY, words)
        if err != nil {
            log.Panicln(err)
        }

        defer rows.Close()
        for rows.Next() {
            var id int
            var nullable sql.NullString
            rows.Scan(&id, &nullable)
        }
    }
    end := time.Now();
    log.Printf("db.Query %f秒\n",(end.Sub(start)).Seconds())

    start = time.Now();
    for i := 0; i < counts ; i++ {

        var rows *sql.Rows

        stmt, err := db.Prepare(QUERY)
        if err != nil {
            log.Panicln(err)
        }

        rows, err = stmt.Query(words)
        if err != nil {
            log.Panicln(err)
        }

        for rows.Next() {
            var id int
            var nullable sql.NullString
            rows.Scan(&id, &nullable)
        }
        rows.Close()
        stmt.Close()
    }
    end = time.Now();
    log.Printf("Stmt.Queryを都度Open/Close %f秒\n",(end.Sub(start)).Seconds())


    start = time.Now();

    stmt, err := db.Prepare(QUERY)
    if err != nil {
        log.Panicln(err)
    }
    for i := 0; i < counts ; i++ {

        var rows *sql.Rows

        rows, err = stmt.Query(words)
        if err != nil {
            log.Panicln(err)
        }

        for rows.Next() {
            var id int
            var nullable sql.NullString
            rows.Scan(&id, &nullable)
        }
        rows.Close()
    }
    stmt.Close()
    end = time.Now();
    log.Printf("Stmt.Queryを実行中は保持 %f秒\n",(end.Sub(start)).Seconds())
}

結果

$ go run main.go -c 1
2016/06/04 22:54:38 1 回実行 開始
2016/06/04 22:54:38 db.Query 0.002638秒
2016/06/04 22:54:38 Stmt.Queryを都度Open/Close 0.000355秒
2016/06/04 22:54:38 Stmt.Queryを実行中は保持 0.000329秒
$ go run main.go -c 10
2016/06/04 22:54:56 10 回実行 開始
2016/06/04 22:54:56 db.Query 0.004719秒
2016/06/04 22:54:56 Stmt.Queryを都度Open/Close 0.002690秒
2016/06/04 22:54:56 Stmt.Queryを実行中は保持 0.001427秒
$ go run main.go -c 10000
2016/06/04 22:55:04 10000 回実行 開始
2016/06/04 22:55:07 db.Query 2.288819秒
2016/06/04 22:55:09 Stmt.Queryを都度Open/Close 2.804229秒
2016/06/04 22:55:11 Stmt.Queryを実行中は保持 1.767292秒

1万回を超えてくると、Stmtを毎回閉じるよりも、普通にQuery投げたほうが早いとかそういうレベルになってくるのがわかった。
一回の実行時にも少し差があるのは誤差か。

このブログの人気の投稿

2016年にgoを使ったのでまとめ

採用とは何か

vue-bulmaと太陽と埃の中で