Golang 从入门到放弃 -0x0F
database/sql 入门:连接数据库、CRUD、事务,以及连接池的基本认知。
前面我们已经能写出最基本的 HTTP 服务了。接下来这个坑几乎谁都绕不过去:数据库。理论上你也可以把数据都放内存里,然后重启一次喜提失忆,但那样很快就会把自己送走。
先认识 database/sql
Go 标准库里和数据库打交道,核心入口通常是 database/sql。它更像一层通用规范,真正连 MySQL、PostgreSQL,还得配合对应驱动。
import (
"database/sql"
_ "github.com/go-sql-driver/mysql"
)那个下划线导入看着有点怪,但很常见。它表示“我不用直接调用这个包里的东西,但我要它的初始化副作用”。驱动注册基本就靠这个。
连接数据库
dsn := "user:password@tcp(127.0.0.1:3306)/demo?charset=utf8mb4&parseTime=True&loc=Local"
db, err := sql.Open("mysql", dsn)
if err != nil {
return err
}
defer db.Close()
if err := db.Ping(); err != nil {
return err
}这里有个很容易忽略的小点:sql.Open 不一定会立刻真去连库,它更多是在准备一个连接池对象。所以更稳妥的习惯是后面补一发 Ping()。
查一条数据
type User struct {
ID int
Name string
Age int
}
var user User
row := db.QueryRow("SELECT id, name, age FROM users WHERE id = ?", 1)
err := row.Scan(&user.ID, &user.Name, &user.Age)
if err != nil {
return err
}Scan 的时候别忘了传地址,不然数据往哪儿写都成问题。
查多条数据
rows, err := db.Query("SELECT id, name, age FROM users")
if err != nil {
return err
}
defer rows.Close()
users := make([]User, 0)
for rows.Next() {
var user User
if err := rows.Scan(&user.ID, &user.Name, &user.Age); err != nil {
return err
}
users = append(users, user)
}
if err := rows.Err(); err != nil {
return err
}这里最后那句 rows.Err() 不要省。很多人遍历完就直接走人,结果把中途出错的信息漏掉了。
写数据
增删改大多走 Exec。
result, err := db.Exec(
"INSERT INTO users(name, age) VALUES(?, ?)",
"Raymond",
18,
)
if err != nil {
return err
}
id, _ := result.LastInsertId()
fmt.Println("new id:", id)更新、删除也差不多,主要看返回的 RowsAffected()。
事务别只会听过
只要你的业务里有“这几步要么一起成功,要么一起失败”,事务就该出场了。
tx, err := db.Begin()
if err != nil {
return err
}
defer tx.Rollback()
if _, err := tx.Exec("UPDATE users SET age = age + 1 WHERE id = ?", 1); err != nil {
return err
}
if _, err := tx.Exec("UPDATE users SET age = age - 1 WHERE id = ?", 2); err != nil {
return err
}
if err := tx.Commit(); err != nil {
return err
}这里的套路建议你养成肌肉记忆:先 Begin,马上 defer Rollback(),全部成功后再 Commit()。这样就算中途 return 了,也不至于留下半截事务在那儿发呆。
连接池也要有点概念
db 不是一个单连接,它背后其实是连接池。默认值有时候不一定合适,服务一上量就可能出现各种奇奇怪怪的问题。
db.SetMaxOpenConns(20)
db.SetMaxIdleConns(10)
db.SetConnMaxLifetime(time.Hour)这些参数没有什么一刀切神仙值,核心还是看你的数据库配置、业务并发和部署环境。但至少你要知道它们存在,不要把数据库性能问题全怪给“Go不行”。
小结
这一章你先把这些能力点拿住:
sql.Open是入口,Ping用来确认真的能连上。QueryRow查一条,Query查多条,Exec做增删改。- 事务是业务一致性的底线,不是装饰品。
db背后是连接池,参数别完全没概念。
下一章我们说测试。写代码全靠信仰也不是不行,但一般坚持不了太久。