read concern "majority" 在事务中不自动生效,必须显式配置 writeconcern: {w: "majority"} 才能触发其语义;否则事务内读操作可能看到未确认、可回滚的数据。

Read Concern "majority" 在事务中不自动生效,必须显式配置 writeConcern: {w: "majority"} 才能触发其语义;否则事务内读操作可能看到未确认、可回滚的数据。
事务内 readConcern: "majority" 本身不保证数据已多数写入
很多人误以为只要设置了 readConcern: "majority",事务里的读就能看到“已落盘到多数节点”的数据——这是错的。MongoDB 要求:事务提交时必须使用 writeConcern: {w: "majority"}(默认即如此),readConcern: "majority" 才在事务上下文中起作用。否则,即使你在 startTransaction() 里传了 {readConcern: {level: "majority"}},它也只影响该事务内读操作的快照起点,不绑定 oplog 多数确认状态。
常见错误现象:
- 事务用
writeConcern: {w: 1}提交,随后用readConcern: "majority"查询,仍可能读到旧值或报错ReadConcernMajorityNotAvailableYet - 跨分片事务中某分片未开启
featureCompatibilityVersion: "4.2",导致 majority 读关注被静默降级为"local"
commitTransaction 成功 = oplog 已写入 majority 节点,但不等于数据已重放完成
事务提交返回成功,只表示该事务所有操作对应的 oplog 条目已被写入并确认到副本集大多数可投票节点(如 3 节点集需 2 个)。Secondary 上的数据重放是异步、单线程串行执行的,中间存在延迟窗口。
这意味着:
- 你立刻在 Secondary 上执行
db.collection.find().readConcern("majority"),仍可能返回上一个稳定快照的值 - 判断是否已重放完成,得看节点本地的
lastStableOpTime是否 ≥ 事务 commit 时的clusterTime,而不是看lastAppliedOpTime - 大事务(比如批量更新 10 万文档)会让重放延迟更明显,尤其在磁盘慢或 CPU 高的 Secondary 上
跨地域副本集下 majority 语义容易失效
当副本集跨机房部署(如北京 2 节点 + 广州 2 节点 + 上海 1 节点),网络分区会导致 Majority 计算失真:心跳未超时前,隔离节点仍被计入 majority,但实际无法通信。
此时:
-
rs.status().members中 health 为 1 且stateStr === "SECONDARY"的节点才真正参与 majority 判定 - 隐藏节点(
hidden: true)、延迟节点(slaveDelay > 0)默认不计入 majority,除非显式设votes: 1 - 仲裁节点(Arbiter)能凑数达成 majority,但它不存数据——事务成功提交不代表有足够数据冗余
事务中不能混用 readConcern "majority" 和非 majority 写操作
事务是一个原子单元,所有写操作共享同一套 writeConcern。如果你在事务中调用 insertOne() 时额外传 {writeConcern: {w: 1}},MongoDB 会直接报错 Cannot override write concern in transaction。
所以必须统一在 startTransaction() 时指定:
- 必须用
writeConcern: {w: "majority"}(或等价的{w: 2}等数值) - 若要容忍短时网络抖动,加
wtimeout,例如{w: "majority", wtimeout: 5000} - 别指望靠
j: true强制刷盘来提升一致性——它只控制 journal 写入,不影响 majority 判定逻辑
最常被忽略的一点:事务生命周期内,所有读写都绑定在同一个 session 的 causal consistency 上;一旦 session 断开或超时(默认 transactionLifetimeLimitSeconds=60),还没 commit 的事务自动 abort,之前的所有 majority 写入也作废——不是回滚,是从未发生过。











