go mysql 使用总结
前言
关系数据库是企业使用较多的数据库类型,因此Go集成了SQL的接口,但是该接口必须与某种关系数据库驱动结合起来使用,如下:
Package sql provides a generic interface around SQL (or SQL-like) databases.The sql package must be used in conjunction with a database driver. See https://golang.org/s/sqldrivers for a list of drivers.For more usage examples, see the wiki page at https://golang.org/s/sqlwiki.
由于Mysql是企业使用较多的开源关系数据库,所以这里主要总结一下工作中对Golang Mysql的使用。
使用
这里以go-sql-driver/mysql驱动作为例子,详细介绍工作中通常会关注的问题:
Open与Ping
func Open(driverName, dataSourceName string) (*DB, error)
Where driver specifies a database driver and dataSourceName specifies database-specific connection information such as database name and authentication credentials.
Open may just validate its arguments without creating a connection to the database. To verify that the data source name is valid, call Ping.
Most users will open a database via a driver-specific connection helper function that returns a *DB. No database drivers are included in the Go standard library. See https://golang.org/s/sqldrivers for a list of third-party drivers.
func (db *DB) Ping() error
Ping verifies a connection to the database is still alive, establishing a connection if necessary.
也即Open通常与Ping一起使用,Open函数返回一个数据库连接池DB,Ping负责检测该远端数据库是否运行正常,因此通常会创建一个连接。
注意:Open返回的是一个连接池,而并非一个链接,如下:The returned DB is safe for concurrent use by multiple goroutines and maintains its own pool of idle connections. Thus, the Open function should be called just once. It is rarely necessary to close a DB.
所以通常是执行Open函数一次得到DB连接池,然后所有线程复用该连接池,理解这一点很重要!
例子:
import (
"fmt"
"net/http"
"time"
"database/sql"
_ "github.com/go-sql-driver/mysql"
)
//create the connection pool
func initConnPool() (*sql.DB, error) {
//connect to the database
//par := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8&writeTimeout=1s&timeout=10s", username, password, addrs, port, database)
par := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8&timeout=30s", username, password, addrs, port, database)
db, err := sql.Open("mysql", par) //第一个参数为驱动名
if err != nil {
return nil, fmt.Errorf("Failed to connect to log mysql: %s", err)
}
//ping the mysql
err = db.Ping()
if err != nil {
return nil, fmt.Errorf("Failed to ping mysql: %s", err)
}
//set db
//reuse the connection forever(Expired connections may be closed lazily before reuse)
//If d <= 0, connections are reused forever.
fmt.Printf("connMaxLifetime:%d\n", connMaxLifetime)
db.SetConnMaxLifetime(time.Duration(connMaxLifetime) * time.Hour)
//db.SetConnMaxLifetime(10*time.Second)
//SetMaxIdleConns sets the maximum number of connections in the idle connection pool.
//If n <= 0, no idle connections are retained.
fmt.Printf("maxIdleConns:%d\n", maxIdleConns)
db.SetMaxIdleConns(maxIdleConns)
//SetMaxOpenConns sets the maximum number of open connections to the database.
//If MaxIdleConns is greater than 0 and the new MaxOpenConns is less than MaxIdleConns, then MaxIdleConns will be reduced to match the new MaxOpenConns limit
//If n <= 0, then there is no limit on the number of open connections. The default is 0 (unlimited).
fmt.Printf("maxOpenConns:%d\n", maxOpenConns)
db.SetMaxOpenConns(maxOpenConns)
return db, nil
}
需要注意的是上面例子中的sql.DB
设置,这里详细讲解一下几个参数设置问题,也可参考这里:
timeout
par := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8&timeout=30s", username, password, addrs, port, database)
db, err := sql.Open("mysql", par) //第一个参数为驱动名
timeout参数用来设置TCP链接建立超时时间,通常为30s(网络环境正常)。
SetConnMaxLifetime
db.SetConnMaxLifetime(time.Duration(connMaxLifetime) * time.Hour)
SetConnMaxLifetime函数用来设置长连接的最长使用时间(从创建时开始计算),超过该时间Go会自动关闭该链接。通常将该时间设置较大,但小于mysqld端wait_timeout/2
。例如wait_timeout如果为36000,那么建议该函数设置为4 Hours,也即14400。
SetMaxIdleConns
db.SetMaxIdleConns(maxIdleConns)
SetMaxIdleConns函数用来设置连接池的大小,也即长连接的最大数量。通常用该值限制Mysql客户端创建长连接的数目,是非常重要的参数!
SetMaxOpenConns
db.SetMaxOpenConns(maxOpenConns)
SetMaxOpenConns函数用来设置向Mysql服务端发出的所有链接(包括长连接和短连接)的最大数目。通常该函数与SetMaxIdleConns函数设置的值应该一样,用于保证所有使用的连接均为长连接,不会有短连接产生!
查询
查询是数据库很普遍的操作,例子如下:
age := 27
rows, err := db.Query("SELECT name FROM users WHERE age=?", age)
if err != nil {
log.Fatal(err)
}
defer rows.Close()
for rows.Next() {
var name string
if err := rows.Scan(&name); err != nil {
log.Fatal(err)
}
fmt.Printf("%s is %d\n", name, age)
}
if err := rows.Err(); err != nil {
log.Fatal(err)
}
注意:要在db执行成功后执行rows.Close()(执行rows.Close()不会关闭长连接!)
修改(增、删、改)
以插入为例子,详细参考
insert_str := "insert into student (name, major, age) values('wang', 'computer', '20')"
_, err := db.Exec(insert_str)
if err != nil {
return fmt.Errorf("Failed to insert table student: %s", err)
}
其它修改操作参考官网
补充
这里Go没有提供函数设置长连接空闲timeout
,参考这里
总结
对于连接池,总结了Tcp链接timeout、长连接的最长使用时间、连接池大小以及发送链接的最大数目;对于Mysql的操作,总结了增、删、改、查等。后续还会补充链接read/write timeout设置问题……