MongoDB多文档事务需4.0+副本集或4.2+分片集群,单节点不支持;必须在session中显式startTransaction()和commitTransaction();仅支持常规集合且同库操作;默认snapshot隔离,超时60秒,冲突报WriteConflict。

必须先确认 MongoDB 版本和部署模式
MongoDB 多文档事务只在 4.0+(副本集)或 4.2+(分片集群)中可用,单节点 mongod 实例不支持。如果你用的是云服务(如 Atlas),默认开启副本集,但本地用 mongod --port 27017 直接启动,事务会直接报错 Transaction numbers are only allowed on a replica set member or mongos。
实操建议:
- 本地开发:用
mongod --replSet rs0 --port 27017启动,再执行rs.initiate() - Docker 用户:确保容器启动时带
--replSet rs0参数,并在进入 shell 后调用rs.initiate() - 检查是否就绪:连接后运行
rs.status(),看到members列表且状态为PRIMARY或SECONDARY才算成功
事务必须在 session 中显式启动和结束
MongoDB 不像 SQL 那样自动管理事务上下文,所有事务操作都绑定到一个 session 对象,且必须手动调用 startTransaction() 和 commitTransaction()(或 abortTransaction())。漏掉任一环节都会导致操作被当作普通写入执行——也就是「你以为在事务里,其实根本没有」。
常见错误现象:
- 没调用
startTransaction()就直接执行collection.insertOne()→ 操作立即生效,不回滚 - 调用了
startTransaction()但忘记commitTransaction()→ 事务长时间挂起,占用资源,最终超时自动 abort(默认 60 秒) - 在事务中调用未启用读关注的查询(如
find()不带readConcern: "snapshot")→ 可能读到未提交数据或报错Read concern 'snapshot' is not available for this operation
示例(Node.js driver):
const session = client.startSession();
try {
await session.withTransaction(async () => {
await db.collection('orders').insertOne({ status: 'pending' }, { session });
await db.collection('inventory').updateOne(
{ sku: 'abc' },
{ $inc: { qty: -1 } },
{ session }
);
});
} finally {
await session.endSession();
}
事务内集合必须是副本集上的 capped collection 以外的常规集合
事务不支持 capped 集合、system.* 集合(如 system.users)、以及任何使用 collation 且 collation 不为 simple 的集合(除非显式指定 readConcern: "snapshot")。最常踩的坑是:你在测试时建了个 capped: true 的日志集合,然后试图在里面做事务更新,结果报错 Transactions are not supported on capped collections。
实操建议:
- 建集合前确认:用
db.getCollectionInfos({ name: "mycol" })查看options.capped字段 - 事务中读写的所有集合,必须位于同一数据库(跨库事务不支持)
- 避免在事务中调用
createIndex()、drop()等 DDL 操作,它们会直接报错Command not supported in transactions
事务超时和锁行为比想象中更敏感
MongoDB 事务不是“无限期挂起”,它有硬性超时(默认 60 秒),而且事务期间对涉及文档加的是“文档级锁”——看起来很轻量,但在高并发写同一批文档时,很容易触发冲突,表现为 WriteConflict 错误。这不是代码 bug,而是事务引擎主动中止冲突操作来保证一致性。
影响点:
- 长耗时操作(如大数组遍历、外部 HTTP 调用)放进事务里 → 极大概率超时或被 kill
- 多个事务同时修改同一个
_id文档 → 其中一个会收到WriteConflict,需重试逻辑(driver 通常不自动重试事务) -
readConcern: "snapshot"是事务默认值,但如果你手动改成"local"或"majority",可能读不到已写入但未提交的数据,或引发兼容性问题
真正容易被忽略的是:事务一旦开始,所有操作都走 snapshot 隔离,但 driver 日志里不会提示你当前处于事务上下文——你得自己盯紧 session 生命周期和错误类型。










