(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

posted @ 2022-04-19 21:49  郭大侠1  阅读(383)  评论(0编辑  收藏  举报