Skip to content

MongoDB 复制同步问题排查

常见同步问题类型

  1. 复制延迟:从节点数据落后主节点
  2. 同步失败:从节点无法同步主节点数据
  3. ** oplog 不足**:主节点 oplog 容量不足,导致从节点无法追上同步
  4. 网络问题:节点之间网络连接故障
  5. 硬件资源问题:CPU、内存、磁盘 I/O 瓶颈

监控复制同步状态

查看复制集状态

javascript
// 查看复制集状态
rs.status()

// 查看复制集成员详细信息
rs.status().members.forEach(member => {
  print(`成员 ${member.name} 状态: ${member.stateStr}, 复制延迟: ${member.replicationLag}`)
})

查看 oplog 状态

javascript
// 查看 oplog 状态
rs.printReplicationInfo()

// 查看 oplog 大小和时间范围
db.getReplicationInfo()

// 查看 oplog 条目
db.oplog.rs.find().sort({ ts: -1 }).limit(5)

查看从节点同步状态

javascript
// 查看从节点同步状态
rs.printSecondaryReplicationInfo()

// 查看从节点应用 oplog 的状态
db.currentOp({
  $all: true,
  $or: [
    { "op": "getmore", "ns": "local.oplog.rs" },
    { "appName": "mongod", "desc": "rs background sync" }
  ]
})

复制延迟问题排查

复制延迟的原因

  1. 写负载过高:主节点写操作过于频繁,从节点无法及时处理
  2. 网络延迟:节点之间网络延迟过高
  3. 从节点资源不足:CPU、内存、磁盘 I/O 不足
  4. 慢查询:从节点上的慢查询占用资源,影响同步
  5. 索引构建:从节点上构建索引会阻塞复制
  6. ** oplog 滚动过快**:主节点 oplog 太小,从节点还没同步就被覆盖

排查步骤

  1. 查看复制延迟情况
javascript
// 查看复制延迟
rs.printSecondaryReplicationInfo()

// 查看成员状态,重点关注 replicationLag 字段
rs.status().members.filter(m => m.stateStr !== "PRIMARY").forEach(m => {
  print(`${m.name}: ${m.stateStr}, 延迟: ${m.replicationLag}秒`)
})
  1. 检查从节点资源使用情况
javascript
// 查看从节点 CPU 和内存使用情况
db.serverStatus().tcmalloc

// 查看磁盘 I/O 情况
db.serverStatus().diskIO

// 查看连接数
db.serverStatus().connections
  1. 检查从节点上的慢查询
javascript
// 查看从节点上的慢查询
db.system.profile.find().sort({ ts: -1 }).limit(10)

// 启用慢查询日志

db.setProfilingLevel(1, { slowms: 100 })
  1. 检查 oplog 状态
javascript
// 查看 oplog 大小和时间范围
rs.printReplicationInfo()

// 计算 oplog 可以保存的时间
const oplogStats = db.getSiblingDB('local').oplog.rs.stats()
const oplogSizeMB = oplogStats.size / (1024 * 1024)
const oplogTimeRange = db.getSiblingDB('local').oplog.rs.find().sort({ ts: 1 }).limit(1)[0].ts
const now = new Date()
const oplogHours = (now - oplogTimeRange.getDate()) / (1000 * 60 * 60)
print(`oplog 大小: ${oplogSizeMB.toFixed(2)} MB, 可保存时间: ${oplogHours.toFixed(2)} 小时`)

解决方案

  1. 增加从节点资源:升级 CPU、内存,使用更快的磁盘(SSD)
  2. 优化写负载:分散写操作,使用批量写入,优化索引
  3. 调整 oplog 大小:增加 oplog 容量,避免 oplog 滚动过快
  4. 优化网络:减少节点之间的网络延迟,使用专线连接
  5. 避免在从节点上执行慢查询:将查询流量引导到专用的读节点
  6. 合理安排索引构建:在低峰期构建索引,或使用滚动索引构建

同步失败问题排查

同步失败的原因

  1. 网络连接中断:节点之间网络连接故障
  2. 认证问题:节点之间认证失败
  3. ** oplog 不连续**:从节点需要的 oplog 条目已被主节点覆盖
  4. 数据损坏:从节点数据损坏
  5. 配置错误:复制集配置错误
  6. 版本不兼容:节点之间 MongoDB 版本不兼容

排查步骤

  1. 查看复制集状态
javascript
// 查看复制集状态,重点关注 errMsg 字段
rs.status()

// 查看成员状态,寻找错误信息
rs.status().members.forEach(member => {
  if (member.errMsg) {
    print(`${member.name} 错误: ${member.errMsg}`)
  }
})
  1. 检查节点之间的网络连接
