MongoDB 4.2 分布式事务依赖两阶段提交(2PC)保障ACID,但隔离性最脆弱:snapshot读关注易因分片延迟退化为local,导致读到未提交中间态或跳过已提交变更。
MongoDB 4.2 的分布式事务**能保证 ACID,但有严格前提和明显代价**:它依赖两阶段提交(2PC),所有参与分片必须全程持有文档锁,且隔离级别必须设为 snapshot、写关注必须为 { w: "majority" }。不满足这些,所谓“ACID”只是假象。
MongoDB 没有中心事务协调器,跨分片操作必须靠一种可收敛的分布式协议。2PC 是它在无全局时钟、无 Paxos 类共识引擎前提下的务实选择——prepare 阶段让各分片预写日志并锁定文档,commit 阶段再统一确认生效或全部回滚。
prepare 不是轻量试探:它真实修改数据(WiredTiger 内部事务日志已刷盘),只是对外不可见;失败后需走完整回滚流程,不是“撤销预占”txnNumber 必须单调递增且对齐,否则直接报 TransactionTooOld;logicalSessionCacheRefreshPeriodMS 默认 5 分钟,生产建议压到 30 秒transactionLifetimeLimitSeconds(默认 60 秒)超时触发自动 abort**隔离性(Isolation)最容易被打破,且业务最难感知**。默认 readConcern: "snapshot" 要求所有读节点都已同步 oplog 到同一时间点,一旦某个分片延迟 > 100ms,snapshot 就退化为 local —— 你读到的可能是未提交的中间态,或跳过刚提交的变更。
writeLock,哪怕只读也要等锁释放(因 snapshot 需构建一致视图),高并发下极易排队$lookup 引用其他库,也不能操作 system.* 或 config 库,否则直接 InvalidNamespace
maxTimeMS 只约束 coordinator 本地执行,不覆盖网络往返和 prepare 响应延迟;实际事务 hang 住十几秒才失败很常见即使代码里写了 session.startTransaction(),以下任一配置缺失都会让事务降级为“多个独立写操作”,完全失去原子性和一致性保障:
replication.enableMajorityReadConcern: true → snapshot 不可用,事务强制退化为 local 隔离writeConcern: { w: "majority" } → 提交后可能仅写入主节点,从节点宕机即丢数据db1.col1 和 db2.col2)→ 触发 InvalidNamespace,根本进不了 2PC 流程真正难的不是“怎么写事务”,而是判断“这笔业务到底该不该放进来”。prepare 阶段耗时占比常超 60%,一个慢查询或一次网络抖动就能拖垮整个两阶段流程。别迷信 commitTransaction() 的返回成功——先看 P99 prepare 延迟,再谈 ACID。