参考网址
zhuanlan.zhihu.com/p/80303098
前言
go database/sql只是一套统一的抽象的接口,真正与数据库打交道的是各个数据库对应的驱动实现,
在使用时需要先注册对应驱动,然后就可以使用sql中定义的接口统一操作数据库。
连接池sql.DB
package main
import (
"database/sql"
_ "github.com/go-sql-driver/mysql"
)
func main() {
db, err := sql.Open("mysql",
"user:password@tcp(127.0.0.1:3306)/hello")
if err != nil {
log.Fatal(err)
}
defer db.Close()
err := db.Ping()
if err != nil {
}
}
sql.DB刚开始建立时是懒加载的,不会自动创建新的连接,只有使用Ping()或者运行查询时才会自动生成一个新的连接然后去连接数据库,只有这个时候才能确定数据库是否真的OK,所以建议**一定要在sql.Open后运行Ping()确定数据连接正常运行**。
sql.DB是连接后初始化的一个连接池,通常全局就初始化这一个连接池,并且长期运行,所有后续数据库操作都使用该连接池进行。sql.DB内部自动维护连接池,当需要连接时自动选择一个空闲的连接,如果没有空闲就建立一个新的连接,当连接不再使用时放回连接池中,内部会自动管理空闲回收。
数据库的连接是一个比较大的耗时和资源消耗操作,首先需要经典的TCP三次握手,tcp连接后数据库需要分配连接资源,同时根据连接信息鉴权等,所以建议使用长连接。对应到我们的go中,sql.DB会自动管理连接池,最好全局使用一个连接池,不要重复的open或者close。
查询
sql.DB支持4种查询:
db.Query()
db.QueryRow()
db.Prepare(sql) stmt.Query(args)
db.Exec()
- db.Query() 返回多行数据,需要依次遍历,并且需要自己关闭查询结果集
- db.QueryRow() 是专门查询一行数据的一个语法糖,返回ErrNoRow或者一行数据,不需要自己关闭结果集
- db.Prepare() 是预先将一个数据库连接(con)和一个条sql语句绑定并返回stmt结构体代表这个绑定后的连接,然后运行stmt.Query()或者stmt.QueryRow();stmt是并发安全的。之所以这样设计,是因为每次直接调用db.Prepare都会自动选择一个可用的con,每次选择的可能不是同一个con
- db.Exec() 适用于执行insert、update、delete等不需要返回结果集的操作
结果集
只有db.Query()函数返回结果集。
var (
id int
name string
)
rows, err := db.Query("select id, name from users where id = ?", 1)
if err != nil {
log.Fatal(err)
}
defer rows.Close()
for rows.Next() {
err := rows.Scan(&id, &name)
if err != nil {
log.Fatal(err)
}
log.Println(id, name)
}
err = rows.Err()
if err != nil {
log.Fatal(err)
}
- 首先检查结果集返回时是否有error
1. 如果有error,直接操作error
1. 如果无error,defer rows.Close(),确保结果集最终关闭,这样才能释放连接回连接池,Close是可以重复调用的,关闭已经关闭的结果集不会报错
- 遍历结果集
1. 使用for rows.Next()遍历结果集,这样迭代**一行一行处理结果**,节约内存分配,同时防止出现OOM的问题
1. 使用rows.Scan将一行数据填入指定的变量中,**scan会自动根据目标变量的类型处理类型转换**的问题,比如数据库中是varchar,但目标变量是int,那么scan会自动转换,当然如果转化出现error会返回error
事务
tx := db.Begin()
tx.Commit()
tx.Rollback()
事务是使用db.begin开始,以db.commit/db.rollback结束
普通的db.Query/db.QueryRow自动从连接池中选择一个可用连接,运行结束后会自动将连接放回连接池,下次运行再次重复这个过程
**db.begin会自动从连接池中选择一个连接并返回一直持有该连接的tx**(和db.Prepare有点像),后续所有事务操作都用tx,这样能保证是在用一个连接内运行事务,只有commit/rollback才会释放连接