图 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 之外的所有读偏好都可能返回过期数据。调旋钮的本质,是在延迟、吞吐与一致性之间找业务能接受的点。
图 2:角色、故障转移时序与两个一致性旋钮的全景。
一句话记住它与分片的分工。
同批的分片题讲「装得下」(水平扩展),这篇的副本集讲「挂不了」(高可用):分片把数据切开摊到多台机器,副本集把同一份数据复制到多台机器。一个管容量,一个管容错,真实集群里两者经常叠加。
回到面试现场,建议的答题结构:先讲三种角色与 oplog 异步复制这条主线,再按「心跳超时、多数派选举、旧主降级追平」复述故障转移时序,最后主动补上写关注与读偏好两个旋钮,说清各自牺牲什么、换来什么。时序背对是及格线,能把「异步复制」和两个旋钮的因果串起来才是加分项。你的线上业务用的是 w 等于 1 还是 majority?有没有为读偏好交过「读到旧数据」的学费?欢迎分享。