用 db.transaction() 更安全,因它自动处理回滚与提交;所有操作必须链在 tx 上,db 操作无效;嵌套事务实为 savepoint;默认事务可关闭但需权衡一致性与性能。

用 db.Transaction() 就对了,90% 的场景它比手写 Begin/Commit 更安全、更少出错。
为什么不能继续用 db 而必须用 tx
很多人调了 tx := db.Begin(),后面还写 db.Create(&u),结果数据根本没进事务——因为 db 是原始无事务连接,tx 才是带上下文的新实例。所有 CRUD 必须链在 tx 上,否则等于白开事务。
-
tx.Create()、tx.First()、tx.Model().Update()都可以;db.Create()不行 - 嵌套调用里传参也要传
tx,别一不小心又传回了原始db -
tx提交或回滚后不可复用,再调tx.Create()会 panic 报"invalid transaction"
db.Transaction() 怎么用才不踩坑
它内部封装了 defer + recover + Rollback/Commit 全流程,但有几个边界你得盯紧:
Colly 是一个用于 Go 语言的快速开源爬取和爬虫框架。它适用于从简单的页面提取到异步爬虫处理大量页面集合,支持请求回调和结构化解析。
- 函数返回非
nil error→ 自动回滚;返回nil→ 自动提交 - 函数内
panic会被捕获并回滚;但os.Exit()或runtime.Goexit()拦不住,事务就挂住了 - 不支持“中途退出但不回滚”,比如想
return做日志然后继续提交——这种逻辑得换显式事务 - 钩子如
tx.AfterCommit()必须在Transaction函数体内注册,且只对当前tx生效
嵌套事务其实是 Savepoint,不是真嵌套
GORM 的 “嵌套事务”(比如在 tx 里再 tx.Begin())底层是数据库的 SAVEPOINT,MySQL 和 PostgreSQL 行为略有差异,别当它是独立事务来用:
-
inner.RollbackTo("sp")只回滚到保存点,外层已执行的语句不受影响 - 但外层
tx.Rollback()会连带清掉所有 savepoint,inner 的状态直接作废 - 别混用
tx.SavePoint()和原生tx.Exec("SAVEPOINT sp1"),容易报"no such savepoint" - Savepoint 不提供跨事务的数据可见性,也做不到“部分提交”
要不要关掉默认事务?看场景
GORM 默认给每个 Create/Update/Delete 包一层事务,单条语句原子性有保障,但性能损耗约 30%。关闭它能提速,代价是你得自己兜底:
- 配置方式:
gorm.Config{SkipDefaultTransaction: true} - 适用场景:日志写入、埋点上报、缓存刷新等“尽力而为”操作
- 禁用后,单条失败不会自动回滚(本来也没啥可回滚),但你也失去了隐式一致性保护
- Web handler 中仍需手动事务控制,禁用默认事务 ≠ 不需要事务
真正难的不是写 tx.Commit(),而是判断什么时候该用 Savepoint、什么时候该拆成多个独立事务、以及 HTTP 调用卡住时怎么不让整个连接池被拖死——这些没法靠封装函数自动解决。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










