readconcern: "majority" 仅在写操作使用 writeconcern: { w: "majority" } 且读写均在同一因果一致会话中时,才能保证因果一致性;否则可能读到回滚数据。

readConcern 不是开箱即用的一致性开关,它必须配合写操作的 writeConcern 和客户端会话机制才能生效。单独设置 readConcern: "majority" 无法保证“查到刚写入的数据”,尤其在副本集故障、网络分区或并发读写场景下。
什么时候 readConcern: "majority" 才真正起作用?
只有当读和写都满足以下两个条件时,readConcern: "majority" 才能提供因果一致性(即“读己所写”“单调读”等):
- 写操作使用了
writeConcern: { w: "majority" }(不是w: 1或w: 0) - 读写操作发生在同一个因果一致的客户端会话(
startSession()创建的会话)中
缺一不可。例如,用 Node.js 驱动写入时没传 writeConcern,或用了默认 w: 1,那即使读端设了 "majority",也有可能读到旧值,甚至读到之后被回滚的数据。
readConcern: "local" 是默认值,但它不保证持久性
绝大多数 MongoDB 部署(包括单机、副本集、分片集群)默认使用 "local" 读关注。它的行为是:
- 从当前节点(主或从)返回最新已应用的数据,不管是否同步到多数节点
- 该数据可能在后续故障转移中被回滚(比如写入只到达主节点,主挂了,新主没拿到该写入)
- 性能高、延迟低,适合对一致性要求不敏感的报表、缓存填充等场景
注意:"local" 在事务中允许使用,但显式创建集合/索引时强制要求它——这是个容易被忽略的限制。
如何在不同层级设置 readConcern?优先级很关键
设置位置有四个层级,按优先级从低到高为:客户端 → 数据库 → 集合 → 操作(如 find() 或 aggregate())。高优先级会覆盖低优先级:
- 连接 URI 中加
&readConcernLevel=majority是客户端级默认(Node.js 驱动支持) -
db = client.db("mydb", { readConcern: { level: "majority" } })是数据库级,覆盖客户端设置 -
collection = db.collection("users", { readConcern: { level: "majority" } })是集合级 - 单次查询中直接传参:
collection.find({}).readConcern({ level: "majority" })(部分驱动支持链式调用)
别在客户端设 "majority" 却又在某个慢查询里手动降级成 "local"——这会破坏因果一致性链条,且难以追踪。
因果一致性会话必须显式开启,且线程不能复用
即使读写都用了 "majority",如果不启用因果一致会话,MongoDB 不会维护逻辑时钟(Lamport clock)和操作时间戳,也就无法排序事件顺序:
- Node.js 中必须调用
client.startSession({ causalConsistency: true }) - 每个会话应由单一逻辑线程使用;多线程共享同一会话对象会导致时间戳错乱
- 会话需显式传递给事务或带
readConcern/writeConcern的操作,例如session.withTransaction(...)或collection.findOne(..., { session })
最容易被忽略的是:会话对象本身不携带 readConcern,它只是载体;readConcern 和 writeConcern 还得分别设在操作或事务选项里——两者缺一,因果链就断了。











