深蓝黑背景封面:左侧三角结构示意图——顶部青色卡片「Primary 主节点」,标注「所有写请求入口」;底部左右两张紫色卡片「Secondary 从节点」,标注「oplog 异步复制」。从 Primary 分别向两个 Secondary 画出青色实线箭头,箭头旁标注「oplog」;两个 Secondary 之间一条灰色虚线,标注「心跳」。示意图下方两行小字:主节点宕机时从节点投票选出新主,切换自动完成;客户端重连后写请求继续。右侧大标题「MongoDB 副本集,凭什么高可用」,副标题「自动选主 + oplog 复制,主挂了自己换」。左上标注「面试经典 · 数据库」,右上圆角胶囊标注「Day 06 · 11-13」 图 1:本文封面,主从复制与自动选主的整体结构。

先说明属性:这是一篇经典题解析,题目出自本地历史面试资料,与任何公司的当前招聘无关。「MongoDB 副本集怎么做到高可用」一句话的答案是「自动选主加 oplog 复制」,但面试官真正想听的是这两件事怎么配合:节点各扮演什么角色、主挂了之后按什么顺序换、写和读的一致性分别由哪个旋钮控制。

三种角色:主节点、从节点、仲裁者。

副本集是一组维护同一份数据集的 mongod 进程。主节点(Primary)是唯一接收写请求的节点,所有写操作先落在主节点,并记录进它的 oplog(操作日志)。从节点(Secondary)异步复制这份 oplog 并重放,维持与主节点一致的数据副本;客户端默认从主节点读,也可以把读流量分担给从节点。仲裁者(Arbiter)是可选角色:只参与选举投票,不存数据,也永远不能成为主节点。官方推荐的最小配置是三个数据节点(一主两从);一主一从且成本受限时,才加仲裁者凑票数。整个副本集最多 50 个成员,但有投票权的最多 7 个。

oplog:数据追平的传送带。

复制链路本身很简单:主节点把每个写操作追加到 oplog,从节点不断拉取并应用到自己的数据集。关键在「异步」两个字——从节点不是实时镜像,而是持续追平的副本。这带来两个直接推论:主节点宕机的瞬间,某些刚写入的数据可能还没复制到任何从节点;以及从从节点读,读到的可能是稍旧的数据。这两个推论,正好解释了后面两个一致性旋钮为什么存在。

故障转移:四步完成自动换主。

成员之间每两秒互发一次心跳;某个节点的心跳超过十秒没有响应,其他成员就把它标记为不可达。主节点失联后的流程是:第一步,心跳超时触发选举;第二步,有投票权的成员投票,拿到多数票的从节点成为新主——候选者必须拥有可见成员中最新的 optime,保证新主的数据不比任何人旧;第三步,新主开始接收写请求;第四步,旧主恢复后降级为从节点,追平落后期间的 oplog。官方文档给出的参考值是:默认配置下选出新主的中位时间通常不超过 12 秒,这段时间副本集不能处理写,但配置允许的话读可以继续。还有一个隐蔽细节:网络分区把主节点隔在少数派一侧时,它发现自己看不到多数派就会主动降级,避免「两个主」同时收写。多数派(majority)是整套机制的基石——3 个投票成员的多数派是 2,这正是节点数要凑奇数、或用仲裁者补票的原因。

两个一致性旋钮:写关注与读偏好。

写关注(Write Concern)管「写到什么程度才算成功」。w 等于 1 表示主节点确认就返回,最快,但主节点此刻宕机而数据又没复制出去,这条写入之后可能被回滚;w 等于 majority 要等多数数据节点确认才返回,更稳也更慢。官方文档还提醒,一主一从一仲裁者的拓扑里,majority 实际上要求全部两个数据节点都在线,任何数据节点挂掉都可能写不进去,所以生产更推荐三个数据节点。读偏好(Read Preference)管「从哪里读」:默认 primary 只读主节点,保证读到最新;secondary 系列把读压分担给从节点,代价是可能读到旧副本——官方明确说,除 primary 之外的所有读偏好都可能返回过期数据。调旋钮的本质,是在延迟、吞吐与一致性之间找业务能接受的点。

深蓝黑背景。顶部小字「概念图 · 副本集高可用」,大标题「自动选主 + 数据追平」。左侧大卡片「三种节点角色」:顶部 Primary 卡片标注「接收全部写请求」,向下两条青色 oplog 箭头指向两个 Secondary 卡片「复制并应用 oplog」;底部一张灰色虚线卡片「Arbiter 仲裁者(可选):只投票,不存数据」;卡片底部注明奇数节点或加仲裁者可保证选举拿到多数票。右上方卡片「故障转移四步走」以 2×2 排列四个编号步骤:① 主节点宕机(心跳检测中断);② 心跳超时(默认约 10 秒触发选举);③ 多数派投票(从节点选出新主);④ 旧主恢复(降为从节点,追平 oplog)。右下方卡片「一致性的两个旋钮」:写关注 Write Concern,w=1 主节点确认即返回、快但可能丢数据,w=majority 多数节点确认才返回、更稳;读偏好 Read Preference,primary 只读主节点保证强一致,secondary 系列分担读压但可能读到稍旧副本。底部通栏记法:分片解决「装得下」,副本集解决「挂不了」——一个管扩展,一个管高可用 图 2:角色、故障转移时序与两个一致性旋钮的全景。

一句话记住它与分片的分工。

同批的分片题讲「装得下」(水平扩展),这篇的副本集讲「挂不了」(高可用):分片把数据切开摊到多台机器,副本集把同一份数据复制到多台机器。一个管容量,一个管容错,真实集群里两者经常叠加。

回到面试现场,建议的答题结构:先讲三种角色与 oplog 异步复制这条主线,再按「心跳超时、多数派选举、旧主降级追平」复述故障转移时序,最后主动补上写关注与读偏好两个旋钮,说清各自牺牲什么、换来什么。时序背对是及格线,能把「异步复制」和两个旋钮的因果串起来才是加分项。你的线上业务用的是 w 等于 1 还是 majority?有没有为读偏好交过「读到旧数据」的学费?欢迎分享。