MongoDB 副本集原理及管理
MongoDB副本集(Replica Set)是一组MongoDB实例组成的集群,由一个主节点(primary)和多个备节点(Secondary)组成。通过Repication,将数据的更新由primary推送到其他备节点上。每个MongoDB实例维护相同的数据集副本,通过维护冗余的数据集副本,能够实现数据的异地备份,读写分离和自动故障转移。
副本集架构
创建一个Replica set,包含三个成员,一个primary和两个secondary成员,primary处理客户端请求,secondary用于保存primary数据副本。客户端在primary进行读写操作,通过replication的异步同步机制,将数据操作同步sencondary成员,保证三个成员拥有相同的数据集。如果primary连接中断超过10s,其他节点会自动选举出一个primary节点,负责响应客户端的请求,实现数据的自动故障转移。
技术原理说明
mongodb instance有两种不同的启动方式,一种是以单实例启动(standalone),一种是副本集(replica set)模式,如果设置replSet参数,mongodb实例将以副本集模式启动。
选举primary成员
在Replica Set中有两种成员:Primary成员和Secondary成员,一个Replica Set只能有一个Primary成员,但可以有多个Secondary成员。Primary用于处理客户端请求,Secondary用于保存Primary的数据副本。如果Primary崩溃了,Replica Set探测到Primary不可访问,将启动自动故障转移进程,从剩下的Secondary成员中,投票选举出一个成员作为Primary,接收和处理客户端的请求。
选举Primary成员时,使用“大多数”和“一票否决”原则。在Replica Set中,每个成员只能要求自己被选举为Primary节点。当一个Secondary成员无法与Primary成员连通时,该成员就会发起选举,请求其他成员将自己选举为Primary成员,只有得到“大多数”成员的支持,该成员才能被选举为Primary成员;只要有一个成员否决,选举就会取消。
不是每一个成员都有投票选举的权利,在一个Replica Set中,最多有7个成员用于投票选举的权利,Primary成员是由这7个成员选举出来的。有投票权利的成员,其属性:"votes" 是1;若为0,表示该成员没有投票权利。
操作日志
mongodb使用操作日志(oplog)来实现复制(replication)功能。oplog包含了primary节点的每一个更新操作,通过oplog传递到其他secondary节点中,在其他节点重做(redo)已经提交的操作,实现数据的异步同步。每个成员维护着自己的oplog,记录着每一个从primary节点复制过来的数据。
复制过程是先复制数据,再写oplog中。如果因为重启导致执行重复操作,将oplog中同一个操作执行多次,与执行一次的结果是一样的。
oplog保存的是对每个doc的更新操作日志,如果一个命令只更新一个doc,那么Replication进程向oplog插入一条日志;如果一个命令更新多个doc,那么Replication进程向oplog插入多条日志,每一条日志只更新一个doc。
配置文件参数定义
replSet:设置Replica Set的name,在各个配置文件中,其值必须相同
dbpath: MongoDB用于存储数据的目录
logpath:用于记录mongod的日志数据
port: 指定MongoDB监听的端口,默认值是27017
创建配置文档,调用rs.initiate()函数,按照配置文档来初始化Replica Set:
conf= { "_id" : "replname", "members" : [ { "_id" : 0, "host" : "127.0.0.1:2777" }, { "_id" : 1, "host" : "127.0.0.1:3777" }, { "_id" : 2, "host" : "127.0.0.1:4777" } ] } rs.initiate(conf)
使用"_id" : "replname" 指定Replica Set的name
members数组指定 Replica Set的成员的ID 和 host(“host:port”)
修改Replica Set
如果以rs.initiate()方式初始化Replica Set,那么MongoDB以默认配置文档初始化Replica Set,可以通过add()函数增加成员
向Replica Set中增加一个成员
rs.add("host:port")
从Replica Set中删除一个成员
rs.remove("host")
查看Replica Set的配置
rs.conf()
查看Replica Set的状态
rs.status() { "_id" : "replname", "version" : 7, "protocolVersion" : NumberLong(1), "writeConcernMajorityJournalDefault" : true, "members" : [ { "_id" : 0, "host" : "127.0.0.1:2777", "arbiterOnly" : false, "buildIndexes" : true, "hidden" : false, "priority" : 1, "tags" : { }, "slaveDelay" : NumberLong(0), "votes" : 1 }
Replica Set的ID字段唯一标识一个Replica Set,每一个Replica Set都有一个自增的版本号,由Version字段标识,标识Replica Set的不同版本。version字段的初始值是1,每次修改Replica Set的配置时,version字段都会自增.
arbiterOnly:0或1,标识一个仲裁(arbiter),Arbiter的唯一作用是参与Primary的选举,Arbiter不保存数据,不会为client提供服务,它存在的意义就是为了选举Primary
hidden:0或1,表示该成员是不是隐藏成员,Hidden成员的主要作用是备份数据,可以使用性能较差的服务器作为Hidden成员。Hidden成员不会接收Client的请求,也不会成为Primary。在设置Hidden成员时,必须设置members[n].priorty属性为0. priority:数值类型,用于设置成员成为Primary的优先级。priority越高的成员,越有机会成为Primary。如果priority=0,那么该成员永远不会成为Primary. votes:1或0,表示该成员的投票的数量,在每个Replica Set中,最多有7个成员,其votes属性值是1。votes 属性是1的成员(voting members)拥有选举Primary的权利。一个成员要想成为一个Primary,那么必须获得voting members的大多成员的支持.
在Replica Set中,如果voting members的数量是5,那么一个成员成为Primary的条件是:获得超过2个voting members的支持,并且没有任何voting members 反对。只要有任意一个voting member 反对该成员成为Primary,那么该成员就不能成为Primary。
在客户端查看进程信息:
shard1:PRIMARY> db.serverCmdLineOpts()
命令
rs.:副本命令,是replSet是缩写,代表副本集 db.:数据库命令,比如db.printReplicationInfo(),db.printSlaveReplicationInfo() rs.status:查看成员的复制状态,可以在任意节点执行 rs.config():可以得到当前副本的配置,修改配置文件,然后将修改后的配置文件传递给reconfig、initiate命令 rs.reconfig()(replSetReconfig):修改副本集的配置。rs.reconfig(conf,{"force":true}) rs.initiate():会初始化配置,只需要对副本集中的一个成员调用rs.initiate就可以(一般主节点),收到initiate命令的成员会自动将配置文件传递给副本集中的其他成员
修改副本集成员配置时的限制
1、不能修改_id; 2、不能将当前执行rs.reconfig命令的成员的优先级设置为 0; 3、不能将仲裁者成员变为非仲裁者成员,反正亦然; 4、不能将buildIndexes由false改为 true;
强制重新配置
在备份节点上调用rs.reconfig(conf,{"force":ture})强制重新配置副本集。注意conf必须是正确、有效的配置。而且只允许在备份节点执行强制重新配置。
复制源
查询节点从哪个节点处复制。在备份节点上运行,也可以运行rs.status(),查看"syncingTo"字段信息
db.adminCommand({"replSetGetStatus":1})['syncingTo'];
复制链
MongoDB根据ping时间选择同步源,一个成员向另一个成员发送心跳请求,就知道心跳请求所耗费的时间(rs.status()中的"pingMs"记录了成员到达相关成员的所花费的平均时间)。MongosDB维护着不同成员间请求的平均花费时间。选择同步源时,会选择一个离自己比较近而且数据比自己新的成员。但是同一数据中心的成员可能会从同一数据中心的其他成员处复制,而不是从位于另一个数据中心的主节点处复制(这样可以减少网络流量),所以会出现复制链的情况,复制链越长会导致主节点的操作复制到所有的服务器所花费的时间越长。这对于需要从副本中读取数据的需求这种情况是不希望看到的。
修改复制源
db.adminCommand({"replSetSyncFrom":"192.168.1.100:2777"});
禁用复制链(在主副本中执行)
conf = rs.conf() conf.settings.chainingAllowed = false rs.reconfig(conf)
查看当前副本集oplog状态
rs.printReplicationInfo() configured oplog size:oplog配置的大小 log length start to end: oplog包含的操作时长。 oplog first event time: oplog第一条操作的时间。 oplog last event time: oplog最后一条操作的时间。 now:当前时间。 注意:oplog中第一条操作与最后一条操作的时间差就是操作日志的长度。
查看复制延迟,会显示当前所有的备份节点同步时间,和落后主节点的时长
rs.printSlaveReplicationInfo()
调整oplog大小
1) 如果是主节点,将主节点变成备份节点。 2) 将oplog中的最后一条insert操作保存到其它集合中。 use local db.tempLastOp20180330.save( db.oplog.rs.find( { }, { ts: 1, h: 1 } ).sort( {$natural : -1} ).limit(1).next() ) 3) 关闭当前服务 4) 以单机模式启动,可以指定一个新的端口,或者将replSet注释掉已配置文件启动 mongod --port 6777 --bind_ip 192.168.1.10,127.0.0.1 --dbpath /u02/mongo6777/data/ 5) 删除当前oplog db.oplog.rs.drop(); 6) 创建新的oplog db.createCollection("oplog.rs",{"capped":true,"size":1048576}) db.createCollection("oplog.rs",{"capped":true,"size":(2*1024*1024*1024)}) 7) 将最后一条insert记录写回oplog var tempLastOp=db.tempLastOp20180330.find().next() db.oplog.rs.insert(tempLastOp) db.oplog.rs.find() 8) 以副本成员身份启动当前服务
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 上周热点回顾(3.3-3.9)
· AI 智能体引爆开源社区「GitHub 热点速览」