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
相关产品推荐
相关产品推荐

