MongoDBNotes

第 20 章:一致性与读关注

zjc 于 2026-01-20 发布

这是《MongoDB 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开,代码块保留了原书可直接运行的版本。 MongoDB 的一致性由写关注、读关注和读偏好共同决定。理解三者组合,才能回答“这条数据能不能读到”“读到的是否是多数已提交”“是否可能比 Primary 旧”。

20.1 三层控制

机制 问题
Write Concern 写到多少节点才返回成功
Read Concern 读哪个一致性级别的数据
Read Preference 从哪个成员读

示例:

db.orders.find(
  { user_id: "u_1001" }
).readPref("primary").readConcern("majority")

20.2 Write Concern

写关注:

db.orders.insertOne(
  { _id: "o_1", status: "CREATED" },
  { writeConcern: { w: "majority", j: true, wtimeout: 3000 } }
)
参数 说明
w: 0 不等待确认,风险高
w: 1 Primary 确认
w: "majority" 多数成员确认
j: true 等待 journal
wtimeout 超时时间

重要数据建议 majority;日志、行为事件可按成本选择,但要有补偿和对账。

20.3 Read Concern

常见级别:

级别 语义
local 返回本节点已应用的数据
majority 返回多数节点已提交的数据
linearizable 强一致线性读
snapshot 事务快照
available 可用性优先,分片场景语义需注意

local 可能读到后续回滚的数据;majority 降低回滚读风险;linearizable 延迟和限制更高。

20.4 Read Preference

模式 行为
primary 只读 Primary
primaryPreferred 优先 Primary
secondary 只读 Secondary
secondaryPreferred 优先 Secondary
nearest 最低延迟成员

Secondary 读不等于弱一致,最终语义还要看 read concern;但复制延迟会让数据版本较旧。

20.5 因果一致性

客户端会话可以维护因果令牌,让后续操作看到之前操作的影响:

const session = db.getMongo().startSession({ causalConsistency: true });
const orders = session.getDatabase("shop").orders;

orders.insertOne({ _id: "o_1", status: "CREATED" });
orders.find({ _id: "o_1" });

session.endSession();

适用:

  1. 写后读;
  2. 会话内单调读;
  3. 分页延续;
  4. 多步配置变更。

因果一致性不等于全局线性一致。

20.6 写后读场景

支付后立即查询:

write majority
  -> read primary + majority

配置:

db.orders.insertOne(
  { _id: "o_1", status: "PAID" },
  { writeConcern: { w: "majority" } }
)
db.orders.find({ _id: "o_1" }).readPref("primary")

如果使用 Secondary 读,用户可能看不到刚支付的状态,需要产品提示或强制 Primary。

20.7 报表读取

报表可以读 Hidden Secondary:

read preference: secondary
read concern: majority

特点:

  1. 不影响主业务 Primary;
  2. 可能滞后;
  3. 重查询仍会影响该 Secondary;
  4. 应有资源隔离和超时;
  5. 跨分片聚合由 mongos 合并。

20.8 分片一致性

分片下还要注意:

  1. 不带分片键的查询可能读取多个 shard;
  2. 各 shard 复制进度可能不同;
  3. majority 读降低回滚风险;
  4. 事务需要快照和提交协调;
  5. mongos 拓扑状态影响路由。

对账和全局报表应明确时间点。

20.9 一致性权衡

需求 建议
支付状态 primary + majority
用户资料 primary 或 secondaryPreferred
报表 secondary + majority
推荐缓存 nearest + local
审计追踪 majority 写入
跨服务流程 业务状态机 + 对账

不要为所有请求使用最高一致性,也不要默认所有 Secondary 读都能接受。

20.10 超时与重试

写关注超时后,写入可能已经发生:

timeout -> unknown result

处理方式:

  1. 保存请求 ID;
  2. 查询业务状态;
  3. 幂等重试;
  4. 记录不确定结果;
  5. 告警;
  6. 不盲目重复不可幂等写入。

本章小结

一致性不是单一参数,而是写关注、读关注和读偏好的组合。核心交易使用 majority 与 Primary 读,分析报表可以读 Secondary,但仍要定义滞后容忍度。所有超时都要按“结果未知”处理。

思考题

  1. w: 1w: majority 的核心差异是什么?
  2. local 读可能读到什么风险数据?
  3. Read Preference 是否决定数据提交级别?
  4. 因果一致性适合什么场景?
  5. 写关注超时后为什么不能直接认定失败?