资讯动态

MongoDB Replica Set中Arbiter的选票真相:搭建、容错与故障演练

发布时间:2026/10/14 14:18:06 来源:尧图企业网站定制
“有个关系还不错的开发者前阵子找我帮忙看一套 MongoDB 高可用方案他说自己把生产环境从单节点迁到了 Replica Set用的是两台数据节点加一台 Arbiter。我听完第一反应不是‘稳不稳’而是‘你先告诉我这台 Arbiter 放在哪儿了’。很多人一看 MongoDB 使用 Arbiter 搭建 Replica Set就自动带入‘省钱版三节点高可用’这个定位但 Arbiter 本质上是投票补位不是数据副本用对了能少买一台全量存储机器用错了会在你最需要它的时候让故障切换直接失效。这篇文章我会从复制集原理讲起把 Arbiter 的角色、适用场景、完整搭建步骤、故障演练以及各种坑一次性说清楚适合正在设计 MongoDB 高可用方案的 DBA、后端开发和架构师阅读。”1. 先搞懂 Replica Set 是怎么高可用的1.1 单点故障的痛和一份省钱版的解法MongoDB 早期部署最常见的形态是单节点跑一个 mongod应用连接它就完事了。这种模式在开发环境毫无问题但一旦上了生产就会遇到几个绕不开的坎机器重启的时候写入被打断磁盘损坏直接丢数据半夜出现故障时没有自动恢复机制需要人肉切流量甚至从备份重建。Replica Set 解决的就是这些问题它的思路本质上是把同一份数据在多个节点上各保留一份节点之间通过心跳互相感知状态一旦主节点不可用其余节点会自动选出一个新的主节点顶上客户端不需要手工改配置。但这里有一个容易被忽略的细节高可用是分等级的。三个全量数据节点组成的 Replica Set数据在同一时刻至少存在三个完整副本任何一个节点宕机剩余两个节点依然有完整的读写能力而两台数据节点加一台 Arbiter 的副本集数据实际只冗余了两份。Arbiter 不存数据它只参与投票所以这种架构被很多人叫作“省钱版三节点方案”——花两台全量机器的钱拿到三节点的投票资格。这不是坏事前提是你清楚这个“资格”能换来什么、换不来什么。大多数团队选择 Arbiter 的动机很实际存储资源很贵尤其是使用云盘或高配 SSD 的时候第三个全量节点的成本可能比一台普通虚机高出数倍而 Arbiter 只需要很低的内存和磁盘就能运行。这个动机本身没问题但很多人忽略了一个前提——Replica Set 的高可用能力来自“大多数投票成员可达”而数据安全性来自“大多数数据节点收到写入”。Arbiter 能帮你凑够投票大多数却不会帮你多存一份数据这两个概念从设计开始就要分开。1.2 节点成员类型不是所有节点都在存数据Replica Set 里并不只有主节点和从节点两种角色。在配置层面每个成员可以被赋予不同的优先级、投票权和隐藏属性从而适配不同的部署意图。为了讲清楚 Arbiter 的位置我先列一下常见的成员类型。成员类型是否存储数据是否参与投票priority 默认值典型用途Primary是是1处理全部写入默认读取Secondary是是1数据冗余可承担读流量Arbiter否是0只参与选举投票不存数据Hidden Secondary是可选0备份、报表分析客户端不可见Delayed Secondary是可选0延迟同步用于误操作恢复从这张表就能看出来Arbiter 的定位非常特殊它不落数据不提供读服务不参与同步唯一存在价值就是在选举的时候投出自己那一票。它的 priority 必须是 0意味着它永远不会被选举为 primary它的投票权默认为 1意味着当集群需要对新的主节点做出裁决时它的声音和全量数据节点一样有效。这一点常常是新手的认知分界线。有人会问既然它不存数据那两台数据节点加一台 Arbiter和两台数据节点有什么区别区别就在选举。两台数据节点发生故障时只剩一台机器在线它只有一票在默认的多数派规则下永远凑不齐两张票无法被提升为新的 primary而有一个 Arbiter 在线时剩余这台数据节点加上 Arbiter 正好两张票可以合法完成选举。Arbiter 的价值是数学意义上的票数补位而不是物理意义上的数据副本。1.3 选举、心跳与多数派高可用的底层协议Replica Set 的自动故障切换不是靠某个外部调度器而是靠节点之间的心跳和选举协议。默认情况下副本集成员每 2 秒互相发送一次心跳如果一个节点超过选举超时时间默认大约 10 秒没有收到 primary 的心跳它就会认为自己所在的副本集可能失去了主节点并发起一轮选举。选举采用类 Raft 的多数派规则候选节点需要获得副本集中所有投票成员的大多数选票才能成为新的 primary。多数派规则可以通过一个日常例子解释三个人开投票会任何决议至少需要两票通过两个人私下商量好就能拍板但一个人无论如何无法代表整体。数据库里的选举也是这个逻辑只不过它要防的不是“代表整体”而是“同一时间出现两个主节点”。两个节点各自认为对方已死同时把自己变成 primary客户端写入就会分成两路数据会逐渐分叉之后想合并都找不到依据。多数派规则保证在任何网络分区下最多只有一个分区能凑齐大多数票从而只有那一个分区能合法产生 primary。除了节点之间的内部协议客户端写入也有对应的关注机制。默认的写入关注是 w:1即只要 primary 确认写入就算成功如果你想在副本集层面确认数据已经复制到大多数投票成员可以显式配置 w:majority。这个参数在带 Arbiter 的副本集里有一个容易被忽略的坑我后面会在故障演练部分专门展开。2. Arbiter 到底解决了什么问题2.1 投票平局与奇数节点的数学道理为什么 MongoDB 官方经常建议生产环境至少有 3 个投票成员核心原因是多数派规则要求“过半”所以投票成员总数最好是奇数。两个投票成员的副本集有一个天然缺陷如果其中一个节点宕机剩余节点无论怎么投票都只有一票而多数派阈值是两票永远选不出主节点整个集群实际上失去了写入能力。两台数据节点加一台 Arbiter 之后投票成员总数变成 3多数派阈值变成 2。这意味着两个数据节点中任意一个宕机时只要 Arbiter 还活着另一个数据节点就能拿到两票完成选举。这个算术可以从另一个角度解读三节点副本集的容错能力是“同时允许一台投票成员不可用”两数据节点加一个 Arbiter 也拥有同样的容错能力区别只是把第三份数据副本替换成了无数据的仲裁票。这种替换的代价需要被明确认知。三个全量数据节点允许任意一台机器完全损坏而不丢数据因为另外两台还有完整副本而两数据节点加 Arbiter 的架构不允许两台数据节点同时损坏因为此时即使 Arbiter 还活着也只有一个数据副本残存数据完整性完全取决于那台幸存机器的磁盘状态。Arbiter 提供的是选举层面的可用性不是数据层面的冗余这个边界必须刻在脑子里。2.2 什么时候该用 Arbiter什么时候应该果断放弃根据我的实际经验下面几种场景是 Arbiter 的正確使用方向预算有限但业务对写入连续性有要求希望至少能扛住单台数据节点故障。例如主从两节点配合一个轻量仲裁节点可以在主节点宕机时自动切换到从节点避免人工干预。开发或测试环境需要模拟三节点副本集的故障转移行为。用两台开发机加一个容器跑 Arbiter成本和复杂度都远低于再申请一台全量数据节点。现有副本集已经是偶数个数据投票节点例如 4 个数据节点想用 Arbiter 把投票节点凑成奇数降低选举时出现平票的概率。反过来以下几种情况我会明确建议不要用 Arbiter业务无法容忍任何数据节点故障期间出现“只有一份数据副本”的状态。如果两个数据节点之一宕机后剩下的唯一数据节点再出问题就会导致数据丢失那么应该使用三个全量数据节点。系统写入量很大且你希望每个数据节点都承担冗余。Arbiter 不承接同步负载对数据复制能力没有任何帮助。节点数量已经是奇数且全量数据例如已经是三个数据节点时再加 Arbiter 并不会带来额外的高可用收益反而增加维护点。Arbiter 适用的核心条件是“你缺的不是数据副本而是选票”。2.3 双机房与三地点部署的讲究部署位置是 Arbiter 架构里最容易翻车的地方。很多人图省事把 Arbiter 和 primary 放在同一个服务器或同一个机柜这等于在最关键的故障时刻把 Arbiter 和 primary 一起拖下水。如果主节点所在机房发生整段网络故障Arbiter 也会同时失联它那票就废了剩余数据节点只有一票无法完成选举高可用效果归零。更值得注意的场景是双机房部署。假设机房 A 放 primary 节点机房 B 放 secondary 节点Arbiter 放在机房 A。如果两个机房间的网络发生分区机房 A 内部 primary 和 Arbiter 仍然互通它们一共有两票超过多数派阈值primary 会继续保持主节点身份而机房 B 的 secondary 由于只能拿到自己的一票永远不可能被提升。这种情况下业务表面上没有中断但写入只发生在机房 A机房 B 的数据副本在分区期间停更一旦 A 机房整体损坏B 机房的数据滞后会造成数据丢失。所以如果只能在两个机房之间选一个放 Arbiter应该优先把它放在故障域更独立、网络更稳定的位置或者干脆把 Arbiter 放在第三个低延迟地点让它在两个数据机房发生分区时保持中立和可达。3. 搭建实操从两台虚机加一台小机器开始3.1 环境规划与版本选择我下面的搭建步骤以 MongoDB 6.x/7.x 为基础配置语法在 4.x 之后基本兼容所以如果你用的是 5.0 或 8.0核心流程也可以照搬。规划时准备三台 Linux 虚拟机或物理机假设主机名分别是 node1、node2、arbiter。node1 和 node2 配备正常的数据盘arbiter 只需要很小的磁盘空间因为它的数据目录基本是空的。角色主机名端口资源配置建议Primary 候选node1270172C4G 或以上数据盘按业务量估算Secondary 候选node227017与 node1 对齐Arbiterarbiter270171C1G 足够磁盘 5G 以上即可三台机器之间要确保 27017 端口可以互通防火墙和云安全组需要放行。生产环境必须启用认证但为了让你先把架构跑通下面的步骤先以开放认证的方式展开我会在文末的安全章节单独说加认证的要点。3.2 一份可复制的三节点配置MongoDB 的节点角色不是从配置文件里强行指定的而是通过复制集初始化命令决定的。每个节点的 mongod.conf 核心配置几乎一样只是 replSetName 必须保持一致。以 node1 为例配置文件如下storage: dbPath: /data/mongodb systemLog: destination: file path: /var/log/mongodb/mongod.log logAppend: true processManagement: fork: true net: bindIp: 0.0.0.0 port: 27017 replication: replSetName: rs0关键是replication.replSetName这个字段。三个节点的配置里必须写同样的 rs0否则启动后它们不会向同一个复制集注册。如果你用的是 systemd 托管 mongod可以把processManagement.fork去掉让服务直接在前台模式运行。启动命令很简单每台机器执行mongod -f /etc/mongod.conf启动后检查日志确认没有报错并用下面的命令测试端口是否监听ss -lntp | grep 270173.3 初始化副本集并加入仲裁节点三台 mongod 都启动完成后登录其中一台比如 node1执行初始化命令。最简单的做法是一次性把三个成员全部写进初始化配置Arbiter 节点通过arbiterOnly: true标记。进入 mongo shellrs.initiate({ _id: rs0, members: [ { _id: 0, host: node1:27017 }, { _id: 1, host: node2:27017 }, { _id: 2, host: arbiter:27017, arbiterOnly: true } ] })如果你的环境里三个节点的 IP 固定但域名没配host 字段写 IP 也可以例如192.0.2.11:27017。初始化成功后node1 会成为 primary。此时可以用rs.addArb()这种分步方式添加仲裁节点也可以像上面这样一步到位我更推荐一步到位因为可以避免先初始化再修改配置时出现网络或角色状态不一致的概率。执行完初始化后在任意节点查看复制集状态rs.status()重点关注每个成员的 stateStr期望看到的结果是node1 为 PRIMARYnode2 为 SECONDARYarbiter 为 ARBITER。如果 arbiter 长时间显示 STARTUP 或 RECOVERING需要检查三台机器之间的网络连通性以及节点日志。3.4 应用连接串与读写关注配置副本集搭建完成后应用的连接串不能只写单个节点地址否则故障切换后客户端不会自动发现新主节点。正确写法是把多个数据节点作为种子列表并指定 replicaSet 参数mongodb://node1:27017,node2:27017/?replicaSetrs0wmajorityreadPreferencesecondaryPreferred这里有几个参数值得展开说明。replicaSetrs0让驱动知道这是一个副本集而不是多个独立 mongod。驱动会通过种子节点获取整个副本集的拓扑图并持续跟踪主节点变化。wmajority表示写入需要得到大多数投票成员确认才向业务返回成功在这个副本集里也就是两票。readPreferencesecondaryPreferred表示优先从 secondary 读取primary 只处理写入这个配置适合读多写少的场景可以在不动业务代码的前提下减轻主节点压力。在代码里不同语言驱动设置这些参数的方式略有差异但连接的 URI 部分是通用的。需要注意的是如果客户端只填了 primary 的地址而没带 replicaSet 参数那它只是一个普通的单点连接完全无法感知副本集的故障转移这是很多线上事故低配版出现的原因。4. 故障演练把主节点干掉会发生什么4.1 优雅降级与强制宕机两种玩法搭建完成不等于真的高可用我强烈建议你在上线前做一次故障演练。演练有两种方式一种是优雅降级一种是强制宕机。优雅降级使用 Mongo 提供的主动让位命令rs.stepDown(60)这条命令会让 primary 主动放弃主节点身份并且在 60 秒内不会重新参与选举。执行后观察 node2 和 arbiter 的投票过程node2 大概率会在几秒后被选举为新的 primary。这种方式适合验证日常运维中的计划性切换比如机器巡检、内核升级。强制宕机用于模拟真实故障直接杀掉 primary 进程kill -9 mongod_pid此时副本集会等待选举超时默认大约 10 秒左右然后 node2 发起选举arbiter 投出支持票node2 成为新的 primary。在故障发生时正在写入的数据如果设置了wmajority客户端会收到写入失败或超时错误如果没有设置部分写入可能只发生在旧主节点上恢复后通过回滚机制处理。演练完成后观察rs.status()会看到 node1 回来后自动变成 SECONDARY并开始从 node2 同步 oplog把宕机期间丢失的数据补回来。这个过程不需要人工干预但你需要关注同步延迟可以用下面的命令观察rs.printSecondaryReplicationInfo()4.2 仲裁节点一起挂掉还能玩吗Arbiter 挂掉的场景很多人没想过但生产环境一定会遇到。我设计了几种故障组合它们的表现差异很大。场景一secondary 宕机Arbiter 仍然存活。此时 primary 和 arbiter 在线投票数两票满足多数派primary 保持正常读写只是少了一个数据副本。随后如果你的 primary 也宕机二级节点的位置已经被第一次宕机占据剩余二级 1 票 arbitration 1 票 2 票可以选主。但如果剩余数据节点挂掉就只剩 arbiter 一票永远选不出主。场景二Arbiter 宕机primary 和 secondary 都在线。投票成员在线数为 2仍构成多数派因此 primary 不会自动退位读写服务正常。但这个状态其实非常脆弱如果此时 primary 再宕机剩下 secondary 只有 1 票达不到多数派阈值整个集群将没有主节点写入彻底中断。也就是说Arbiter 单独挂机时集群表面症状不明显实则已经把容错能力从“允许一个投票节点离线”降到了“不允许任何额外故障”。场景三primary 和 Arbiter 同时挂机。此时只剩一个 secondary 在线1 票 2无法完成选举集群会进入只读或不可用状态。这说明 Arbiter 在保存数据方面毫无帮助如果希望两数据节点加一个仲裁的架构在“primary 和数据节点同时故障”时还能恢复写入那你需要一个全量数据节点来替代 Arbiter 的角色这也是我建议高风险场景用三数据节点的原因。4.3 从演练结果反推架构设计思路演练最常见的结论是“原来 Arbiter 挂了之后影响这么大”其实 Arbiter 挂掉本身只是失去了一票真正的问题是你把容错空间建在了一票之上。三节点副本集能容忍一台订阅服务器离线但两数据节点加一仲裁的结构如果仲裁离线则容错能力退化为“不允许任何投票节点持续离线”。因此Arbiter 不仅需要监控它还需要更严格的监控告警。另外客户端同时配置wmajority时写入返回成功只代表大多数投票成员确认了事务但这个“大多数”可能包含了 Arbiter 的票。Arbiter 不存储数据所以返回成功的数据仍然只存在于一个或两个数据节点上。这听起来有些反直觉但确实是这样majority 确认不是多数数据副本确认而是投票成员的多数确认。所以当业务层要求严格的数据多副本确认时不要依靠 Arbiter 凑数。5. 常见坑与排查速查表5.1 初始化阶段就翻车的几个状况初始化副本集的时候下面几个问题出现的概率非常高我把现象、原因和解决办法整理成一张表现象常见原因解决方式rs.initiate 报错 no replset config has been received三台机器 replSetName 不一致或未正确加载配置检查 mongod.conf 中 replication.replSetName节点状态长期 STARTUP节点之间 27017 端口不通bindIp 设置太严格检查防火墙和安全组bindIp 改为 0.0.0.0 或内网地址添加 arbiter 后显示 RECOVERING网络有延迟或 MongoDB 大版本不一致等待 1-2 个心跳周期仍异常检查日志驱动连接时报 unknown replica set连接串没带 replicaSet 参数在 URI 中追加 ?replicaSetrs0rs.status() 能看到节点但客户端无法读写客户端没有把 primary 地址包含进种子列表连接串至少包含两台数据节点再让驱动自动发现这些错误最常见的根源其实只有一个节点之间网络不通。MongoDB 复制集对网络质量非常敏感初始化之前先用telnet或nc验证两两端口的连通性可以省掉很多定位时间。5.2 故障切换后的连接与数据同步异常故障切换之后第一次遇到的往往是客户端连接问题。如果应用只连接了旧 primary 的 IP或者连接串没有副本集拓扑信息新主节点上线后客户端并不会自动重连体现为连接超时、写失败。使用官方驱动并配置了正确 URI 的情况下驱动会通过拓扑感知自动切换但连接池里的旧连接要等到请求报错后才会被丢弃重建所以短暂的写入失败或超时在切换瞬间是正常的业务层要做好重试。另一个常见的故障是 secondary 恢复后一直追不上 primary同步延迟不断增大。这种情况通常与 oplog 大小有关。如果备用节点离线时间太长而 primary 的 oplog 已经覆盖了离线期间的日志备用节点就找不到同步起点只能通过全量同步重新建立数据副本。重新同步期间节点一直处于 RECOVERING 状态需要占用大量网络和磁盘 IO。生产环境建议把 oplog 大小根据业务写入量设置为足够覆盖一天以上的量并给全量同步预留窗口。5.3 监控、备份和安全配置建议Arbiter 不存数据但它仍然是整个高可用架构的关键投票节点因此不能因为资源占用低就忽视监控。至少要对 Arbiter 做端口存活、进程存活、心跳延迟的监控有条件的话把它与数据节点放到同一套监控体系里任何一台投票成员离线都要触发告警。数据节点的监控重点则包括主备切换事件、同步延迟、oplog 时间窗口、磁盘容量。备份方面Arbiter 不需要备份但两台数据节点必须定期备份。推荐的做法是使用文件系统快照或mongodump定时导出备份文件和 oplog 之间的时间点要通过恢复演练验证否则备份等于白做。某些团队喜欢对隐藏节点做备份这是一个更优的选择因为它不会占用业务节点性能。安全部分需要强调几点生产环境必须开启访问控制和传输加密如果已经启用了认证Arbiter 也需要配置对应用户并且在集群初始化时把 keyfile 放到所有节点上数据库端口不要直接暴露到公网只允许应用服务器网段访问。这些基础操作虽然不复杂但很多高可用演练翻车就是栽在认证配置不一致上。最后分享一个我个人的使用习惯在资源允许的情况下我更倾向于把 Arbiter 视为“过渡期方案”而不是长期固定架构。当业务量上涨、数据重要性提升时尽早把 Arbiter 替换成第三个全量数据节点Arbiter 可以在数据节点同步完成后直接从配置中移除整个过程中业务不会有明显感知。如果你只是想在低成本下获得自动故障切换能力那两数据节点加 Arbiter 已经足以覆盖大多数中小业务的可用性要求关键是别把它当成三数据节点的平替想清楚自己的可用性目标再决定怎么使用这一票。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