MongoDB 4.2 事务不等同于 MySQL,其快照隔离非可串行化、跨分片需完整架构支持、写冲突需应用层重试、oplog 行为与生命周期特殊,且性能开销大,官方建议优先规避分布式事务。

MongoDB 4.2 的事务不等于“换个语法的 MySQL”,它在隔离性、锁机制、跨分片行为和错误恢复上都存在实质性差异,直接照搬关系型数据库经验容易踩坑。
快照隔离(Snapshot)不是可串行化(Serializable)
MongoDB 默认且仅支持 snapshot 隔离级别,它能防止脏读、不可重复读和幻读,但不提供传统关系型数据库中 SERIALIZABLE 那种严格的串行执行语义。例如:
- 两个并发事务同时读取同一份库存并各自扣减,即使都基于 snapshot 时间点,仍可能因无全局锁导致超卖 —— 这不是 bug,是设计使然
-
readConcern: "snapshot"只保证读取时看到一个一致的快照,不阻塞写入;而 MySQL 的SERIALIZABLE会加范围锁阻止并发修改 - 事务内多次读同一文档,结果一定一致;但外部新写入不会被阻塞,也不影响当前事务视图
跨分片事务依赖 config server 协调,不是“自动生效”
MySQL 的跨表事务天然存在,MongoDB 4.2 的跨分片事务却是显式架构依赖,缺一不可:
- 必须所有 shard、config server、mongos 都运行 4.2+,且
featureCompatibilityVersion设为"4.2" - 必须使用 WiredTiger 引擎 —— MMAPv1 在 4.2 中已被完全弃用,连启动都失败
- config server 必须可写且在线;若它宕机,新事务无法开启,进行中的事务可能卡在
prepare状态,不会自动回滚 - 客户端驱动版本必须 ≥ 3.11(Java)、≥ 3.12(Python),旧驱动调用
startTransaction()会静默降级为非事务行为
写冲突不自动重试,应用层必须自己处理 WriteConflict
MySQL 遇到死锁会自动选择 victim 并回滚,MongoDB 对写冲突(如两个事务并发更新同一文档)只抛出 WriteConflict 错误,不做任何干预:
- 错误信息形如:
WriteConflict: Write conflict during commit,不是网络或权限类错误,不能忽略 - 必须在应用层捕获该异常,并实现重试逻辑(推荐指数退避 + 最大重试次数限制)
- 重试时需重新获取 session、重启事务,不能复用原 session 或原查询结果
- 注意:重试不等于幂等 —— 若事务内有生成 UUID 或调用系统时间的操作,需提前移到事务外
事务生命周期与 oplog 行为和 MySQL 完全不同
MySQL 的 binlog 是追加式日志,事务提交即落盘;MongoDB 的事务日志绑定 WiredTiger 快照和 oplog 分发机制:
- 4.0 中所有写操作打包进单条 oplog,硬性限制总大小 ≤ 16MB;4.2 支持多条 oplog(每条 ≤ 16MB),但跨分片时每条写仍需独立同步到对应分片的 oplog
-
transactionLifetimeLimitSeconds默认 60 秒,超时强制 abort;这个计时器从startTransaction()开始,不受maxTimeMS影响 - 事务提交后,各分片的写入并非原子可见:
readConcern: "local"下,外部读可能只看到部分分片的结果(A 已写,B 还未同步),不保证全局强一致
最常被忽略的一点:MongoDB 事务不是兜底方案。它代价明确 —— 跨分片事务吞吐通常比副本集事务低 30%~50%,且运维复杂度陡增。官方文档至今仍建议优先通过嵌入文档、预计算、最终一致性补偿等方式规避分布式事务。

















