Database sql
-
Upload
- -
Category
Engineering
-
view
3.885 -
download
0
Transcript of Database sql
database/sqlとコネクションTalos208
TL;DRdatabase/sqlには意図した使用法があるdatabase/sqlにはDBとの接続数を設定するAPIがいくつかある
ライブラリのソースも読んでみよう
Who am I株式会社スプラウト R&D Tech Lead組込み→Windowsゲーム→SIer(Web系BtoB)→セキュリティと流れてきたので、無駄に色々できる
Twitter : Talos208GitHub : https://github.com/Talos208
database/sqlgo標準のSQLライブラリ
使用法
db, err := sql.Open("driver name", "dsn")if err != nil { return nil, err}rows, err := db.Query(...)
db.Close()
……ってしたくなりますよね?
Db.Close()のドキュメントこんな記述が……
It is rare to Close a DB , as the DB handle is meant to be long‑lived and shared between many goroutines.
訳)
DBをCloseすることは、ほとんどない。なぜなら、DBハンドルは長期間生存して複数のgoroutine間で共有することを意図しているから。
ソースも見てみよう
type DB struct { driver driver.Driver dsn string numClosed uint64
mu sync.Mutex freeConn []*driverConn connRequests []chan connRequest numOpen int
// (後略)}
ソースも見てみよう(2)
func (db *DB) conn(strategy connReuseStrategy) (*driverConn, error) { // (略) numFree := len(db.freeConn) if strategy == cachedOrNewConn && numFree > 0 { conn := db.freeConn[0] copy(db.freeConn, db.freeConn[1:]) db.freeConn = db.freeConn[:numFree-1] conn.inUse = true // (略) return conn, nil // freeConnの先頭を再利用 } // (略) db.numOpen++ // optimistically db.mu.Unlock() ci, err := db.driver.Open(db.dsn) // ここでOpen // (略)}
ソースも見てみよう(3)
func (db *DB) Close() error { // (略) var err error fns := make([]func() error, 0, len(db.freeConn)) for _, dc := range db.freeConn { fns = append(fns, dc.closeDBLocked()) } db.freeConn = nil // (略) for _, fn := range fns { err1 := fn() if err1 != nil { err = err1 } } return err}
ソースから読み取れたこと
sql.Open() で返ってくるDB構造体は、コネクションプールを持
っている
接続をするときには、まずプールの空きコネクションを使用する
DB.Exec() / DB.Query() などから自動的に DB.conn() が呼ばれる。あれば空きコネクションが利用され、無ければ新規コ
ネクションが取得される
DB.Close() は、全ての空きコネクションを切断する
つまり
都度Open()/Close()すると、その度にコネクション接続/切断のコス
トがかかる
DBへのログインのコストもかかる
並行して複数のDB構造体を持つと、余計なメモリを消費する
これを避けるために
sql.Open() は起動時に1回だけ呼ぶ
DB.Close() は最後に1回呼ぶ
接続数の制限
func (*DB) SetMaxIdleConns
func (db *DB) SetMaxIdleConns(n int)
空きコネクションの最大数を指定。0だとコネクションプーリング
を行わない
func (*DB) SetMaxOpenConns
func (db *DB) SetMaxOpenConns(n int)
(空きも含めた)全コネクションの最大数。0だと上限なし
最大値しか設定できない。しばらく経つと、最大数に張り付いたままに
なる
What happen?症状
sql.Open()は出来るDB.Ping()も成功する
DB.Query()を実行するとコネクションエラー
????
Condition
サーバとDBとの間に、MariDB Maxscaleを使用していた
MaxscaleMySQL(MariaDB)向けのL7ロードバランサ
クエリ内容を見て SELECT はリードレプリカに、 UPDATE はマスターにとか出来る
Cause
1. サーバ <‑> Maxscaleのコネクションは存在Maxscaleとはつながるので、DB.Ping()は成功
2. Maxscale <‑> MySQLの接続がタイムアウトしていたサーバにタイムアウトが通知されない
3. クエリ発行時にコネクションエラー
じつはMaxscaleがエラーを出していた
SetConnMaxLifetime()Go1.6から新しいAPIが導入された
func (*DB) SetConnMaxLifetime
func (db *DB) SetConnMaxLifetime(d time.Duration)
一定時間以上経った接続は再利用しない
これを使うだけのためにGo1.4‑>Go1.6に移行
解決
1. SetConnMaxLifetime() の設定により、サーバ <‑> Maxscale間のコネクションのほうが先に切れる
2. DB.Ping()の時点で、コネクションが再接続される
3. 正常にクエリ発行