Go中MongoDB事务必须用session.WithTransaction回调实现,因v1.4+驱动已弃用mongo.Session的StartTransaction等方法;需满足副本集/分片集群、驱动≥v1.4、同Database获取Collection三前提,否则运行时失败。

Go 里用 mongo.Session 接口本身不能直接实现事务——它已被弃用,必须用 mongo.SessionContext 配合 session.WithTransaction 才能跑通 ACID 事务。 直接调 session.StartTransaction() 或手动调 session.CommitTransaction() 在新版驱动(v1.4+)中会 panic,旧版驱动则可能静默失败。别被过时文档带偏。
为什么 mongo.Session 不再用于事务控制
从 mongo-go-driver v1.4 开始,mongo.Session 的事务相关方法(如 StartTransaction、CommitTransaction)已被标记为 deprecated,并在 v1.11+ 中彻底移除。驱动强制要求所有事务操作必须封装在 session.WithTransaction 回调中,由其内部统一管理生命周期和上下文传播。
- 手动调
session.StartTransaction()会触发 panic:「session does not support manual transaction control」 -
session.EndSession()不能在回调外提前调用,否则WithTransaction内部会 panic:「session has been ended」 - 事务状态(如是否已提交、是否可重试)完全由
WithTransaction内部逻辑判断,外部无法干预
必须用 session.WithTransaction 的三个硬性前提
缺一不可,否则运行时直接失败或行为不可控:
- MongoDB 服务端是副本集(≥4.0)或分片集群(≥4.2),且已执行
rs.initiate()—— 单节点mongod --port 27017永远不支持事务 - Go 驱动版本 ≥ v1.4(推荐 v1.12+),检查方式:
go list -m go.mongodb.org/mongo-driver/mongo - 所有读写操作必须使用同一个
mongo.Database实例获取的mongo.Collection,跨实例会报错:「Transaction numbers do not match」
session.WithTransaction 回调里怎么写才不出错
关键不是“能不能写”,而是“怎么传 ctx、怎么拿 collection、怎么处理错误”:
立即学习“go语言免费学习笔记(深入)”;
- 回调参数是
sessCtx context.Context,所有 DB 操作(InsertOne、UpdateOne等)必须用它,不能混用外部context.WithTimeout等生成的 ctx -
collection必须在回调开头一次性获取:coll := db.Collection("orders"),不要在工具函数里重复调db.Collection - 错误必须区分类型:
mongo.IsTransientTransactionError(err)可重试;err.(mongo.CommandError).Name == "UnknownTransactionCommitResult"绝对不可重试 - 超时必须设在
options.Transaction().SetMaxCommitTime(),不是给sessCtx加 timeout
容易被忽略的因果一致性细节
即使事务成功提交,读操作也可能看不到最新数据——除非显式启用因果一致性:
- 启动 session 时需传
options.Session().SetCausalConsistency(true)(默认开启,但显式写更安全) - 读操作必须用
readconcern.Majority(),写操作依赖默认writeconcern.Majority() - 如果事务内先
InsertOne再FindOne同一文档,不用因果一致性也能看到;但跨事务读取时,没开 causal consistency 就可能读到旧快照
真正卡住人的从来不是语法,而是副本集没初始化、collection 实例来源不一致、或者把 UnknownTransactionCommitResult 当成普通错误去重试——这会导致双花。



















