recover不能回滚事务,仅终止panic传播;必须在panic前defer注册,手动调用tx.rollback()等清理操作,子goroutine需独立recover。

recover 不能用于回滚已发生的事务操作,它只终止 panic 传播,不逆转副作用。你在事务函数里 defer recover,顶多让程序不崩溃,但数据库写入、文件修改、状态变更等已执行动作不会自动撤销。
recover 必须在 defer 中调用,且 defer 要在 panic 前注册
事务函数中如果先 panic 再 defer,recover 永远捕获不到。Go 的 defer 链在 panic 触发时只执行**已注册**的 defer,后续注册的会被跳过。
- 错误写法:
panic("db timeout"); defer func() { recover() }()——recover不会执行 - 正确顺序:先
defer func() { if r := recover(); r != nil { /* 记录、清理 */ } }(),再可能触发 panic 的事务逻辑 - 若事务跨多个函数(如
beginTx→updateUser→commit),每个可能 panic 的函数都得自己配defer+recover,否则上层无法兜底
事务失败后必须显式 rollback,recover 不会自动做这件事
recover 的作用仅限于“不让 goroutine 死掉”,它不会调用 tx.Rollback(),也不会关闭连接、释放锁或重置状态。这些都得你手动补全。
- 常见遗漏:只打印日志或返回错误,却忘了
if tx != nil { tx.Rollback() } - 注意
tx.Rollback()本身也可能 panic(比如网络断开),建议再包一层defer或用defer func() { _ = tx.Rollback() }()防止二次崩溃 - 如果事务中启用了子 goroutine(如异步日志上报),它们不受当前
recover影响,需各自加defer+recover
不要在主 goroutine 的 defer 中试图捕获子 goroutine 的 panic
HTTP handler 启动一个子 goroutine 执行数据库事务,主线程里写 defer recover() 完全无效。子 goroutine panic 后静默退出,事务状态悬空,日志也无迹可寻。
- 正确做法:每个事务 goroutine 内部独立封装,例如
go func() { defer func() { recover() }(); doTx() }() - 更稳妥的方式是统一用
safeGo包装:传入事务函数前,确保其内部已含defer+recover和Rollback逻辑 - 框架如 Gin 默认带 recover 中间件,但它只覆盖 handler 函数体,不覆盖你手动起的 goroutine
事务场景下最易被忽略的是:recover 后的代码继续运行,但 tx 变量可能已为 nil 或处于不可用状态,直接调用 tx.Commit() 会 panic。务必在 recover 分支里明确判断事务对象有效性,而不是假设“只要没崩溃就能继续提交”。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











