MongoDB事务必须在副本集或分片集群中运行,单机实例不支持;调用Session.StartTransaction()会返回ErrUnshardedCluster错误,需通过isMaster命令确认部署模式,并配置readConcern=majority和writeConcern=majority以保障一致性。

事务必须在副本集或分片集群中运行
单机 MongoDB 实例不支持事务,mongo-go-driver 调用 Session.StartTransaction() 会直接返回 mongo.ErrUnshardedCluster 或类似错误。确认部署模式是第一步,不是可选项。
检查方式很简单:连接后执行 db.runCommand({isMaster: 1}),响应中必须包含 "ismaster": true 且 "hosts" 数组长度 ≥ 2(副本集)或有 "msg": "isdbgrid"(分片集群)。本地开发建议用 docker run -d -p 27017:27017 --name rs0 mongo:6.0 --replSet rs0 启动,再用 rs.initiate() 初始化。
正确获取会话并显式结束生命周期
事务依赖 mongo.Session,但 driver 不自动管理其释放;忘记 session.EndSession() 会导致连接泄漏、会话堆积,最终触发 maxTransactionLockRequestTimeoutMillis 超时或连接池耗尽。
必须用 defer 包裹 session.EndSession(),且确保它在所有分支(包括 panic)下执行:
立即学习“go语言免费学习笔记(深入)”;
session, err := client.StartSession()
if err != nil {
return err
}
defer session.EndSession(ctx) // 关键:不能漏
err = session.WithTransaction(ctx, func(sessCtx mongo.SessionContext) (interface{}, error) {
// 执行操作
_, err := collection1.InsertOne(sessCtx, doc1)
if err != nil {
return nil, err
}
_, err = collection2.UpdateOne(sessCtx, filter, update)
return nil, err
})
注意:session.WithTransaction() 内部已处理提交/回滚,你只需关注业务逻辑返回的 error;非空 error 会触发自动回滚。
事务内操作必须使用会话上下文(sessCtx)
常见错误是继续用原始 context.Background() 或客户端默认 ctx 调用 CRUD 方法,这会让操作脱离事务边界,变成独立的「自动提交」操作 —— 看似成功,实则无法回滚。
所有事务内数据库调用必须传入 sessCtx(即 mongo.SessionContext 类型),它由 session.WithTransaction() 回调参数提供:
-
collection.FindOne(sessCtx, filter)✅ -
collection.InsertMany(sessCtx, docs)✅ -
collection.DeleteMany(sessCtx, filter)✅ -
collection.UpdateOne(context.Background(), filter, update)❌(脱离事务) -
client.Database("db").Collection("coll").FindOne(ctx, filter)❌(ctx 非 sessCtx)
driver 不做静默降级,也不会报错提醒你用错了 ctx —— 它就真的不进事务。
读关注与写关注需显式配置,否则可能读不到刚写入的数据
事务默认使用 readConcern: local 和 writeConcern: {w: 1},在多节点副本集中,如果其他会话用 readConcern: majority 查询,可能看不到事务中已提交但尚未同步到多数节点的数据,造成“幻读”或业务不一致。
解决方法是在 SessionOptions 中指定更强的一致性要求:
session, err := client.StartSession(&options.SessionOptions{
DefaultReadConcern: readconcern.Majority(),
DefaultWriteConcern: writeconcern.New(writeconcern.WMajority()),
})
但要注意:majority 会增加延迟,且要求副本集多数节点在线;若对延迟敏感或容忍短暂不一致,保持默认即可。别盲目设高,先看场景是否真需要。
事务超时默认 60 秒(transactionLifetimeLimitSeconds),长事务容易被服务端中止,关键路径务必控制操作粒度和耗时。


















