使用原则
- 库自带连接池,使用方不需自行实现。*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
}