mongodb多文档事务仅支持副本集或分片集群,单节点不支持;需预创建集合与索引;会话必须配对管理;推荐使用snapshot读关注和majority写关注。

必须在副本集(Replica Set)或分片集群中运行,单节点部署不支持多文档事务。
事务只能在副本集上启动
MongoDB 的多文档事务从 4.0 版本起仅支持副本集和分片集群。如果你连接的是单个 mongod 实例(非副本集),调用 db.getMongo().startSession() 不会报错,但后续 session.startTransaction() 会直接失败,错误类似:CommandNotSupported: Transactions are not supported in sharded clusters without a config server or on standalone servers。
确认方式很简单:执行 rs.status()。如果返回 "errmsg" : "not running with --replSet" 或根本不是对象结构,说明当前不是副本集模式。
常见误操作:
- 本地开发用
mongod --port 27017启动,没加--replSet rs0参数 - Docker Compose 中只启了一个容器,未配置副本集初始化脚本
- Atlas 免费层默认是单节点,需手动升级为副本集部署
会话与事务生命周期必须严格配对
每个事务绑定一个 ClientSession,且该会话在事务提交或中止前不能被复用或提前关闭。典型错误是忘记调用 session.endSession(),导致连接池中会话泄漏;更隐蔽的问题是,在 try/catch 外部提前调用了 session.endSession(),使 abortTransaction() 无法执行。
正确做法是把 endSession() 放在 finally 块里,并确保它只在事务结束之后调用:
try {
session.startTransaction();
await coll.updateOne({ _id: 1 }, { $inc: { balance: -100 } });
await coll.updateOne({ _id: 2 }, { $inc: { balance: 100 } });
await session.commitTransaction();
} catch (error) {
await session.abortTransaction();
throw error;
} finally {
await session.endSession(); // 必须放在这里
}
注意:MongoDB 驱动(如 Node.js 的 mongodb@6.x)不会自动回收未结束的会话,长期运行的服务可能因会话堆积触发服务器端 maxTransactionLockRequestTimeoutMillis 限制。
事务内禁止隐式建集合或索引
即使集合不存在,insertOne 在事务外会自动创建集合,但在事务中这么做会直接报错:NamespaceNotFound: collection does not exist。MongoDB 明确禁止在事务中执行 DDL 操作,包括由写入触发的自动建集合行为。
所以务必提前确保所有涉及的集合已存在。验证方法:
- 执行
db.getCollectionNames()确认集合名存在 - 或用
db.runCommand({ listCollections: 1, filter: { name: "orders" } })
另外,createIndex 也不能放在事务块内——它不是写操作,而是元数据变更,同样被拒绝。索引必须在事务开始前建好。
读关注(readConcern)和写关注(writeConcern)影响事务可见性
默认情况下,事务使用 readConcern: "local" 和 writeConcern: { w: 1 }。这意味着:事务中的读操作能看到未同步到从节点的数据;而写操作只要主节点落盘就认为成功——这在主节点宕机、未同步到多数节点时可能导致回滚后数据丢失。
生产环境强烈建议显式设置:
-
readConcern: "snapshot":保证事务内多次读取看到同一快照,避免不可重复读 -
writeConcern: { w: "majority" }:确保写入被大多数节点确认,防止主节点故障后丢失已提交事务
设置方式(Node.js 驱动示例):
const txnOptions = {
readConcern: { level: "snapshot" },
writeConcern: { w: "majority" }
};
await session.withTransaction(async () => { ... }, txnOptions);
注意:snapshot 级别要求 MongoDB ≥ 4.2,且仅在副本集或分片集群中可用;若用 linearizable,则每次读都会等待 majority 提交,延迟更高,一般不用于普通事务。
事务不是银弹。跨文档一致性需求出现时,先检查是否能通过嵌入文档、原子更新操作符(如 $push、$addToSet)或两阶段提交模式绕开事务——毕竟事务有性能开销、超时限制(默认 60 秒)、以及对锁粒度和内存的隐性压力。真正需要它的时候,务必确认架构、会话管理、集合预置和读写关注这四点都踩准了。











