Go 中操作 SQL 数据库完全指南

Go database/sql 标准用法:驱动注册、连接池配置、查询与 Scan、预编译语句、事务、常见访问模式与工具选型(sqlc/sqlx/GORM)。本文从连接到生产模式全覆盖。

最佳实践
数据整理与查询插画

本文依据官方文档整理,未执行运行验证或性能基准。代码片段展示局部用法,业务函数、数据和环境需按项目补齐;版本与配置以所引文档为准。

直接回答:Go 操作 SQL 数据库的标准入口是 database/sql 包——驱动注册后 sql.Open 拿连接池(不是单连接),QueryContext+Scan 读数据,ExecContext 写数据,Prepare 预编译高频语句,Tx 管事务。生产要点:连接池参数必须调、查询必须带 Context、行必须关闭。

连接:Open 得到的是池

import (
    "database/sql"
    _ "github.com/lib/pq"  // 驱动注册
)

db, err := sql.Open("postgres", dsn)
if err != nil { return err }
defer db.Close() // 在应用生命周期结束时关闭,不要每个请求建池
db.SetMaxOpenConns(25)           // 最大打开连接
db.SetMaxIdleConns(5)            // 空闲连接
db.SetConnMaxLifetime(5 * time.Minute)  // 连接寿命
if err := db.PingContext(ctx); err != nil { return err }

sql.Open 返回可复用的数据库句柄,可能仅验证参数而不建连;用 PingContext 验证连通性。连接池参数不调等于裸奔:默认无上限的连接数能拖垮数据库。

查询与修改

// 单行
var name string
err := db.QueryRowContext(ctx, "SELECT name FROM users WHERE id=$1", id).Scan(&name)
if errors.Is(err, sql.ErrNoRows) { return err } // 交由上层映射业务“未找到”
if err != nil { return err }

// 多行——rows 必须 Close
rows, err := db.QueryContext(ctx, "SELECT id, name FROM users")
if err != nil { return err }
defer rows.Close()
for rows.Next() {
    var id int; var name string
    if err := rows.Scan(&id, &name); err != nil { return err }
}
if err := rows.Err(); err != nil { return err }

// 写
res, err := db.ExecContext(ctx, "UPDATE users SET name=$1 WHERE id=$2", name, id)
if err != nil { return err }
_ = res

事务

tx, err := db.BeginTx(ctx, nil)
if err != nil { return err }
defer tx.Rollback()
if _, err := tx.ExecContext(ctx, "UPDATE users SET name=$1 WHERE id=$2", name, id); err != nil {
    return err
}
return tx.Commit()

defer tx.Rollback() 是安全网——Commit 成功后 Rollback 返回 sql.ErrTxDone,不会撤销已提交事务。Commit 报错不能当作成功;部分网络故障下还需业务层核对提交结果。

工具选型

工具 定位
database/sql 裸用 可控性最高,样板多
sqlx 小幅增强(StructScan 等),保留 SQL
sqlc SQL 生成类型安全 Go 代码,编译期校验
GORM 全功能 ORM,开发快,黑盒多

团队 SQL 能力强选 sqlc/sqlx,追求 CRUD 速度选 GORM——sqlx 和常见 GORM 驱动依赖 database/sql;sqlc 也可生成 pgx 原生代码,并非都经 database/sql。资源和事务管理原则仍适用。

常见问题(FAQ)

Q:QueryRow 的 ErrNoRows 算错误吗?
A:是"业务上的没找到"——Scan 返回它时单独判断,别和系统错误混为一谈,更别当 500 返回。

Q:连接池满了的表现是什么?
A:请求阻塞等连接(默认无限等待)——表象是接口集体变慢。监控 MaxOpenConns 水位与等待时间,及早发现。

Q:为什么强调 Context 版本的方法?
A:Query/Exec 不接收调用方的 Context,但数据库或驱动自身的超时仍可能生效。生产代码一律用 QueryContext/ExecContext,把上层超时传进数据库层。

官方参考

资料核对日期:2026-09-29。

延伸阅读

获取专属方案

联系我们

加入社区

微信扫码
加入官方交流群

立即体验

在线开通,按量计费,真正的云服务!

立即开始

选择观测云版本

代码托管平台