一、故事背景:凌晨三点响起的告警
某次大促前夜,笔者的手机突然收到MongoDB集群状态异常的告警。当时副本集包含3个数据节点和1个仲裁节点,但业务系统开始出现间歇性写入失败。通过rs.status()
命令发现仲裁节点处于"UNKNOWN"状态,而主节点频繁切换导致写入操作大面积失败。
(技术栈说明:本文所有示例均基于MongoDB 5.0版本,部署在Ubuntu 20.04系统)
二、致命配置错误分析
// 错误配置示例(初始化副本集时)
rs.initiate({
_id: "myReplSet",
members: [
{ _id: 0, host: "node1:27017" },
{ _id: 1, host: "node2:27017" },
{ _id: 2, host: "node3:27017" },
{ _id: 3, host: "arbiter:27017", arbiterOnly: true } // 仲裁节点声明
]
})
/*
错误点分析:
1. 仲裁节点与数据节点使用相同端口号(违反最佳实践)
2. 未配置投票权(votes)参数
3. 网络防火墙未开放仲裁节点通信端口
*/
三、典型故障现象全记录
- 日志中的幽灵选举(主节点日志片段):
2023-08-20T03:12:45.456+0800 I REPL [replexec-0] Starting election
2023-08-20T03:12:45.458+0800 I REPL [replexec-0] conducting a dry run election
2023-08-20T03:12:46.123+0800 W REPL [replexec-0] Not standing for election;
arbiter:27017 has no vote
- 客户端报错集锦:
pymongo.errors.NotPrimaryError: node2:27017 is not primary
# Java客户端异常
com.mongodb.MongoNotPrimaryException: The replica set has no primary
四、排查法
步骤1:验证基础通信
# 在所有节点执行连通性测试
mongo --host arbiter --port 27017 --eval "db.adminCommand('ping')"
# 预期成功输出:
{ "ok" : 1 }
步骤2:检查副本集配置
// 在主节点执行配置验证
cfg = rs.conf()
cfg.members.forEach(m => {
print(`节点 ${m._id}:
- 类型: ${m.arbiterOnly ? "仲裁" : "数据"}
- 投票权: ${m.votes}
- 优先级: ${m.priority}`)
})
步骤3:网络分区模拟测试
# 临时阻断仲裁节点网络(慎用!)
sudo iptables -A INPUT -p tcp --dport 27017 -s arbiter_ip -j DROP
步骤4:强制重新配置
// 危险操作!需先移除问题节点
rs.remove("arbiter:27017")
rs.reconfig(cfg, {force: true})
五、正确配置示范
# 生产环境推荐配置模板
members:
- _id: 0
host: "node1:27017"
priority: 2
votes: 1
- _id: 1
host: "node2:27017"
priority: 1
votes: 1
- _id: 2
host: "node3:27017"
priority: 1
votes: 1
- _id: 3
host: "arbiter:30000" # 使用独立端口
arbiterOnly: true
votes: 1
priority: 0
六、关联技术深度解析
副本集选举算法改进: MongoDB 4.0后采用Raft-like算法,当仲裁节点不可达时:
- 数据节点必须获得超过半数的投票
- 选举超时时间从默认的10秒调整为动态计算
- 心跳间隔影响故障检测灵敏度
// 选举参数调优示例
cfg.settings = {
electionTimeoutMillis: 2000,
heartbeatIntervalMillis: 500
}
rs.reconfig(cfg)
七、应用场景决策树
是否需要仲裁节点?
├── 节点数量为奇数 → 不需要
├── 节点数量为偶数 → 需要
└── 跨地域部署 → 优先使用隐藏节点代替
八、技术方案优劣对比
仲裁节点方案优势:
- 节省硬件成本(不需要存储数据)
- 快速故障转移(减少选举成员数量)
- 简化维护复杂度
潜在风险:
- 单点故障风险(需部署多个仲裁节点)
- 网络抖动导致误判
- 版本升级时的兼容性问题
九、十大避坑指南
- 永远为仲裁节点配置独立端口
- 生产环境禁止将仲裁节点部署在数据节点所在物理机
- 跨机房部署时仲裁节点应位于第三方区域
- 监控必须包含
replSetGetConfig
和replSetGetStatus
指标 - 定期执行
rs.stepDown()
测试故障转移 - 使用连接字符串包含所有节点地址
- 避免在JavaScript模式下执行长时间操作
- 配置合理的writeConcern级别
- 日志级别至少保持为INFO
- 升级前使用
featureCompatibilityVersion
验证
十、血泪经验总结
经过本次故障排查,我们最终发现根本原因是仲裁节点未正确配置votes参数,导致在节点故障时无法形成有效多数派。修正配置后,集群稳定性显著提升,故障转移时间从原来的15秒缩短至3秒以内。
(以下是作者深夜调试后的灵魂感悟)
"仲裁节点就像分布式系统中的裁判,当裁判自己都站不稳时,整个比赛就会陷入混乱。给裁判一个稳固的哨位,比赛才能顺利进行。"