应使用 pgx/v5 驱动并调用 sql.open("postgres", connstr),注册驱动需加 _ 前缀;连接池需设 maxopenconns/maxidleconns;事务必须用 begintx(ctx, opts) 并传入 context,所有操作使用同一 tx 实例,显式判断错误后 rollback 或 commit。

用 database/sql 连 PostgreSQL,别直接写驱动包名
Go 官方 database/sql 是接口层,必须搭配具体驱动(如 github.com/lib/pq 或 github.com/jackc/pgx/v5)才能工作。直接 import github.com/lib/pq 但不调用 sql.Open 时的驱动注册逻辑(_ "github.com/lib/pq"),会导致 sql.Open("postgres", ...) 报错 sql: unknown driver "postgres"。
实操建议:
- 用
pgx/v5更推荐:性能更好、原生支持pgx.Conn和pgx.Tx,且对上下文取消更友好 - 连接字符串里避免硬编码密码;用
url.QueryEscape处理含特殊字符的密码(比如@或/) - 设置
MaxOpenConns和MaxIdleConns,否则默认不限制可能压垮数据库
BeginTx 要传 context,别用 Begin
*sql.DB.Begin() 不接受 context,无法响应超时或取消;生产环境必须用 *sql.DB.BeginTx(ctx, opts)。如果 ctx 已被 cancel,BeginTx 会立即返回错误,而不是卡住或创建无效事务。
常见错误现象:
- 忘记传
context.WithTimeout,导致事务卡死数分钟,连接池耗尽 - 传了
context.Background()却在 HTTP handler 中没做 cancel,导致请求中断后事务仍挂起 -
sql.TxOptions{Isolation: sql.LevelRepeatableRead}在 PostgreSQL 上实际降级为READ COMMITTED(PostgreSQL 不支持真正的可重复读)
示例关键片段:
PostgreSQL 18.4 官方 Ubuntu 安装包现已发布,这是目前最新的稳定版本。推荐通过官方 APT 仓库安装:先执行 sudo apt update 更新索引,再运行 sudo apt install postgresql-18 即可完成部署。新版本引入了异步 I/O 子系统,在顺序扫描与 VACUUM 场景下性能提升显著,同时支持 UUID v7 原生生成函数与虚拟生成列。
ctx, cancel := context.WithTimeout(r.Context(), 5*time.Second)
defer cancel()
tx, err := db.BeginTx(ctx, &sql.TxOptions{Isolation: sql.LevelReadCommitted})
if err != nil {
http.Error(w, "tx begin failed", http.StatusInternalServerError)
return
}
事务内所有查询必须用 tx 对象,不能混用 db
tx.QueryRow、tx.Exec 等方法才真正运行在事务上下文中;若误用 db.QueryRow,语句会走独立连接、自动提交,破坏事务原子性——这是最隐蔽也最常见的 bug。
使用场景提醒:
- INSERT 后需要 RETURNING 获取生成 ID?必须用
tx.QueryRow,否则RETURNING结果不参与事务回滚 - 执行多个 UPDATE 涉及不同表?全部走同一个
tx实例,别拆成多个db.Exec - 调用其他函数时,把
*sql.Tx当参数透传进去,不要让它内部再取*sql.DB
显式 Commit 或 Rollback,别依赖 defer
用 defer tx.Rollback() 看似安全,但一旦 Commit() 成功,再执行 Rollback() 会报错 sql: transaction has already been committed or rolled back,可能掩盖真实错误日志。
正确做法是只在出错路径上 rollback,并确保 commit 后不再调用 rollback:
err := doSomething(tx)
if err != nil {
tx.Rollback()
return err
}
return tx.Commit()
容易被忽略的地方:
- 事务函数返回前忘了 return error,导致 commit 后继续执行后续代码(可能 panic 或污染状态)
- rollback 本身也可能失败(如网络断开),应记录日志但通常不重试
- pgx 用户注意:
pgx.Tx.Commit(ctx)和pgx.Tx.Rollback(ctx)都要传 context,且 rollback 失败时 ctx 取消可能让其提前返回