bash
# 测试节点之间的网络连接
telnet <主节点IP> 27017

# 测试网络延迟
ping <主节点IP>

# 测试网络带宽
iperf3 -c <主节点IP> -p 27017
  1. 检查认证配置
javascript
// 检查复制集认证配置
db.adminCommand({ getParameter: 1, authenticationMechanisms: 1 })

// 检查 keyFile 配置
rs.conf().members.forEach(member => {
  print(`${member.name} 使用 keyFile: ${member.ssl ? '是' : '否'}`)
})
  1. 检查 oplog 连续性
javascript
// 查看从节点需要的 oplog 时间戳
const secondary = rs.status().members.find(m => m.stateStr === "SECONDARY")
if (secondary.syncingTo) {
  const syncTarget = secondary.syncingTo
  const syncSourceStatus = db.adminCommand({
    replSetGetStatus: 1,
    forMember: syncTarget
  })
  print(`从节点 ${secondary.name} 正在同步 ${syncTarget}`)
}

// 查看从节点的 oplog 时间范围
const localDB = db.getSiblingDB('local')
const minOpTime = localDB.oplog.rs.find().sort({ ts: 1 }).limit(1)[0].ts
const maxOpTime = localDB.oplog.rs.find().sort({ ts: -1 }).limit(1)[0].ts
print(`从节点 oplog 时间范围: ${minOpTime} 到 ${maxOpTime}`)

解决方案

  1. 修复网络连接:检查网络配置,修复网络故障
  2. 重新初始化从节点:如果从节点数据损坏或 oplog 不连续,需要重新初始化
  3. 调整复制集配置:修复复制集配置错误
  4. 升级 MongoDB 版本:确保所有节点版本兼容
  5. 检查硬件故障:检查磁盘、内存等硬件是否故障

重新初始化从节点

方法一:使用 rs.reSync()

javascript
// 连接到从节点
mongo --host <从节点IP> --port 27017

// 重新同步从节点
rs.reSync()

方法二:手动重新初始化

  1. 停止从节点 mongod 服务
bash
# Linux
systemctl stop mongod

# Windows
net stop MongoDB
  1. 删除从节点数据目录
