使用原則
- 庫自帶連接池,使用方不需自行實現。*sql.DB 線程安全,開箱即用,屏弊了底層創建連接的實現
- Open 只是創建類,調用一次即可,使用前需要 Ping 確保連接正常。
- 一定要設置連接池的兩個參數 MaxIdle, MaxOpen,否則在極端情況會把 DB 連接打滿(未加索引,大事務阻塞)。可選 MaxLifetime,需咨詢 DBA,一般 DB 默認8小時,無需設置,如果很短要視情況而定
- 事務會占用一個連接,盡可能減小事務耗時,打散大事務,否則會將 DB 連接數打滿
- prepare 會占用一個連接,每次使用完后,一定要 close ,否則同樣會將連接數打滿
- DSN 需要指定時區和對時間字段的支持,否則會出現時間提前8小時的問題
- Query, Prepare, Exec 無需業務層重試,底層已經實現
下一篇源碼走讀會詳細說明原因
連接創建示例
type MySQLClient struct {
Host string
MaxIdle int
MaxOpen int
User string
Pwd string
DB string
Port int
pool *sql.DB
}
func (mc *MySQLClient) Init() (err error) {
// 構建 DSN 時尤其注意 loc 和 parseTime 正確設置
// 東八區,允許解析時間字段
uri := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8&loc=%s&parseTime=true",
mc.User,
mc.Pwd,
mc.Host,
mc.Port,
mc.DB,
url.QueryEscape("Asia/Shanghai"))
// Open 全局一個實例只需調用一次
mc.pool, err = sql.Open("mysql", uri)
if err != nil {
return err
}
//使用前 Ping, 確保 DB 連接正常
err = mc.pool.Ping()
if err != nil {
return err
}
// 設置最大連接數,一定要設置 MaxOpen
mc.pool.SetMaxIdleConns(mc.MaxIdle)
mc.pool.SetMaxOpenConns(mc.MaxOpen)
return nil
}
數據庫查詢示例
func testQuery(beginTime, endTime, code string) ([]*OrderInfo, error) {
db := iowrapper.MySQLClient.GetMySQL()
err := db.Ping()
if err != nil {
return nil, err
}
var rows *sql.Rows
// sql 可以用占位符,涉及業務分表提前生成
rows, err = db.Query(getSqlByCode(code, beginTime, endTime))
if err != nil {
return nil, err
}
OrderInfos := make([]*OrderInfo, 0, 10)
for rows.Next() {
oi := &OrderInfo{}
var createTime time.Time
err := rows.Scan(&oi.OrderId, &createTime, &oi.StartingLng, &oi.StartingLat, &oi.DestLng, &oi.DestLat)
if err != nil {
continue
}
oi.CreateTime = createTime.Unix()
OrderInfos = append(OrderInfos, oi)
}
return OrderInfos, nil
}
數據庫更新示例
func testExec() error {
sql := "update test.table1 set _create_time=now() where id=?"
res, err := db.Exec(sql, 7988161482)
if err != nil {
fmt.Println("exec error ", err.Error())
return err
}
fmt.Println(res.LastInsertId())
fmt.Println(res.RowsAffected())
return nil
}
數據庫prepare示例
func testStmt() error {
//占位符sql
sql := "update test.table1 set _create_time=now() where id=?"
stmt, err := db.Prepare(sql)
if err != nil {
fmt.Println("stmt error ", err.Error())
return err
}
// 一定要關閉,很重要
defer stmt.Close()
// 可以批量,示例只有一個
_, err = stmt.Exec(7988161474)
if err != nil {
fmt.Println("stmt exec error ", err.Error())
return err
}
return nil
}