You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MongoDB副本集单向同步配置及主从数据差异排查需求

解决MongoDB副本集单向同步及差异对比问题

核心原理澄清

首先明确:MongoDB副本集的复制机制默认就是单向的——只有Primary节点的操作会通过oplog同步到Secondary节点,Secondary节点的本地写入(即使开启了secondaryOk)不会被同步回Primary,也不会被其他节点复制。你遇到的"Secondary变更同步到Primary"的情况,大概率是操作误判或配置疏漏,而非副本集本身的双向复制。

一、确保Secondary节点无法成为Primary且限制写入

1. 调整副本集配置,彻底锁定Secondary角色

你已经设置了Secondary的priority:0,但还需要将votes设为0,让它完全失去选举资格,永远无法成为Primary:

rs.reconfig({
  "_id": "你的副本集名称",
  "members": [
    {
      "_id": 0,
      "host": "localhost:27018",
      "priority": 2,
      "votes": 1
    },
    {
      "_id": 1,
      "host": "localhost:27020",
      "priority": 0,
      "votes": 0,
      "hidden": false // 若无需对外提供读服务,可设为true隐藏节点
    },
    // 第三个Secondary节点同理配置
    {
      "_id": 2,
      "host": "localhost:xxxx",
      "priority": 0,
      "votes": 0
    }
  ]
})

执行后用rs.status()确认配置生效。

2. 通过权限控制禁止Secondary写入

默认情况下Secondary不允许写入,除非客户端执行db.getMongo().setSecondaryOk()。为彻底杜绝写入,给连接Secondary的用户分配只读权限:

use admin
db.createUser({
  user: "secondary_read_only",
  pwd: "你的密码",
  roles: [ { role: "readAnyDatabase", db: "admin" } ]
})

应用程序连接Secondary时使用该账号,即可限制写入操作。

二、排查"Secondary变更同步到Primary"的问题

如果确实观察到Secondary的内容出现在Primary上,检查以下几点:

  • 连接节点误判:执行db.isMaster()确认当前连接的节点,ismaster: true表示是Primary,可能你误以为操作的是Secondary,实际连接的是Primary。
  • 节点切换历史:是否曾将Secondary提升为Primary并写入,之后又切回原Primary?这种情况需要重新同步Secondary,执行rs.syncFrom("localhost:27018")指定同步源。
  • 应用配置错误:检查应用的MongoDB连接字符串,确保写入请求被正确路由到Primary(副本集连接字符串默认会自动路由写请求到Primary)。

三、实现Secondary与Primary的差异对比

如果允许Secondary有本地写入(不建议,仅用于特殊场景),可通过以下脚本对比差异:

1. 对比集合列表,找出Secondary新增集合

// 第一步:在Primary上执行,导出集合列表
use 你的数据库名
var primaryCollections = db.getCollectionNames()
printjson(primaryCollections)

// 第二步:在Secondary上执行,找出独有的集合
use 你的数据库名
var secondaryCollections = db.getCollectionNames()
var newCollections = secondaryCollections.filter(c => !primaryCollections.includes(c))
printjson("Secondary新增集合:", newCollections)

2. 对比同一集合内的文档差异

// 在Secondary上执行,对比指定集合
use 你的数据库名
var collName = "目标集合名"
// 连接Primary节点
var primaryConn = new Mongo("localhost:27018")
var primaryColl = primaryConn.getDB("你的数据库名").getCollection(collName)

// 遍历Secondary文档,与Primary对比
db.getCollection(collName).find().forEach(doc => {
  var primaryDoc = primaryColl.findOne({_id: doc._id})
  if (!primaryDoc) {
    printjson(`Secondary新增文档: ${JSON.stringify(doc)}`)
  } else {
    // 排除_id和系统字段,对比内容
    let secondaryContent = {...doc}
    let primaryContent = {...primaryDoc}
    delete secondaryContent._id
    delete primaryContent._id
    if (JSON.stringify(secondaryContent) !== JSON.stringify(primaryContent)) {
      printjson(`文档ID ${doc._id} 内容不一致:`)
      printjson("Primary内容:", primaryContent)
      printjson("Secondary内容:", secondaryContent)
    }
  }
})

注意:数据量较大时,建议分页遍历,避免内存溢出。

总结

  • MongoDB副本集本身就是单向复制,Secondary本地写入不会同步回Primary,你遇到的问题多为操作或配置失误。
  • 配置priority:0+votes:0+只读权限,可彻底确保Secondary的从属角色。
  • 通过简单脚本即可实现Primary与Secondary的集合、文档差异对比。

内容的提问来源于stack exchange,提问作者Prasanna

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 00:25:16