新节点卡在startup2是因初始同步阻塞于选择同步源;需确保网络连通、源节点未设slavedelay或priority:0,必要时显式指定syncfrom,且dbpath必须为空。

rs.add() 执行后新节点一直卡在 STARTUP2 状态
这是最常见现象:执行 rs.add() 后,新节点日志里反复出现 STARTUP2,同步迟迟不进入 SECONDARY。根本原因不是命令错了,而是它还没开始复制数据——MongoDB 要先拉取全量快照(initial sync),而这个过程默认会阻塞在「找一个合适的同步源」上。
实操建议:
- 确认新节点能连通副本集所有成员,特别是主节点和至少一个数据较新的
SECONDARY(用mongo --host手动连一下测试) - 检查目标同步源是否启用了
slaveDelay或设置了priority: 0,这类节点不会被选为 initial sync 源 - 如果副本集跨网络分区(比如新节点在另一可用区),显式指定同步源更可靠:
rs.add({ host: "new-node:27017", priority: 0, votes: 0 })<br>// 然后立刻在新节点上执行:<br>db.adminCommand({ replSetSyncFrom: "source-node:27017" }) - 初始同步期间,新节点的
dbpath目录必须为空;若之前失败过,删掉整个目录再重启 mongod
全量同步期间主节点 CPU 和磁盘 IO 飙升
初始同步本质是主节点(或选定的同步源)执行一次 mongodump-级的数据遍历 + 压缩传输,不是走 oplog 回放。所以它会对源节点造成真实负载压力,尤其当集合大、索引多时。
实操建议:
- 避免在业务高峰执行
rs.add(),否则可能触发主节点慢查询甚至连接超时 - 如果副本集已有多个
SECONDARY,优先让新节点从负载最低、数据最新(optimeDate最近)的那个同步,而不是默认找主节点 - 确认源节点开启了
syncdelay?没开的话,它得边读数据边写本地 journal,加重 IO —— 这个参数不能改,但你可以选一台 journal 写入压力小的节点做源 - 监控同步进度看
db.printSlaveReplicationInfo()在源节点上输出的syncedTo时间戳,比看新节点状态更准
rs.add() 后新节点进不了 SECONDARY,日志报 “Oplog is empty”
这通常发生在你往一个刚初始化、还没任何写入的副本集里加节点,或者源节点的 oplog 太小,存不下新节点加入前的所有操作。新节点完成全量同步后,需要回放 oplog 追到最新状态,但发现 oplog 里最早的操作时间比自己快照的截止时间还晚。
实操建议:
- 检查 oplog 大小:
db.getReplicationInfo(),重点看oldestTimestamp是否早于你期望的同步起点 - 如果确实太小(比如只有几小时),扩容 oplog 需要停机:先
rs.stepDown()让当前主节点退位,再在每个节点上用mongod --replSet ... --oplogSize重启(注意版本兼容性) - 临时绕过办法:在新节点启动前,先对主节点执行一次
db.runCommand({ create: "dummy", capped: true, size: 1 })触发一条 trivial oplog 条目,确保 oplog 不为空 - 别依赖
rs.add({ arbiterOnly: true })来“省事”,仲裁节点不存数据,无法参与 initial sync,也不能当同步源
添加节点后选举异常或写入变慢
新节点一旦进入 SECONDARY,就参与心跳和投票。如果它的网络延迟高、磁盘响应慢,或者配置了 priority 但没设 hidden: true,可能意外被选为主节点,导致写入确认变慢(尤其 w:majority 写关注下)。
实操建议:
- 新加节点默认
priority: 1,上线前务必评估它是否适合当主:磁盘是机械盘?网络是跨公网?如果是,加节点时直接设低权重:rs.add({ host: "slow-node:27017", priority: 0.5, hidden: true }) -
hidden: true和priority: 0不等价:hidden 节点不显示在isMaster结果里,客户端完全看不见;priority 0 的节点仍参与投票,只是不争主 - 检查
members[n].tags是否和读写策略冲突,比如你用readPreference=nearest,但新节点 tags 里标了"region": "backup"却没配对应 readPreferenceTags - 添加完成后,用
rs.status().members.map(m => [m.name, m.stateStr, m.pingMs])快速扫一眼延迟和状态,比翻日志快得多
真正麻烦的不是加节点那条命令,而是 initial sync 的不可见性——它不报错,只沉默;它不承诺时间,只消耗资源。盯住 syncSourceHost 和 lastHeartbeatMessage 这两个字段,比反复 rs.status() 更有效。











