(1.3)mongodb RS,mongodb复制集
【1】mongodb RS 介绍
(1.1)Replicateset 复制集介绍
Mongodb复制集由一组Mongod实例(进程)组成,包含一个Primary节点和多个Secondary节点;
Mongodb Driver(客户端)的所有数据都写入Primary,Secondary从Primary同步写入的数据,以保持复制集内所有成员存储相同的数据集,提供数据的高可用。
下图(图片源于云栖社区)是一个典型的Mongdb复制集,包含一个Primary节点和2个Secondary节点。
Primary 中有 oplog,对比mysql的binlog,但mongodb 的 oplog 是存表的;存在如下库:
local库:本地预留库,存储关键日志(oplog,类似于mysqlbinlog)
如上图,注意 Driver 驱动会自动识别,实现读写分离(需要配置),自动寻找故障转移后的新主;
新从节点加入,无需任何操作,会自动同步一份完整数据过去;
MongoDB复制集具有如下四个特点:
1.主是唯一的,但不是固定的。
整个复制集中只有一个主节点,其余为从节点或选举节点,但是因为MongoDB具有自动容灾的功能,所以当唯一的主节点发生宕机时会从从节点的Priority参数不为0当中选举一个为主节点,所以说主是唯一的,但不是固定的。
2.由大多数据原则保证数据的一致性。
即MongoDB复制集内投票成员(参数Vote不为0的其他成员具有选举权,在2.6版本后不能设置Vote大于1,即只能投票一次)数量为N,则大多数为 N/2 + 1;
当复制集内存活成员数量不足大多数时,整个复制集将无法选举出Primary,复制集将无法提供写服务,处于只读状态。通常建议将复制集成员数量设置为奇数。
3.从库无法写入。
MongoDB复制集中只有主节点可以Writes,主节点和从节点可以Read。
4.相对于传统的主从结构,复制集可以自动容灾。
即当某个主节点宕机后会自动从从节点(Priority大于0的从节点)中选举出一个作为新的主节点,而某个从节点宕机后能继续正常工作。
(1.2)复制集 5大节点介绍
MongoDB按功能可分为主节点、从节点(隐藏节点、延时节点、“投票”节点)和选举节点。
节点名 说明
{1}主节点(Primary) :可以提供读Writes/Read的节点,整个复制集中只有一个主节点。
{2}隐藏节点(Hidden) :提供Read并对程序不可见的节点。
{3}延时节点(Delayed) :提供Read并能够延时复制的节点。
{4}“投票”节点(Priority) :提供Read并具有投票权的节点。
{5}选举节点(Arbiter) :Arbiter节点,无数据,仅作选举和充当复制集成员。又成为投票节点。
2个重要参数:
Priority参数:选举优先级,默认为1,Priority参数设置范围在0-1000的整数值。Priority0节点的选举优先级为0,永远不会被选举为Primary。
Vote参数:投票权,Vote默认为1,Vote参数的值在2.6版本后只能设置为0或1。Mongodb 3.0里,复制集成员最多50个,参与Primary选举投票的成员最多7个,其他成员的vote属性必须设置为0,即不参与投票。Vote设置为0时永远没有投票权。
详细解释:
Primary节点:复制集通过replSetInitiate命令(或mongo shell的rs.initiate())进行初始化,初始化后各个成员间开始发送心跳消息,并发起Priamry选举操作,获得『大多数』成员投票支持的节点,会成为Primary,其余节点成为Secondary。
Secondary节点:正常情况下,复制集的Seconary会参与Primary选举(自身也可能会被选为Primary),并从Primary同步最新写入的数据,以保证与Primary存储相同的数据。Secondary可以提供读服务,增加Secondary节点可以提供复制集的读服务能力,同时提升复制集的可用性。另外,Mongodb支持对复制集的Secondary节点进行灵活的配置,以适应多种场景的需求。
Arbiter节点:Arbiter节点只参与投票,不能被选为Primary,并且不从Primary同步数据。比如你部署了一个2个节点的复制集,1个Primary,1个Secondary,任意节点宕机,复制集将不能提供服务了(无法选出Primary),这时可以给复制集添加一个Arbiter节点,即使有节点宕机,仍能选出Primary。Arbiter本身不存储数据,是非常轻量级的服务,当复制集成员为偶数时,最好加入一个Arbiter节点,以提升复制集可用性。
Hidden节点:Hidden节点不能被选为主(因为Priority为0),并且对Driver不可见。因Hidden节点不会接受Driver的请求,可使用Hidden节点做一些数据备份、离线计算的任务,不会影响复制集的服务。
Delayed节点:delayed的配置受到opolg的影响。Delayed节点必须是Hidden节点,并且其数据落后与Primary一段时间(可配置,比如1个小时)。因Delayed节点的数据比Primary落后一段时间,当错误或者无效的数据写入Primary时,可通过Delayed节点的数据来恢复到之前的时间点。
(1.3)基本原理
基本构成是1主2从的结构,自带互相监控投票机制 Raft(mongodb),Paxos(mysql MGRyong de 用的是这种);
如果发生主库宕机,复制集内部会进行投票选举,选择一个新的主库替代原有主库对外提供服务。同时复制集会自动通知客户端程序,主库已经发生了切换,应用就会识别并自动连到新主库;
【2】复制集搭建
注意,local 库是不会同步的,其他库 admin 之类的会同步
(2.1)基本架构
4个节点
机器:192.168.191.25
端口:一个机器上部署4个实例, 28017、28018、28019、28020
(2.2)快速构建 4个实例
从0 安装参考:https://www.cnblogs.com/gered/p/16108016.html
前提,已经基本安装好,已经设置好环境变量;
mkdir -p /data/mongodb/{28017,28018,28019,28020}/{data,log}
useradd mongodb -s /sbin/false
# 配置文件
cat <<eof >/data/mongodb/28017/mongod.conf
systemLog:
destination: file
path: /data/mongodb/28017/log/mongodb.log
logAppend: true
storage:
journal:
enabled: true
dbPath: /data/mongodb/28017/data
directoryPerDB: true
#engine: wiredTiger
wiredTiger:
engineConfig:
cacheSizeGB: 1
directoryForIndexes: true
collectionConfig:
blockCompressor: zlib
indexConfig:
prefixCompression: true
processManagement:
fork: true
net:
bindIp: 0.0.0.0
port: 28017
replication:
oplogSizeMB: 2048
replSetName: my_repl
eof
# 构造不同实例的配置文件
cp /data/mongodb/28017/mongod.conf /data/mongodb/28018/mongod.conf
cp /data/mongodb/28017/mongod.conf /data/mongodb/28019/mongod.conf
cp /data/mongodb/28017/mongod.conf /data/mongodb/28020/mongod.conf
sed -i 's#28017#28018#g' /data/mongodb/28018/mongod.conf
sed -i 's#28017#28019#g' /data/mongodb/28019/mongod.conf
sed -i 's#28017#28020#g' /data/mongodb/28020/mongod.conf
# 启动多个实例
chown -R mongodb:mongodb /data/mongodb
chmod -R 775 /data/mongodb
su - mongodb -c "/usr/local/mongodb/bin/mongod --config /data/mongodb/28017/mongod.conf &"
su - mongodb -c "/usr/local/mongodb/bin/mongod --config /data/mongodb/28018/mongod.conf &"
su - mongodb -c "/usr/local/mongodb/bin/mongod --config /data/mongodb/28019/mongod.conf &"
su - mongodb -c "/usr/local/mongodb/bin/mongod --config /data/mongodb/28020/mongod.conf &"
# netstat -nltp #查看端口监听
(2.3)搭建集群 1主2从
登录 28017(以它为主库)
mongo --port 28017 admin config = { _id: 'my_repl', members: [ {_id: 0, host: '192.168.191.25:28017'}, {_id: 1, host: '192.168.191.25:28018'}, {_id: 2, host: '192.168.191.25:28019'}] }
rs.initiate(config)
# 初始化后,登录上会显示是主还是从 ,如:my_repl:PRIMARY>
rs.status() // 查看集群所有描述
rs.isMaster() // 查看集群成员,以及当前节点是否是 master
rs.conf() // 查看一下集群的配置信息,比如成员的权重、是否是投票节点等等
(2.4)rs.status() 状态查看
my_repl:PRIMARY> rs.status() { "set" : "my_repl", "date" : ISODate("2022-04-25T14:48:08.447Z"), "myState" : 1, "term" : NumberLong(1), "syncSourceHost" : "", "syncSourceId" : -1, "heartbeatIntervalMillis" : NumberLong(2000), "majorityVoteCount" : 2, "writeMajorityCount" : 2, "votingMembersCount" : 3, "writableVotingMembersCount" : 3, "optimes" : { "lastCommittedOpTime" : { "ts" : Timestamp(1650898088, 1), "t" : NumberLong(1) }, "lastCommittedWallTime" : ISODate("2022-04-25T14:48:08.022Z"), "readConcernMajorityOpTime" : { "ts" : Timestamp(1650898088, 1), "t" : NumberLong(1) }, "readConcernMajorityWallTime" : ISODate("2022-04-25T14:48:08.022Z"), "appliedOpTime" : { "ts" : Timestamp(1650898088, 1), "t" : NumberLong(1) }, "durableOpTime" : { "ts" : Timestamp(1650898088, 1), "t" : NumberLong(1) }, "lastAppliedWallTime" : ISODate("2022-04-25T14:48:08.022Z"), "lastDurableWallTime" : ISODate("2022-04-25T14:48:08.022Z") }, "lastStableRecoveryTimestamp" : Timestamp(1650898068, 1), "electionCandidateMetrics" : { "lastElectionReason" : "electionTimeout", "lastElectionDate" : ISODate("2022-04-25T14:45:47.828Z"), "electionTerm" : NumberLong(1), "lastCommittedOpTimeAtElection" : { "ts" : Timestamp(0, 0), "t" : NumberLong(-1) }, "lastSeenOpTimeAtElection" : { "ts" : Timestamp(1650897937, 1), "t" : NumberLong(-1) }, "numVotesNeeded" : 2, "priorityAtElection" : 1, "electionTimeoutMillis" : NumberLong(10000), "numCatchUpOps" : NumberLong(0), "newTermStartDate" : ISODate("2022-04-25T14:45:47.979Z"), "wMajorityWriteAvailabilityDate" : ISODate("2022-04-25T14:45:49.525Z") }, "members" : [ { "_id" : 0, "name" : "192.168.191.25:28017", "health" : 1, "state" : 1, "stateStr" : "PRIMARY", "uptime" : 21846, "optime" : { "ts" : Timestamp(1650898088, 1), "t" : NumberLong(1) }, "optimeDate" : ISODate("2022-04-25T14:48:08Z"), "lastAppliedWallTime" : ISODate("2022-04-25T14:48:08.022Z"), "lastDurableWallTime" : ISODate("2022-04-25T14:48:08.022Z"), "syncSourceHost" : "", "syncSourceId" : -1, "infoMessage" : "", "electionTime" : Timestamp(1650897947, 1), "electionDate" : ISODate("2022-04-25T14:45:47Z"), "configVersion" : 1, "configTerm" : 1, "self" : true, "lastHeartbeatMessage" : "" }, { "_id" : 1, "name" : "192.168.191.25:28018", "health" : 1, "state" : 2, "stateStr" : "SECONDARY", "uptime" : 151, "optime" : { "ts" : Timestamp(1650898078, 1), "t" : NumberLong(1) }, "optimeDurable" : { "ts" : Timestamp(1650898078, 1), "t" : NumberLong(1) }, "optimeDate" : ISODate("2022-04-25T14:47:58Z"), "optimeDurableDate" : ISODate("2022-04-25T14:47:58Z"), "lastAppliedWallTime" : ISODate("2022-04-25T14:48:08.022Z"), "lastDurableWallTime" : ISODate("2022-04-25T14:48:08.022Z"), "lastHeartbeat" : ISODate("2022-04-25T14:48:07.921Z"), "lastHeartbeatRecv" : ISODate("2022-04-25T14:48:07.431Z"), "pingMs" : NumberLong(0), "lastHeartbeatMessage" : "", "syncSourceHost" : "192.168.191.25:28017", "syncSourceId" : 0, "infoMessage" : "", "configVersion" : 1, "configTerm" : 1 }, { "_id" : 2, "name" : "192.168.191.25:28019", "health" : 1, "state" : 2, "stateStr" : "SECONDARY", "uptime" : 151, "optime" : { "ts" : Timestamp(1650898078, 1), "t" : NumberLong(1) }, "optimeDurable" : { "ts" : Timestamp(1650898078, 1), "t" : NumberLong(1) }, "optimeDate" : ISODate("2022-04-25T14:47:58Z"), "optimeDurableDate" : ISODate("2022-04-25T14:47:58Z"), "lastAppliedWallTime" : ISODate("2022-04-25T14:48:08.022Z"), "lastDurableWallTime" : ISODate("2022-04-25T14:48:08.022Z"), "lastHeartbeat" : ISODate("2022-04-25T14:48:07.921Z"), "lastHeartbeatRecv" : ISODate("2022-04-25T14:48:07.430Z"), "pingMs" : NumberLong(0), "lastHeartbeatMessage" : "", "syncSourceHost" : "192.168.191.25:28017", "syncSourceId" : 0, "infoMessage" : "", "configVersion" : 1, "configTerm" : 1 } ], "ok" : 1, "$clusterTime" : { "clusterTime" : Timestamp(1650898088, 1), "signature" : { "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="), "keyId" : NumberLong(0) } }, "operationTime" : Timestamp(1650898088, 1) }
很长,一段一段解析:
members:查看每个节点的状态
(1)health:1 为正常
(2)optime:其实就是说的时间戳情况
【3】RS 常规运维操作
(3.0)RS副本及运维命令汇总
# (1)初始化构建 RS
mongo --port 28017 admin config = { _id: 'my_repl', members: [ {_id: 0, host: '192.168.191.25:28017'}, {_id: 1, host: '192.168.191.25:28018'}, {_id: 2, host: '192.168.191.25:28019'}] } rs.initiate(config) // 初始化后,登录上会显示是主还是从 ,如:my_repl:PRIMARY>
# (2)状态查阅
rs.status() // 查看集群所有描述 rs.isMaster() // 查看集群成员,以及当前节点是否是 master rs.conf() // 查看一下集群的配置信息,比如成员的权重、是否是投票节点等等
# (3)节点增删改(主节点执行才行)
rs.remove("192.168.191.25:28019") // 删除节点
rs.add("ip:port") // 默认 priority 和 votes 均为1,超出7个 votes为1的节点后,则默认为0
rs.add( {_id:1,host: "192.168.146.32:27017",priority: 1, votes: 1 } ) // priority 权重 votes 具有投票权限 1 有 0没有
rs.addArb("ip:port") // 增加 arbiter 角色节点
cfg=rs.config() cfg.members[3].priority=0 // 修改该成员的优先级参数为0 cfg.members[3].hidden=true cfg.members[3].slaveDelay=120 // 秒为单位 rs.reconfig(cfg) // 重新应用加载上面修改的配置
# (4)其他运维命令
rs.stepDown() // (不建议人为操作使用,业务繁忙期间操作)主库降级,手动故障转移,把主库变成变成从库,另自动选择主库(根据 priority 优先级参数)
rs.freeze(300) // 单位秒 锁定从,使其不会转变成主库
rs.slaveOk() // 4.4.12版本前使用,从库副本 默认不允许读,在对应从库上执行 该命令后才可以
rs.secondaryOk() // 4.4.13版本后使用
rs.printSlaveReplicationInfo() // 查看主从延时
(3.1)选举节点(Arbiter)
场景:
我们正常情况只需要一主一从即可,另外一个 arbiter 节点做一个投票仲裁即可;
这样可以用一个很差配置的机器 如上图的 A(Arbiter);而无需用一个和主库一样配置的节点
注意:
常规的S 副本不能直接变成 arbiter 角色副本,但可以删除S副本的复制关系后,再把其变成 arbiter 角色副本;
(3.2)删除节点(必须在主节点执行)
现在的集群信息情况如下:
我们现在 把 28019 节点踢出集群
my_repl:PRIMARY> rs.remove("192.168.191.25:28019") { "ok" : 1, "$clusterTime" : { "clusterTime" : Timestamp(1651389506, 1), "signature" : { "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="), "keyId" : NumberLong(0) } }, "operationTime" : Timestamp(1651389506, 1) }
再次查看:就只有2个节点的信息了,28019确实是被我们删除掉了
(3.3)添加节点(必须在主节点执行)
我们上面删除了 28019 节点,现在我们想把 28019节点 作为 arbiter 节点重新加入到集群;
my_repl:PRIMARY> rs.addArb("192.168.191.25:28019"); { "ok" : 1, "$clusterTime" : { "clusterTime" : Timestamp(1651389770, 1), "signature" : { "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="), "keyId" : NumberLong(0) } }, "operationTime" : Timestamp(1651389770, 1) }
那我们 rs.config() 查看,新加点的节点 192.168.191.25:28019 ,如下图,其 arbiterOnly 参数为 true了
然后我们查看 rs.status() ,如下图,发现我们这个节点是报错的
这是因为我们在 remove 的时候,顺便已经关闭了 20819 节点,我们重启该节点即可
su - mongodb -c "/usr/local/mongodb/bin/mongod --config /data/mongodb/28019/mongod.conf --shutdown &" su - mongodb -c "/usr/local/mongodb/bin/mongod --config /data/mongodb/28019/mongod.conf &"
重新起来28019 节点后,再看rs.status() 已经恢复好
查看 rs.isMaster() 函数中,也显示出来了相关副本及的主机信息和角色信息
【4】RS中的3大特殊节点
(4.1)基本介绍
如(1.2)中所述,除了常规的 主、从 节点,还有3大特殊节点
一般情况下,Hidden 与 Delayed 是一起配置使用的;
Arbiter节点:Arbiter节点只参与投票,不能被选为Primary,并且不从Primary同步数据,也不提供任何服务。比如你部署了一个2个节点的复制集,1个Primary,1个Secondary,任意节点宕机,复制集将不能提供服务了(无法选出Primary),这时可以给复制集添加一个Arbiter节点,即使有节点宕机,仍能选出Primary。Arbiter本身不存储数据,是非常轻量级的服务,当复制集成员为偶数时,最好加入一个Arbiter节点,以提升复制集可用性。
Hidden节点:Hidden节点不能被选为主(因为Priority为0),对Driver不可见,不提供对外服务。因Hidden节点不会接受Driver的请求,可使用Hidden节点做一些数据备份、离线计算的任务,不会影响复制集的服务。
Delayed节点:delayed的配置受到opolg的影响,不提供服务不参与选主。Delayed节点必须是Hidden节点,并且其数据落后与Primary一段时间(可配置,比如1个小时)。因Delayed节点的数据比Primary落后一段时间,当错误或者无效的数据写入Primary时,可通过Delayed节点的数据来恢复到之前的时间点。
(4.2)配置演示节点(一般延时节点也配置成 Hidden,默认复制源是从副本节点)
rs.add("192.168.191.25:28020")
# 配置 members[2] 所对应的主机节点参数,注意,这里的 members[里面的数字]
# 这个是数组下标,而不是 _id 的值 cfg=rs.config() cfg.members[3].priority=0 cfg.members[3].hidden=true cfg.members[3].slaveDelay=120 // 秒为单位 rs.reconfig(cfg) # 取消配置 cfg=rs.config() cfg.members[3].priority=1 cfg.members[3].hidden=false cfg.members[3].slaveDelay=0 rs.reconfig(cfg) # 查看修改后的配置 rs.config()
如下左右2图, 我们可以看到,2个图确实是有时间应用差距的,正好是我们配置的 120秒(但如果有数据的时候偶尔这个显示也会有误差);
但下图中还有一个重大的问题,见 syncSourceHost 字段,我们当前复制集的主库是 192.168.191.25:28017;
但这个延时副本它的 syncSourceHost 确是一个从副本 192.168.191.25:28018,
足以说明:延时、隐藏节点的 默认复制源是从副本节点
【疑问】
(1)如果是偶数个节点怎么办?
如下图,我们有4个节点,每个节点一票
votes field value is 2 but must be 0 or 1 该参数只能为 1 或者 0,所以
方法1:
我们把某个节点的投票数设置为0,比如把 延迟复制节点的投票数取消掉
主库操作:
cfg=rs.config()
cfg.members[3].priority=0 cfg.members[3].votes=0 // 修改该成员的投票数为2 rs.reconfig(cfg) // 重新应用加载上面修改的配置
注意,常规可以故障切换成主副本的从副本不建议这样操作,因为会报错 priority must be 0 when non-voting(votes:0)
也就是说没有投票权的节点,也不应该有成为主库的资格;
【参考文档】
【1】部分转自:https://blog.csdn.net/qq_24598601/article/details/81150614