caching session bounded sql.Stmt to increase performance

This commit is contained in:
Nash Tsai 2014-01-14 18:53:00 +08:00
parent 52eef13832
commit 60db598114
1 changed files with 49 additions and 28 deletions

View File

@ -5,6 +5,7 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"hash/crc32"
"reflect" "reflect"
"strconv" "strconv"
"strings" "strings"
@ -33,6 +34,8 @@ type Session struct {
beforeClosures []func(interface{}) beforeClosures []func(interface{})
afterClosures []func(interface{}) afterClosures []func(interface{})
stmtCache map[uint32]*sql.Stmt //key: hash.Hash32 of (queryStr, len(queryStr))
} }
// Method Init reset the session as the init status. // Method Init reset the session as the init status.
@ -53,14 +56,17 @@ func (session *Session) Init() {
// Method Close release the connection from pool // Method Close release the connection from pool
func (session *Session) Close() { func (session *Session) Close() {
defer func() { for _, v := range session.stmtCache {
v.Close()
}
if session.Db != nil { if session.Db != nil {
session.Engine.Pool.ReleaseDB(session.Engine, session.Db) session.Engine.Pool.ReleaseDB(session.Engine, session.Db)
session.Db = nil session.Db = nil
session.Tx = nil session.Tx = nil
session.stmtCache = nil
session.Init() session.Init()
} }
}()
} }
// Method Sql provides raw sql input parameter. When you have a complex SQL statement // Method Sql provides raw sql input parameter. When you have a complex SQL statement
@ -256,6 +262,7 @@ func (session *Session) newDb() error {
return err return err
} }
session.Db = db session.Db = db
session.stmtCache = make(map[uint32]*sql.Stmt, 0)
} }
return nil return nil
} }
@ -394,13 +401,13 @@ func (session *Session) scanMapIntoStruct(obj interface{}, objMap map[string][]b
//Execute sql //Execute sql
func (session *Session) innerExec(sqlStr string, args ...interface{}) (sql.Result, error) { func (session *Session) innerExec(sqlStr string, args ...interface{}) (sql.Result, error) {
rs, err := session.Db.Prepare(sqlStr) stmt, err := session.doPrepare(sqlStr)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer rs.Close() //defer stmt.Close()
res, err := rs.Exec(args...) res, err := stmt.Exec(args...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -866,6 +873,21 @@ func (session *Session) Iterate(bean interface{}, fun IterFunc) error {
return nil return nil
} }
func (session *Session) doPrepare(sqlStr string) (stmt *sql.Stmt, err error) {
crc := crc32.ChecksumIEEE([]byte(sqlStr))
// TODO try hash(sqlStr+len(sqlStr))
var has bool
stmt, has = session.stmtCache[crc]
if !has {
stmt, err = session.Db.Prepare(sqlStr)
if err != nil {
return nil, err
}
session.stmtCache[crc] = stmt
}
return
}
// get retrieve one record from database, bean's non-empty fields // get retrieve one record from database, bean's non-empty fields
// will be as conditions // will be as conditions
func (session *Session) Get(bean interface{}) (bool, error) { func (session *Session) Get(bean interface{}) (bool, error) {
@ -901,11 +923,11 @@ func (session *Session) Get(bean interface{}) (bool, error) {
var rawRows *sql.Rows var rawRows *sql.Rows
session.queryPreprocess(&sqlStr, args...) session.queryPreprocess(&sqlStr, args...)
if session.IsAutoCommit { if session.IsAutoCommit {
stmt, err := session.Db.Prepare(sqlStr) stmt, err := session.doPrepare(sqlStr)
if err != nil { if err != nil {
return false, err return false, err
} }
defer stmt.Close() // defer stmt.Close() // !nashtsai! don't close due to stmt is cached and bounded to this session
rawRows, err = stmt.Query(args...) rawRows, err = stmt.Query(args...)
} else { } else {
rawRows, err = session.Tx.Query(sqlStr, args...) rawRows, err = session.Tx.Query(sqlStr, args...)
@ -1070,11 +1092,10 @@ func (session *Session) Find(rowsSlicePtr interface{}, condiBean ...interface{})
// defer rawRows.Close() // defer rawRows.Close()
if session.IsAutoCommit { if session.IsAutoCommit {
stmt, err = session.Db.Prepare(sqlStr) stmt, err = session.doPrepare(sqlStr)
if err != nil { if err != nil {
return err return err
} }
defer stmt.Close()
rawRows, err = stmt.Query(args...) rawRows, err = stmt.Query(args...)
} else { } else {
rawRows, err = session.Tx.Query(sqlStr, args...) rawRows, err = session.Tx.Query(sqlStr, args...)
@ -1165,19 +1186,19 @@ func (session *Session) Find(rowsSlicePtr interface{}, condiBean ...interface{})
return nil return nil
} }
func (session *Session) queryRows(rawStmt **sql.Stmt, rawRows **sql.Rows, sqlStr string, args ...interface{}) error { // func (session *Session) queryRows(rawStmt **sql.Stmt, rawRows **sql.Rows, sqlStr string, args ...interface{}) error {
var err error // var err error
if session.IsAutoCommit { // if session.IsAutoCommit {
*rawStmt, err = session.Db.Prepare(sqlStr) // *rawStmt, err = session.doPrepare(sqlStr)
if err != nil { // if err != nil {
return err // return err
} // }
*rawRows, err = (*rawStmt).Query(args...) // *rawRows, err = (*rawStmt).Query(args...)
} else { // } else {
*rawRows, err = session.Tx.Query(sqlStr, args...) // *rawRows, err = session.Tx.Query(sqlStr, args...)
} // }
return err // return err
} // }
// Test if database is ok // Test if database is ok
func (session *Session) Ping() error { func (session *Session) Ping() error {