3. 副本集架构与容灾基础

MongoDB 的高可用能力来自副本集(Replica Set)。副本集不是简单的“主从复制”,而是一组共同维护同一份数据、通过心跳与选举决定谁可写的 mongod 进程集合。理解 primary、secondary、arbiter 三种角色的职责边界,以及选举、写关注(writeConcern)、读关注(readConcern)如何共同决定数据安全,是后续备份恢复与容灾设计的地基。

三种角色:谁存数据、谁投票、谁可写

副本集默认由至少三个成员组成,典型结构是“一主两从”。Primary 是当前唯一接受写操作的节点,它把客户端写入记录到自己的 oplog(操作日志),secondary 通过持续拉取 oplog 并重放来保持数据一致。Secondary 默认只读(需设置 readPreference 才可读),它既是数据副本,也是选举中的投票者。Arbiter 不保存数据、不参与复制,只参与投票,用于以较低成本凑够奇数票数,但它无法在 primary 故障时接管成为 primary。

这里有一个常被忽视的容灾含义:如果三成员是“一主一从一仲裁”,那么当 primary 宕机,唯一剩下的数据节点 secondary 可以被选举为 primary,但此时整个副本集只剩一份数据,没有任何冗余。因此仲裁者适合“预算有限、能接受短暂单副本风险”的场景,而不适合对数据安全要求高的核心库。生产环境更推荐三数据节点(PSS)或五数据节点结构。

副本集三成员角色与数据流 客户端 写/读请求 Primary 唯一接受写入 写入 oplog 参与投票 Secondary 1 复制 oplog 可读 / 可被选为 Primary Secondary 2 复制 oplog 可读 / 可被选为 Primary Arbiter 不存数据 仅投票,不能成为 Primary 写请求 oplog 复制 oplog 复制 心跳检测(每 2s) 选举触发条件 Primary 失联 / 主动 stepDown 多数派投票,得票过半即当选 优先级高者更易胜出

选举机制:多数派是硬约束

副本集成员之间每 2 秒发送一次心跳。当 secondary 在 electionTimeoutMillis(默认 10 秒)内没有收到 primary 心跳,就会认为 primary 不可用,发起选举。选举采用多数派原则:候选节点必须获得超过总投票成员半数的票数才能成为 primary。三成员副本集需要 2 票,五成员需要 3 票。

这个“多数派”规则直接决定了容灾能力。三成员副本集最多允许 1 个投票成员故障;如果同时宕机 2 个,剩余 1 个节点无法凑够 2 票,副本集会进入只读状态,无法选出新 primary。因此,节点数量与故障容忍度的关系是:容忍故障数 = floor((N-1)/2)。三成员容忍 1 个,五成员容忍 2 个。这也是为什么生产环境常采用五成员或“三数据节点 + 跨机房分布”的原因。

选举还受 priority 影响。priority 高的节点在条件相同时优先当选,可用于把 primary 固定在资源更好的机房。但要注意:priority 只影响“谁更可能当选”,不能突破多数派约束。此外,如果节点数据落后太多(oplog 差距过大),它可能无法被选为 primary,因为其他成员会拒绝为其投票,避免数据回滚风险。

写关注:决定“写成功”的含义

写关注(writeConcern)回答的是:一次写入在什么条件下才算成功返回给客户端。常用取值如下:

  • w:1:只要 primary 写入内存即返回。速度最快,但 primary 宕机且数据未复制时可能丢失。
  • w:"majority":写入被多数投票成员确认后才返回。三成员需 2 个确认,五成员需 3 个确认。这是生产环境推荐值。
  • w:0:不等待任何确认,仅发出即返回,可靠性最低。
  • j:true:要求写入落到 journal 日志后才确认,防止断电丢数据。

写关注与容灾的关系可以用一个判断来概括:w:"majority" 保证“已确认的写入”在多数派中至少存在一份。当 primary 故障、新 primary 从多数派中选出时,已确认的数据不会丢失。而 w:1 只保证 primary 自己收到,一旦 primary 在复制完成前宕机,这部分数据就可能随回滚消失。

读关注:决定“读到什么”

读关注(readConcern)回答的是:读取时能看到哪个阶段的数据。常见级别包括:

  • local:默认值,读取节点当前本地数据,可能读到尚未被多数派确认、未来可能回滚的数据。
  • majority:只读取已被多数派确认的数据,保证读到的数据不会回滚。
  • linearizable:最强一致性,保证读到最新已确认写入,但延迟高、仅适用于单文档读。
  • snapshot:用于多文档事务,读取某个一致快照。

读关注必须与读偏好(readPreference)配合理解。如果使用 readPreference: secondary 从从节点读,同时 readConcern 为 local,就可能读到落后于 primary 的数据,甚至读到之后会被回滚的数据。对账务、库存等场景,应使用 readConcern: "majority" 配合 primary 读,或至少确保读路径不会读到未确认数据。

组合判断:数据安全等级速查

写关注 读关注 典型风险 适用场景
w:1 local primary 宕机可能丢已确认写;可能读到回滚数据 日志、缓存、可容忍丢失的数据
w:"majority" local 写入安全,但读可能读到未确认数据 写多读少、读一致性要求不高的场景
w:"majority" + j:true majority 延迟较高,但已确认数据不丢、不回滚 订单、支付、库存等核心业务
w:"majority" linearizable 延迟最高,仅单文档读 需要强一致读的关键判断
实践建议:核心库统一使用 w:"majority" 与 readConcern:"majority";副本集至少三个数据节点,避免“一主一从一仲裁”承载核心数据;跨机房部署时,把多数派放在主机房,同时评估网络分区下的可用性。
常见误区:认为“有 secondary 就不会丢数据”。实际上,如果写关注是 w:1,secondary 还没来得及复制,primary 就宕机,这部分写入仍会丢失。副本集提供的是“可选举、可恢复”,不等于“零丢失”,零丢失需要写关注与 journal 共同保证。