用 database/sql 就够了,它轻量稳定可控,适合初学者和小项目;应避免过早使用 ORM,需手动管理连接、事务和预处理逻辑,并合理配置连接池参数。

用 database/sql 就够了,别急着上 ORM
Go 初级项目接入数据库,第一原则是「先跑通、再优化」。database/sql 是标准库,轻量、稳定、可控,比任何第三方 ORM 都更适合练手和小项目。ORM(比如 gorm 或 sqlx)看似省事,但隐藏了连接管理、事务边界、预处理逻辑等关键细节,出问题时反而更难定位。
实操建议:
- 只导入驱动(如
_ "github.com/lib/pq"或_ "github.com/go-sql-driver/mysql"),不引入额外抽象层 - 用
sql.Open获取*sql.DB,立刻调用db.Ping()验证连接是否可用 - 连接字符串里避免硬编码密码,改用环境变量(
os.Getenv("DB_URL")) - 不要在 handler 里反复
sql.Open——*sql.DB本身是并发安全的、带连接池,全局复用一个实例即可
sql.QueryRow 和 sql.Exec 要配对用好
初学者常混淆查询和执行:读数据用 QueryRow / Query,写数据(INSERT/UPDATE/DELETE)用 Exec。混用会导致 panic 或静默失败(比如对 INSERT 用 QueryRow.Scan,会报 sql: expected 1 destination arguments)。
常见错误场景:
立即学习“go语言免费学习笔记(深入)”;
拍客竞拍系统是一款免费竞拍网站建设软件,任何个人可以下载使用,但未经商业授权不能进行商业活动,程序源代码开源,任何个人和企业可以进行二次开发,但不能以出售和盈利为目的。安装方法,将www文件夹里面的所有文件上传至虚拟主机,在浏览器执行http://你的域名/install.php或者直接导入数据库文件执行。本次升级优化了一下内容1,程序和模板完美分离。2,优化了安装文件。3,后台增加模板切换功能。
- INSERT 后想获取自增 ID,却用了
QueryRow("INSERT ...").Scan(&id)→ 应该用Exec+Result.LastInsertId()(MySQL)或QueryRow("INSERT ... RETURNING id").Scan(&id)(PostgreSQL) - UPDATE 语句没检查
RowsAffected(),误以为更新成功 → 实际可能 where 条件没匹配到任何行 - 用
Query做单行查询却不调用rows.Next()和rows.Scan()→ 连接不会释放,迟早触发too many connections
row := db.QueryRow("SELECT name FROM users WHERE id = $1", 123)
var name string
if err := row.Scan(&name); err != nil {
// 处理 NOT FOUND 或其他 error
return
}事务必须显式控制,别依赖框架自动提交
Go 没有「声明式事务」机制,Begin → Commit/Rollback 全靠手动。初级项目最容易漏的是 defer tx.Rollback() 的覆盖逻辑 —— 如果 Commit 成功了,还执行 Rollback 会报错(虽然不影响数据,但日志刷屏)。
正确模式:
- 用
tx, err := db.Begin()开启事务 - 所有 SQL 都调用
tx.QueryRow/tx.Exec,不是db本身 -
if err != nil { tx.Rollback(); return err }出现在每个关键步骤后 - 最后
tx.Commit()成功才返回 nil;否则确保Rollback只执行一次
tx, err := db.Begin()
if err != nil {
return err
}
defer func() {
if p := recover(); p != nil {
tx.Rollback()
panic(p)
}
}()
_, err = tx.Exec("INSERT INTO orders (...) VALUES (...)")
if err != nil {
tx.Rollback()
return err
}
return tx.Commit()连接池参数不调默认值,大概率出生产事故
*sql.DB 默认最大连接数是 0(无限制),在并发稍高的服务里,数据库很快被拖垮。初级项目也得设基础水位:
-
db.SetMaxOpenConns(20):防止打爆数据库连接数 -
db.SetMaxIdleConns(5):空闲连接太多浪费资源,太少又频繁建连 -
db.SetConnMaxLifetime(30 * time.Minute):避免连接僵死(尤其云数据库有连接超时策略)
这些参数必须在 db.Ping() 之后、业务使用之前设置,否则无效。另外,别在每次 HTTP 请求里新建 *sql.DB —— 它不是轻量对象,初始化开销大,且连接池无法复用。









