读关注级别有local、available、majority、linearizable四种;local为默认值,读取当前节点最新数据但不保证复制或持久化;available允许从延迟高或oplog截断的从节点读;majority要求多数节点确认且需journal启用;linearizable仅支持primary读偏好和单文档查询。

读关注(read concern)必须显式设置,否则默认使用 local 级别——这意味着你读到的数据可能尚未复制到其他节点,甚至在故障后丢失。
读关注级别有哪些可选值?
常见且有明确语义的只有四个:local、available、majority、linearizable。其中 majority 是最常被误用也最容易出问题的选项。
-
local:默认值,读取当前节点最新写入的数据,不保证已复制或持久化 -
available:类似local,但允许从延迟较高的从节点读取(比如 oplog 已被截断的节点) -
majority:只返回已被大多数副本集成员确认写入的数据;要求副本集开启journal且写关注支持 majority(即至少有 3 个带投票权的节点) -
linearizable:强一致性读,会阻塞直到确认数据不会回滚,仅适用于primary读偏好 + 单文档查询,且不能用于聚合管道中的非根阶段
在 Go 驱动中怎么设 readConcern?
Go 驱动不支持在连接串里直接指定 readConcernLevel(不像 w 或 readPreference),必须通过代码构造 ReadConcern 实例。
- 全局客户端级:传给
MongoClientSettings.ReadConcern - 数据库级:调用
database.WithReadConcern() - 集合级:调用
collection.WithReadConcern() - 单次操作级:在
FindOne等方法的options.FindOne().SetReadConcern()中传入
示例(设为 majority):
rc := readconcern.Majority()
client, _ := mongo.Connect(context.TODO(), options.Client().ApplyURI("mongodb://localhost:27017").SetReadConcern(rc))
注意:如果副本集只有 1 个节点或未启用 journal,majority 会降级为 local 且不报错——得查日志才能发现。
为什么 readConcern=“majority” 有时查不到刚写入的数据?
这不是 bug,是设计使然。根本原因是:majority 读关注依赖 WiredTiger 的“majority committed snapshot”,而这个快照不是实时更新的。
- 写入后需等待副本集多数节点确认 + journal 刷盘 + 快照推进,才对
majority读可见 - 在高并发写场景下,这个延迟可能达数百毫秒,尤其当从节点负载高或网络抖动时
- 若用
secondary读偏好配majority读关注,会直接失败——因为从节点不维护 majority 快照
验证方式:执行写操作后,立刻用 db.runCommand({ "serverStatus": 1 }) 查看 repl.writtenToMajorityDate 字段,它标出了当前 majority 可见的最新时间点。
容易被忽略的关键约束
读关注不是独立开关,它和读偏好、写关注、副本集配置深度耦合。
-
linearizable要求操作必须路由到 primary,且不能加$lookup或$facet等多阶段聚合 -
majority在 3 节点副本集中实际等价于 “2 节点确认”,但如果其中 1 个是 arbiter(无投票权),就达不到 majority ——得数带votes: 1的节点数 - 使用
readConcern=available时,即使从节点 oplog 已被截断,也能读到部分数据,但结果不可预测
真正决定一致性的从来不是单个参数,而是三者组合:writeConcern 写到哪、readPreference 从哪读、readConcern 读什么版本——漏掉任一环,都可能让应用在扩容或故障时行为突变。











