一、副本集故障的常见表现
当MongoDB副本集出现问题时,通常会有一些明显的症状。最常见的就是应用突然无法写入数据,查询也变得特别慢。有时候你会看到控制台不断弹出"Primary不可用"的警告,或者secondary节点莫名其妙地掉线。
比如我们有个三节点的副本集,突然发现应用报错了:
// 应用层报错示例
try {
await db.collection('orders').insertOne({order_id: 1001});
} catch (err) {
console.error('写入失败:', err.message);
// 典型错误: "no primary available for writes"
}
这种情况多半是副本集内部选举出了问题,或者网络分区导致节点间失去联系。我遇到过最棘手的一次是数据中心网络抖动,导致三个节点互相认为对方挂了,结果全都变成了secondary。
二、诊断故障的基本方法
遇到副本集问题时,别急着重启服务。先做系统性的检查,这里分享我的诊断三部曲:
首先,查看副本集状态:
// 在mongo shell中执行
rs.status()
// 重点关注:
// 1. members[].stateStr (节点状态)
// 2. members[].lastHeartbeat (心跳时间)
// 3. members[].optime (操作时间戳)
其次,检查日志是关键。MongoDB的日志通常会明确告诉你问题所在:
# 查看mongod日志的关键错误
grep -E "replSet error|ELECTION|heartbeat" /var/log/mongodb/mongod.log
# 常见错误示例:
# "Couldn't elect self, not receiving enough votes"
# "Heartbeat timeout, stopping replication"
最后,别忘了基础检查:
# 检查基础资源
free -h # 内存情况
df -h # 磁盘空间
ping node2 # 节点间网络
三、典型故障场景与解决方案
3.1 选举失败问题
这是最常见的副本集问题。比如我们有个三节点集群,突然无法选举出primary:
// rs.status()输出示例
{
"set": "myReplSet",
"members": [
{ "name": "node1:27017", "stateStr": "SECONDARY" },
{ "name": "node2:27017", "stateStr": "SECONDARY" },
{ "name": "node3:27017", "stateStr": "SECONDARY" }
]
}
解决方案分几步走:
- 检查多数节点是否在线:
rs.conf() // 确认配置中定义的节点数
- 强制重新选举:
// 在其中一个secondary上执行
rs.stepDown() // 先让当前primary下台
rs.freeze(60) // 防止立即重新当选
3.2 数据不同步问题
有时候会发现secondary节点严重落后于primary:
// 检查复制延迟
db.printSlaveReplicationInfo()
// 输出示例:
// source: node1:27017
// syncedTo: Thu Jan 01 2020 12:00:00 GMT+0800
// 0 secs (0 hrs) behind the primary
处理方案:
- 对于轻微延迟,可以增加oplog大小:
// 在primary上执行
use admin
db.runCommand({replSetResizeOplog: 1, size: 2048}) // 单位MB
- 对于严重延迟,可能需要重新同步:
// 在secondary上执行
rs.syncFrom("node1:27017") // 指定同步源
四、预防性维护建议
与其被动救火,不如主动预防。以下是我的运维经验:
- 合理设置副本集配置:
// 推荐的副本集配置
cfg = rs.conf()
cfg.settings = {
"heartbeatIntervalMillis": 2000,
"electionTimeoutMillis": 10000
}
rs.reconfig(cfg)
- 定期检查副本集健康状态:
# 编写定期检查脚本
#!/bin/bash
mongo --eval "db.hello().isWritablePrimary || process.exit(1)" && echo "OK" || echo "Not Primary"
- 设置适当的监控告警:
# Prometheus监控规则示例
- alert: MongoDBReplLag
expr: mongodb_replset_oplog_head_timestamp - mongodb_replset_oplog_tail_timestamp > 60
for: 5m
labels:
severity: warning
五、高级故障处理技巧
对于特别棘手的问题,可能需要一些高级手段:
- 当副本集配置损坏时:
// 紧急恢复配置
var tempConfig = rs.conf()
tempConfig.members = tempConfig.members.filter(m => m.host.includes("node1"))
rs.reconfig(tempConfig, {force: true})
// 注意: force参数慎用,可能导致数据不一致
- 处理网络分区问题:
// 手动设置节点优先级
cfg = rs.conf()
cfg.members[0].priority = 2
cfg.members[1].priority = 1
rs.reconfig(cfg)
// 这样在网络恢复时,优先级高的节点更容易当选primary
六、总结与最佳实践
经过多次实战,我总结了几个关键点:
- 保持奇数节点数量(3个或5个),避免选举僵局
- 跨机房部署时,合理设置节点优先级
- 定期检查oplog大小,建议至少保留24小时的操作量
- 重要集群建议部署监控代理,如MongoDB Ops Manager
记住,副本集的问题往往不是孤立的,可能是整个系统问题的表现。处理时要全面考虑网络、存储、资源配置等多方面因素。
评论