bash
# Linux
rm -rf /var/lib/mongodb/*

# Windows
rd /s /q C:\data\db
  1. 启动从节点 mongod 服务
bash
# Linux
systemctl start mongod

# Windows
net start MongoDB
  1. 重新添加到复制集
javascript
// 连接到主节点
mongo --host <主节点IP> --port 27017

// 重新添加从节点
rs.add({
  host: "<从节点IP>:27017",
  priority: 0,
  votes: 1
})

方法三:使用增量同步

如果从节点只是落后一段时间,可以使用增量同步:

javascript
// 连接到从节点
mongo --host <从节点IP> --port 27017

// 检查从节点需要的起始时间戳
const syncState = db.getSiblingDB('local').system.replset.find().toArray()[0]
print(`从节点需要的起始时间戳: ${syncState.syncSourceHost}, ${syncState.syncSourceId}`)

// 调整同步源
rs.syncFrom("<主节点IP>:27017")

优化 oplog 配置

增加 oplog 大小

  1. 查看当前 oplog 大小
javascript
rs.printReplicationInfo()
  1. 修改 oplog 大小
javascript
// 连接到主节点
mongo --host <主节点IP> --port 27017

// 进入 admin 数据库
use admin

// 修改 oplog 大小为 10GB
cfg = rs.conf()
cfg.settings = cfg.settings || {}
cfg.settings.oplogSizeMB = 10240
rs.reconfig(cfg)

监控 oplog 使用情况

javascript
// 创建 oplog 监控脚本
function monitorOplog() {
  const oplogStats = db.getSiblingDB('local').oplog.rs.stats()
  const oplogSizeMB = oplogStats.size / (1024 * 1024)
  const oplogCount = db.getSiblingDB('local').oplog.rs.count()
  const firstOp = db.getSiblingDB('local').oplog.rs.find().sort({ ts: 1 }).limit(1)[0]
  const lastOp = db.getSiblingDB('local').oplog.rs.find().sort({ ts: -1 }).limit(1)[0]
  const timeRangeHours = (lastOp.ts.getHighBits() - firstOp.ts.getHighBits()) / 3600
  
  print(`oplog 大小: ${oplogSizeMB.toFixed(2)} MB`)
  print(`oplog 条目数: ${oplogCount}`)
  print(`oplog 时间范围: ${timeRangeHours.toFixed(2)} 小时`)
  print(`oplog 使用效率: ${(oplogStats.count / oplogStats.size * 1024 * 1024).toFixed(2)} 条目/MB`)
}

// 运行监控脚本
monitorOplog()

复制同步最佳实践

  1. 合理配置 oplog 大小:根据写负载和节点数量,设置合适的 oplog 大小
  2. 监控复制延迟:设置复制延迟告警阈值,及时发现同步问题
  3. 确保节点资源充足:从节点资源配置不应低于主节点
  4. 优化网络连接:使用低延迟、高带宽的网络连接
  5. 定期检查复制状态:定期运行 rs.status() 和 rs.printSecondaryReplicationInfo()
  6. 避免在从节点上执行写操作:从节点默认只读,写操作会导致数据不一致
  7. 使用优先级和选举权:合理配置节点优先级和选举权,确保正确的故障切换
  8. 定期备份 oplog:备份 oplog 以便进行时间点恢复

常见问题(FAQ)

Q1: 复制延迟多少秒是正常的?

A1: 正常情况下,复制延迟应该在几秒以内。如果复制延迟超过 30 秒,就需要进行排查。对于写负载较高的场景,复制延迟可能会达到几分钟,但应该设置告警阈值,如 5 分钟,超过则触发告警。

Q2: 如何处理 oplog 不足的问题?

A2: 处理 oplog 不足的方法包括:

  • 增加 oplog 大小:通过 rs.reconfig() 修改 oplogSizeMB 参数
  • 优化写负载:减少写操作频率,使用批量写入
  • 增加从节点资源:提高从节点处理能力
  • 使用更高效的存储:使用 SSD 磁盘提高 I/O 性能

Q3: 从节点同步失败后如何恢复?

A3: 从节点同步失败后,可以尝试以下恢复方法:

  • 检查并修复网络连接
  • 检查并修复认证配置
  • 使用 rs.reSync() 重新同步
  • 手动重新初始化从节点
  • 调整同步源

Q4: 如何监控复制同步状态?

A4: 可以通过以下方式监控复制同步状态:

  • 使用 MongoDB 内置命令:rs.status()、rs.printReplicationInfo()、rs.printSecondaryReplicationInfo()
  • 使用 MongoDB Atlas 或 Ops Manager 进行监控
  • 使用第三方监控工具:Prometheus + Grafana、Datadog 等
  • 设置告警:当复制延迟超过阈值时触发告警

Q5: 复制集成员之间的网络延迟应该控制在多少以内?

A5: 复制集成员之间的网络延迟应该控制在 100ms 以内,理想情况下应该在 10ms 以内。网络延迟过高会导致复制延迟增加,影响高可用性。

Q6: 如何优化从节点的同步性能?

A6: 优化从节点同步性能的方法包括:

  • 升级硬件:使用更快的 CPU、内存和 SSD 磁盘
  • 优化配置:调整 mongod 配置参数,如 wiredTigerCacheSizeGB、maxIndexBuildMemoryUsageMegabytes 等
  • 减少从节点负载:避免在从节点上执行慢查询和写操作
  • 优化网络:使用低延迟、高带宽的网络连接
  • 合理配置索引:避免过多的索引,影响写操作性能

Q7: 复制集可以跨数据中心部署吗?

A7: 是的,复制集可以跨数据中心部署,实现地理位置冗余和灾备。但需要注意:

  • 网络延迟会增加复制延迟
  • 需要合理配置节点优先级和选举权
  • 考虑使用区域感知复制集配置
  • 确保跨数据中心的网络连接可靠

Q8: 如何处理从节点上的慢查询影响同步?

A8: 处理从节点上的慢查询影响同步的方法包括:

  • 将查询流量引导到专用的读节点
  • 优化慢查询,添加适当的索引
  • 限制从节点上的并发查询数
  • 调整慢查询日志级别,监控慢查询
  • 考虑使用分片集群分散查询负载

Q9: 复制集成员可以使用不同版本的 MongoDB 吗?

A9: 复制集成员可以使用不同版本的 MongoDB,但需要遵循版本兼容性规则:

  • 主节点版本不能高于从节点版本
  • 升级时应该先升级从节点,再升级主节点
  • 不同版本之间的功能差异可能会影响复制

Q10: 如何验证复制同步的一致性?

A10: 验证复制同步一致性的方法包括:

  • 使用 db.collection.count() 比较主从节点的数据量
  • 使用 db.collection.find().sort({ _id: -1 }).limit(1) 比较最新数据
  • 使用 MongoDB 企业版的一致性校验工具
  • 定期进行数据备份和恢复测试
  • 使用第三方数据校验工具