MongoDB事务采用快照隔离(SI)而非可重复读(RR),通过atClusterTime绑定事务快照实现一致性视图,幻读仍可能发生;脏读被避免是因默认readConcern:"majority"与MVCC快照机制共同作用,非传统RC语义。

MongoDB 事务不支持可重复读(RR)及以上隔离级别,无法防止幻读;脏读在已提交读(RC)下被自动避免,但这是靠写锁 + 快照读实现的“类 RC”,不是传统 SQL 的语义。
为什么 MongoDB 事务查不到其他事务插入的新文档(幻读看似不存在)?
MongoDB 使用基于时间戳的多版本并发控制(MVCC),每个事务启动时绑定一个集群范围的逻辑时间戳(atClusterTime),读操作只看到该时间戳前已提交的数据。新插入的文档若在当前事务快照时间戳之后提交,就不会被读到——这看起来像防止了幻读,但本质是快照隔离(SI),不是真正的可重复读。
关键点:
- 幻读仍可能发生:如果事务 A 先查一次
find({status: "pending"})得到 5 条,事务 B 插入 1 条并提交,事务 A 再次执行相同查询(仍在同一事务内),结果仍是 5 条——这不是“防止幻读”,而是“快照固化”;但如果事务 A 在第二次查询前显式调用session.advanceClusterTime()或使用readConcern: "majority"以外的配置,行为可能变化 -
readConcern: "majority"是默认值,它保证读到已多数提交的数据,但不提供跨语句的一致性视图;真正影响幻读感知的是atClusterTime绑定时机,而非 isolation level 设置 - 没有
SELECT ... FOR UPDATE或间隙锁,所以无法阻塞其他事务插入“符合 WHERE 条件”的新文档
脏读在 MongoDB 事务里真的不会发生吗?
不会。MongoDB 在事务中默认使用 readConcern: "local"(内部事务协调器强制升级为 "majority"),且所有读操作都基于事务开始时确定的快照,未提交的写入对其他事务完全不可见。即使你手动设 readConcern: "local",副本集主节点上的未提交事务也不会对外暴露其修改。
但要注意这些边界情况:
- 非事务性读(即没进
withTransaction()或没显式startTransaction())不受保护,可能读到回滚中的数据(如果readConcern: "local") - 跨分片事务中,若某个分片故障导致部分提交,客户端可能观察到短暂不一致(虽有回滚机制,但窗口期存在)
- 使用
readPreference: "primaryPreferred"并发读取时,如果主节点切换,新主可能还没同步完旧主的事务状态,造成瞬时脏读风险(极低,但非零)
MongoDB 的 readConcern 和 writeConcern 怎么配合影响隔离效果?
readConcern 控制“读什么”,writeConcern 控制“写到哪”,二者共同决定事务可见性边界。它们不是独立开关,而是协同生效:
-
readConcern: "majority"+writeConcern: {w: "majority"}:最常用组合,确保读到已被大多数节点确认的写入,也是事务默认行为 -
readConcern: "linearizable":仅适用于单文档读,要求读操作等待所有先前写完成,延迟高,不能用于多文档事务 -
readConcern: "snapshot":仅限于事务内部有效,它启用真正的快照隔离(SI),但 MongoDB 从 4.0 起已将事务默认设为此模式,无需显式指定 - 若
writeConcern过弱(如w: 1),而readConcern: "majority"却开启,可能导致读不到刚写的文档(因为多数节点还没收到)——这不是隔离问题,是写入持久化与读取可见性的错配
实际开发中哪些操作会意外破坏事务一致性?
最容易被忽略的是跨事务上下文的操作和驱动层误用:
- 在事务中调用
collection.findOneAndUpdate()时没传session参数 → 操作脱离事务,变成独立写,可能引发部分更新、丢失修改 - 使用
db.runCommand({commitTransaction: 1})手动提交,但没检查返回结果中的errorLabels(如TransientTransactionError),导致重试逻辑缺失 - 在事务函数内发起 HTTP 请求或访问外部缓存,然后基于响应结果再写数据库——此时外部依赖的延迟或失败会让事务语义失控
- 聚合管道中用了
$lookup关联其他集合,但目标集合不在同一分片键上 → 跨分片事务不支持此操作,会直接报错MultiDocumentTransactionNotSupportedOnShardedCluster
事务的“原子性”只覆盖明确加入 session 的操作,其余一切都不受保护。MVCC 提供的是读一致性基础,不是魔法屏障。











