必须同时配置 readPreference 和 readConcern 才能保证写后立即读到新数据,仅 w: "majority" 仅确保写入落盘多数节点,但不控制读取版本;readConcern: "majority" 才决定可读取的数据版本,且需 enableMajorityReadConcern=true 支持。

必须显式组合 readPreference 和 writeConcern,否则即使 w: "majority" 也读不到刚写入的数据。
为什么 w: "majority" 本身不能保证读到新数据
写入时用 w: "majority" 只表示“写操作已确认落盘到大多数节点”,但读请求默认仍走 readPreference: "primary" —— 而 primary 节点可能还没把 oplog 应用完,或者你读的是另一个 secondary(没配 readPreference 时 Driver 行为不固定)。更关键的是:readConcern: "majority" 才控制“能读到什么数据”,它和 readPreference 是正交的两个配置,缺一不可。
常见错误现象:
- 写入后立刻
find()返回旧值,哪怕用了w: "majority" - 在副本集里切换节点读取时结果不一致,怀疑数据丢失
- 事务提交后,另一客户端查不到刚插入的文档
必须同时设置 readPreference 和 readConcern
readPreference 决定从哪个节点读,readConcern 决定从该节点读到什么版本的数据。二者配合才能闭环:
- 若要“写后立刻读到”,必须用
readPreference: "primary"+readConcern: "majority"(或"local",但后者有回滚风险) - 若用
readPreference: "secondary",则必须配readConcern: "majority",否则可能读到未同步到多数节点的脏数据 -
readConcern: "majority"要求副本集开启enableMajorityReadConcern=true(启动参数或 config 配置),否则会静默降级为"local" - Node.js Driver 示例:
const client = new MongoClient(uri, { readPreference: 'primary', readConcern: { level: 'majority' } });
writeConcern 级别选错会导致读一致性失效
w: 1 或 w: "majority" 不是随便选的 —— 它直接决定 readConcern: "majority" 是否有效:
-
w: 1:只等 primary 确认,secondary 可能完全没收到写入,此时readConcern: "majority"会返回空或旧值(因为 majority 还没看到该写入) -
w: "majority":确保写入已复制到多数节点,readConcern: "majority"才能安全读到它 -
wtimeout必须设合理值(如2000),否则超时后写入可能成功但报错,造成应用层误判 - 事务内必须显式传
writeConcern,继承自 client 或 database 的设置在 session 中不自动生效
容易被忽略的部署前提
很多问题其实卡在底层配置,不是代码写错:
- 副本集必须初始化且状态正常(
rs.status().members[n].stateStr === "PRIMARY") -
enableMajorityReadConcern必须为 true,MongoDB 4.4+ 默认开启,但升级或手动部署时可能漏掉 - 如果用分片集群,
readConcern: "majority"要求所有 shard 和 config server 都支持 majority(即都运行 3.2+ 且启用该选项) - Driver 版本太低(如 Node.js readConcern 参数,会静默忽略
真正难的不是配参数,而是确认整个链路——从写入触发、oplog 复制、secondary 应用、到读请求路由和视图裁剪——每一步都得对得上。漏掉任意一环,强一致性就变成幻觉。











