MongoDB事务ACID能力弱于MySQL:多文档事务受限于单分片/副本集部署,隔离仅支持snapshot(不防write skew),有60秒硬超时,无显式锁、分布式事务及可配置隔离级别。

MongoDB 事务的 ACID 能力不能“媲美”MySQL,尤其在生产级复杂场景下 —— 它是有限制的、有前提的、且实现机制根本不同。
多文档事务只在副本集或分片集群的特定部署下可用
MongoDB 的多文档事务(session.startTransaction())要求整个事务操作必须落在同一个分片上(分片集群中),或全部发生在主节点及其可读副本集内(副本集模式)。一旦跨分片写入(比如同时更新 orders 和 inventory,而它们被路由到不同分片),事务会直接报错:Multi-document transactions are not supported on sharded clusters when the transaction spans multiple shards。这和 MySQL 的 InnoDB 在单实例内天然支持任意表间事务有本质区别。
实操建议:
- 设计分片键时,必须把高频共事务的字段(如
userId)作为分片键,确保相关文档物理聚集 - 避免在事务中调用
$lookup跨集合关联,尤其是目标集合不在同一分片时 - 副本集模式下,事务默认不读取从节点(
readConcern: "majority"),否则可能因复制延迟导致脏读
隔离级别只有 snapshot,不支持 read uncommitted / repeatable read / serializable
MongoDB 事务底层依赖 WiredTiger 存储引擎的 snapshot isolation,它通过 MVCC(多版本并发控制)提供一致性快照视图。但它不提供 SQL 标准中的其他隔离级别,也没有 SELECT ... FOR UPDATE 这类行锁语义。这意味着:
- 无法显式加锁防并发覆盖(比如“先查余额再扣减”,中间可能被其他事务修改)
- 事务内两次读同一文档,结果一定一致(snapshot 保证),但无法阻止外部事务在你提交前修改该文档并抢先提交(write skew 问题依然存在)
- 没有
SET TRANSACTION ISOLATION LEVEL这类配置项,隔离行为完全由引擎固化
对比 MySQL InnoDB:你可以用 SELECT ... LOCK IN SHARE MODE 或 FOR UPDATE 主动控制锁粒度和等待行为;MongoDB 只能靠应用层重试或预占(如用 findAndModify 更新带条件的字段)来规避冲突。
事务生命周期受 60 秒超时硬限制,且无法挂起或恢复
MongoDB 事务不是长期运行的会话,而是严格限时的原子单元:transactionLifetimeLimitSeconds 默认为 60 秒(可在 mongod 启动参数中调大,但不推荐)。超时后服务端自动中止并回滚,客户端收不到明确通知,只会遇到 TransactionAborted 或网络中断错误。
常见踩坑点:
- 在事务中执行耗时聚合(
$facet、$lookup多层嵌套)、批量插入(insertMany数万条)、或调用外部 HTTP 接口 —— 极易触发超时 - 事务内不能 sleep、不能阻塞等待用户输入、不能跨 HTTP 请求生命周期维持(比如前端分步表单提交)
- 没有类似 MySQL 的
XA START/END/COMMIT分布式事务协调能力,无法对接外部消息队列或微服务事务
WiredTiger 的 snapshot 隔离不等于 ACID 中的 “I”(Isolation)全覆盖
Snapshot isolation 解决了脏读、不可重复读,但不防止 write skew(写偏斜) —— 这是它和 Serializable 最关键的差距。典型例子:两个事务同时读取账户余额为 100 元,各自扣减 50 元后提交,最终余额变成 50 而非预期的 0。
MySQL InnoDB 在 SERIALIZABLE 模式下会通过间隙锁(gap lock)或谓词锁阻止这类并发写入;MongoDB 没有等价机制,只能靠应用层用唯一索引约束(如 { userId: 1, version: 1 })、或引入分布式锁服务(Redis + Lua)来兜底。
真正容易被忽略的一点:MongoDB 的事务日志(WAL)和数据文件分离管理,崩溃恢复时依赖 journal commit 点对齐。如果关闭 j: true(即禁用 journal),事务的 Durability(D)就无法保障 —— 这和 MySQL 必须开启 innodb_flush_log_at_trx_commit=1 才算真正 durable 是同理,但很多人在线上为了性能误关 journal。


















