mongodb事务不依赖行锁、undo log或redo log,而是基于mvcc+2pc实现:副本集事务用wiredtiger快照与oplog timestamp保证原子性,分片集群事务强制走两阶段提交;commit成功仅表示多数节点oplog已写入,非数据文件落盘。

MongoDB 事务不是 MySQL 那种“引擎内闭环”的东西,它压根不靠行锁、undo log 或 redo log 实现原子性和持久性——这是最根本的分水岭。
事务不走存储引擎锁路径,而是依赖 MVCC + 2PC 协同
MongoDB 的 WiredTiger 引擎本身支持单文档原子性,但多文档事务的协调完全交给 mongod 进程层:事务开始时打一个 clusterTime 快照,所有读基于该快照(readConcern: "snapshot"),写操作暂存于会话级内存缓冲区,直到 commitTransaction() 触发两阶段提交(2PC)流程。这个过程不加任何行级锁,外部写入不会被阻塞,也不会触发死锁检测。
常见错误现象:
- 用
readConcern: "local"立即查刚 commit 的数据,返回空——因为 oplog 还没同步到本地节点 - 跨分片事务中某个 shard 落后于 config server 的
clusterTime,事务卡在 prepare 状态,不超时也不自动 abort
commit 成功 ≠ 数据已落盘,只代表多数节点 oplog 已写入
MySQL 的 COMMIT 返回即表示 redo log 刷盘、数据页变更已持久;MongoDB 的 commitTransaction() 成功,仅说明该事务所有操作已作为一条或多条 oplog 条目写入「多数节点」的 oplog 集合——WiredTiger 数据文件本身可能尚未刷盘,崩溃恢复依赖 oplog 回放。
这意味着:
-
writeConcern: {w: "majority"}是事务生效的前提,不是可选项 - 若副本集只有 2 个节点,
w: "majority"实际等价于w: 2,任意节点宕机即无法 commit - 使用
readConcern: "majority"才能确保读到已 commit 的数据,"local"或"available"都不可靠
事务内禁止非幂等操作,不是语法限制而是拓扑硬约束
MongoDB 明确拒绝在事务中执行无法回滚或需全集群广播的操作,比如 createIndex()、collMod、$out 聚合阶段。这不是驱动报错,而是服务器直接拒绝——因为索引创建一旦开始,就无法在 prepare 阶段撤销,违反事务的原子性语义。
典型踩坑点:
-
findAndModify在事务中设upsert: true→ 触发隐式索引检查 → 报IllegalOperation - 在事务回调里调用
db.runCommand({create: "newcol"})→ 失败,且不会重试 - 试图在事务中修改用户角色(
db.updateUser)→ 操作路由到 admin 数据库,跨数据库事务允许,但权限系统不参与事务快照,行为未定义
真正难处理的从来不是语法怎么写,而是你得时刻意识到:MongoDB 事务是「会话 + 副本集拓扑 + oplog 分发」三者协同的结果,任何一个环节掉链子(比如 config server 不可用、某 shard 版本低于 4.2、驱动版本太旧),事务就静默失效或卡死——这些状态不会自动修复,只能靠监控和主动探测。











