MongoDB事务实际采用快照隔离,虽官方称Read Committed,但因事务内读取基于一致性快照,可避免不可重复读和脏读,仅无法防止幻读;所有手动设置隔离级别或readConcern的操作均无效。

MongoDB 事务不支持 ANSI SQL 意义上的可重复读(Repeatable Read)隔离级别,它只支持 Read Committed,但底层行为实际是快照隔离(Snapshot Isolation),在多数场景下效果接近可重复读——只是不能防止幻读,也不允许脏读。
为什么 MongoDB 官方说它是 Read Committed,却表现得像可重复读?
MongoDB 4.0+ 的事务由 WiredTiger 存储引擎驱动,默认启用 readConcern: "snapshot"(事务内自动生效,不可更改)。事务开始时会基于 oplog 时间戳获取一个一致性快照,整个事务期间所有读都基于该快照,因此:
- 同一事务内多次
find()查同一批文档,结果一致(避免了不可重复读) - 事务中自己写入的文档,马上能被后续
find()读到(Read Your Own Write) - 其他事务未提交的修改完全不可见(杜绝脏读)
- 但其他事务已提交的新插入文档,仍可能被第二次查询命中(幻读)
试图手动设置隔离级别会怎样?
所有尝试绕过默认机制的操作都会失败或被忽略:
- 在事务中调用
coll.find().readConcern("majority")→ 驱动静默丢弃,不报错但无效 - 显式传入
readConcern: { level: "majority" }给startTransaction()→ 报错:read concern is not supported in transactions - 使用
readPreference调整读节点 → 对事务内读取无影响,只作用于事务外操作 - 执行
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ类 SQL 语句 → 语法错误,MongoDB 不支持该命令
分片集群下幻读更易发生,该怎么应对?
在分片环境中,事务必须路由到同一分片(依赖 shard key 精确定位),否则直接报错。但即使满足该条件,幻读依然存在:
- 事务 A 查询
{ status: "pending" }得到 5 条 - 事务 B 向同一分片插入一条新
pending文档并提交 - 事务 A 再次查询,可能返回 6 条 —— 这不是 bug,是快照隔离的固有行为
- 若业务逻辑强依赖“查询结果集不变”,需在应用层加锁(如用
findAndModify+ 唯一约束)或改用单文档嵌入模型规避多文档事务
真正容易被忽略的是:事务的隔离强度不由你写的代码决定,而由 WiredTiger 快照 + 复制协议共同固化。别在 session.startTransaction() 后折腾 readConcern 或 isolation level 参数——它们不起作用,还可能掩盖真实问题。


















