MongoDB运维篇(2)之复制集-ReplicationSet

MongoDB运维篇(2)之复制集-ReplicationSet

微信搜索 zze_coding 或扫描 👉 二维码关注我的微信公众号获取更多资源推送:

基本架构

MongoDB 复制集基本构成是 1 主 2 从的结构,自带互相监控投票机制(通过 Raft 实现,mysql MGR 用的是 Paxos 的变种)。

如果主库宕机了,复制集内部会进行投票选举,选择一个新的主库替代原有主库对外提供服务。同时复制集会自动通知客户端程序,主库已经发生切换了。应用就会连接到新的主库。

一个三成员的复制集的基本架构有如下两种类型:

  • 三个有数据;
  • 两个有数据,一个仅参与仲裁,这个参与仲裁的节点被称为 arbiter 节点;

下面对这两种类型的架构做一下简单的说明。

三个数据节点

一个主库,两个从库,主库宕机时,这两个从库都可以被选为主库。

image.png

当主库宕机后,两个从库都会进行竞选,其中一个变为主库,当原主库恢复后,作为从库加入当前的复制集群即可。

image.png

存在 arbiter 节点

一个主库,一个从库,一个 aribiter 节点,如果主库挂了,该从库就会被选举成为主库, 在选举中,aribiter 节点仅参与仲裁投票,不能成为主库。

image.png

由于 arbiter 节点没有复制数据,因此这个架构中仅提供一个完整的数据副本。arbiter 节点只需要更少的资源,代价是更有限的冗余和容错。

当主库宕机时,将会选择从库成为主,主库修复后,将其加入到现有的复制集群中即可。

image.png

环境规划

所以对于 MongoDB 复制集架构的部署我们需要三个以上的节点(实验环境可使用单机多实例),我这里就使用单机多实例的方式演示了。

我这里实验环境大致如下:

  • 系统:CentOS 7.8;
  • 内存大小:2G;
  • 规划实例端口:28017、28018、28019、28020;

做如下操作之前请按上一篇文章将 MongoDB 二进制包解压到指定路径并配置好环境变量。

复制集应用

多实例配置

1、切换到 mongod 用户:

$ su - mongod

2、为每个实例准备一套目录:

$ mkdir -p /mongodb/28017/{conf,data,log}
$ mkdir -p /mongodb/28018/{conf,data,log}
$ mkdir -p /mongodb/28019/{conf,data,log}
$ mkdir -p /mongodb/28020/{conf,data,log}

3、添加配置文件:

$ cat > /mongodb/28017/conf/mongod.conf <<EOF
systemLog:
  destination: file
  path: /mongodb/28017/log/mongodb.log
  logAppend: true
storage:
  journal:
    enabled: true
  dbPath: /mongodb/28017/data
  directoryPerDB: true
  # 指定使用 wiredTiger 引擎,类似 MySQL 的 InnoDB,支持事务
  wiredTiger:
    engineConfig:
      # 缓冲区大小,支持数据和索引的缓冲,类似 MySQL 的 InnoDB Buffer Pool
      cacheSizeGB: 1
      directoryForIndexes: true
    collectionConfig:
      blockCompressor: zlib
    indexConfig:
      prefixCompression: true
processManagement:
  fork: true
net:
  bindIp: 10.0.1.51,127.0.0.1
  port: 28017
# 复制集配置
replication:
  # 最多存储日志的大小,MongoDB 的日志直接存储在集合中,即该大小也是日志集合的最大大小,如果该项不设置,则默认为磁盘总空间的 5% 大小
  oplogSizeMB: 2048
  # 复制集名称
  replSetName: my_repl
EOF

# 拷贝
$ \cp  /mongodb/28017/conf/mongod.conf  /mongodb/28018/conf/
$ \cp  /mongodb/28017/conf/mongod.conf  /mongodb/28019/conf/
$ \cp  /mongodb/28017/conf/mongod.conf  /mongodb/28020/conf/

# 修改端口
$ sed 's#28017#28018#g' /mongodb/28018/conf/mongod.conf -i
$ sed 's#28017#28019#g' /mongodb/28019/conf/mongod.conf -i
$ sed 's#28017#28020#g' /mongodb/28020/conf/mongod.conf -i

4、配置每个实例被 systemd 管理:

$ cat > /etc/systemd/system/mongod28017.service <<EOF
[Unit]
Description=mongodb 
After=network.target remote-fs.target nss-lookup.target
[Service]
User=mongod
Type=forking
ExecStart=/mongodb/bin/mongod --config /mongodb/28017/conf/mongod.conf
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/mongodb/bin/mongod --config /mongodb/28017/conf/mongod.conf --shutdown
PrivateTmp=true  
[Install]
WantedBy=multi-user.target
EOF

# 拷贝
$ \cp /etc/systemd/system/mongod28017.service /etc/systemd/system/mongod28018.service
$ \cp /etc/systemd/system/mongod28017.service /etc/systemd/system/mongod28019.service
$ \cp /etc/systemd/system/mongod28017.service /etc/systemd/system/mongod28020.service

# 替换使用的配置文件
$ sed 's#28017#28018#g' /etc/systemd/system/mongod28018.service -i
$ sed 's#28017#28019#g' /etc/systemd/system/mongod28019.service -i
$ sed 's#28017#28020#g' /etc/systemd/system/mongod28020.service -i

5、启动多个实例并检查端口是否已占用:

# 启动
$ systemctl start mongod28017.service mongod28018.service mongod28019.service mongod28020.service
# 检查端口
$ ss -tanl | grep 280
LISTEN     0      128    127.0.0.1:28017                    *:*                  
LISTEN     0      128    10.0.1.51:28017                    *:*                  
LISTEN     0      128    127.0.0.1:28018                    *:*                  
LISTEN     0      128    10.0.1.51:28018                    *:*                  
LISTEN     0      128    127.0.0.1:28019                    *:*                  
LISTEN     0      128    10.0.1.51:28019                    *:*                  
LISTEN     0      128    127.0.0.1:28020                    *:*                  
LISTEN     0      128    10.0.1.51:28020                    *:*  

至此,MongoDB 的 4 个实例就已经正常启动了。

普通复制集

1、以配置 1 主 2 从为例,随便连入一个实例,我这里以连入 28017 为例:

$ mongo --port 28017 admin
MongoDB shell version v3.6.12
connecting to: mongodb://127.0.0.1:28017/admin?gssapiServiceName=mongodb
...
> config = {_id: 'my_repl', members: [
                          {_id: 0, host: '10.0.1.51:28017'},
                          {_id: 1, host: '10.0.1.51:28018'},
                          {_id: 2, host: '10.0.1.51:28019'}]
          }

> rs.initiate(config) 
{
	"ok" : 1,
	"operationTime" : Timestamp(1592398822, 1),
	"$clusterTime" : {
		"clusterTime" : Timestamp(1592398822, 1),
		"signature" : {
			"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
			"keyId" : NumberLong(0)
		}
	}
}
my_repl:SECONDARY> 
# 回车,当看到命令提示符显示 RPIMARY 则说明当前实例选为了主库
my_repl:PRIMARY> 
# 可查看复制集状态
my_repl:PRIMARY> rs.status()

2、再查看另外两个节点,会发现它们是从库了:

$ mongo --port 28018 admin
MongoDB shell version v3.6.12
connecting to: mongodb://127.0.0.1:28018/admin?gssapiServiceName=mongodb
...
my_repl:SECONDARY>

$ mongo --port 28019 admin
MongoDB shell version v3.6.12
connecting to: mongodb://127.0.0.1:28019/admin?gssapiServiceName=mongodb
...
my_repl:SECONDARY> 

3、测试关闭主库,即 28017 实例,检查新主库:

# 关闭主库
$ systemctl stop mongod28017.service

# 检查 28018 实例
$ mongo --port 28018 admin
MongoDB shell version v3.6.12
connecting to: mongodb://127.0.0.1:28018/admin?gssapiServiceName=mongodb
...
# 可以看到 28018 实例已经切换为主库了
my_repl:PRIMARY> 

# 检查 28019 实例
$ mongo --port 28019 admin
MongoDB shell version v3.6.12
connecting to: mongodb://127.0.0.1:28019/admin?gssapiServiceName=mongodb
...
# 可以看到 28019 实例依旧是从库
my_repl:SECONDARY> 

4、启动 28017 实例,然后检查该实例状态:

# 启动
$ systemctl start mongod28017.service
# 可以看到 28017 实例成为了从库,使用 rs.status() 查看更明显
$ mongo --port 28017 admin
MongoDB shell version v3.6.12
connecting to: mongodb://127.0.0.1:28017/admin?gssapiServiceName=mongodb
...
my_repl:SECONDARY> 

存在 arbiter 节点的复制集

以一主一从一 arbiter 节点的复制集为例,配置很简单,仅需要在初始化复制集时设定要作为 arbiter 的节点的 arbiterOnly 属性为 true,如下:

# 以设定 28019 实例为 arbiter 为例
$ mongo --port 28017 admin
MongoDB shell version v3.6.12
connecting to: mongodb://127.0.0.1:28017/admin?gssapiServiceName=mongodb
...
> config = {_id: 'my_repl', members: [
                          {_id: 0, host: '10.0.1.51:28017'},
                          {_id: 1, host: '10.0.1.51:28018'},
                          {_id: 2, host: '10.0.1.51:28019',"arbiterOnly":true}]
          }
rs.initiate(config)

除了在初始化复制集时指定 arbiter 节点,还可通过下面的复制集管理操作来动态添加新的 arbiter 节点到复制集中,继续往下看吧~~~

复制集管理

常用的复制集管理操作有如下:

$ mongo --port 28018 admin
MongoDB shell version v3.6.12
connecting to: mongodb://127.0.0.1:28017/admin?gssapiServiceName=mongodb
...
# 查看复制集状态
my_repl:PRIMARY> rs.status()

# 查看当前节点是否是主节点
my_repl:PRIMARY> rs.isMaster()

# 查看当前复制集的配置信息
my_repl:PRIMARY> rs.conf()

# 删除节点,以删除 28017 节点为例
my_repl:PRIMARY> rs.remove("10.0.1.51:28017")

# 重新将 28017 节点添加为 arbiter 节点
my_repl:PRIMARY> rs.addArb("10.0.1.51:28017")

# 也可新添加一个数据节点,以添加 28020 节点为新的数据节点为例
my_repl:PRIMARY> rs.add("10.0.1.51:28020")

# 将当前主库实例降级,在指定时间内这个实例不会把自己选为主库,不建议人为操作
my_repl:PRIMARY> rs.stepDown(30)

# 设置当前从节点可读
my_repl:SECONDARY> rs.slaveOk()

# 锁定当前实例在指定时间内不会被选举从库,单位为秒
my_repl:SECONDARY> rs.freeze(300)

# 查看从库的同步状态
my_repl:SECONDARY> rs.printSlaveReplicationInfo()

# 默认复制集的从节点是不提供读写操作,可通过执行 rs.slaveOk() 以设定从节点可读,该种方式是临时生效
# 还可将该命令写入启动文件让其永久生效 echo "rs.slaveOk()" > ~/.mongorc.js
my_repl:SECONDARY> rs.slaveOk()

特殊复制集

除了普通的数据节点外,MongoDB 复制集中可存在如下三类特殊的节点:

  • arbiter 节点:主要负责选主过程中的投票,但是不存储任何数据,也不提供任何服务;
  • hidden 节点:隐藏节点,不参与选主,也不对外提供服务;
  • delay 节点:延时节点,数据落后于主库一段时间,因为数据是延时的,也不应该提供服务或参与选主,所以通常会配合 hidden(隐藏)一起使用;

下面就以配置 28020 节点为延时节点为例,即需要同时配置它为隐藏节点,随便选择复制集中的一个实例登入,执行如下操作:

$ mongo --port 28018 admin
MongoDB shell version v3.6.12
connecting to: mongodb://127.0.0.1:28017/admin?gssapiServiceName=mongodb
...
# 首先通过 rs.conf() 查看 28020 节点在 members 列表的索引(索引起始值为 0),内容太长我就省略啦
my_repl:PRIMARY> rs.conf()

# 这里我的环境中 28020 节点处于列表的第四个节点,所以它对应的索引值为 3
my_repl:PRIMARY> cfg=rs.conf() 
# 设置优先级为 0
my_repl:PRIMARY> cfg.members[3].priority=0
# 设置为隐藏节点
my_repl:PRIMARY> cfg.members[3].hidden=true
# 设置延时时长为 120 秒
my_repl:PRIMARY> cfg.members[3].slaveDelay=120
# 应用配置
my_repl:PRIMARY> rs.reconfig(cfg)    

做完上述操作,28020 节点就成为了一个延时节点。

查看从库的同步状态:

$ mongo --port 28017 admin
MongoDB shell version v3.6.12
connecting to: mongodb://127.0.0.1:28017/admin?gssapiServiceName=mongodb
...
my_repl:PRIMARY> rs.printSlaveReplicationInfo()
source: 10.0.1.51:28019
	syncedTo: Wed Jun 17 2020 22:12:23 GMT+0800 (CST)
	4 secs (0 hrs) behind the primary 
source: 10.0.1.51:28017
	no replication info, yet.  State: (not reachable/healthy)
source: 10.0.1.51:28020
	syncedTo: Wed Jun 17 2020 22:10:23 GMT+0800 (CST)
	124 secs (0.03 hrs) behind the primary 

可以看到,28020 节点当前落后于主库 124 秒。

Copyright: 采用 知识共享署名4.0 国际许可协议进行许可

Links: https://www.zze.xyz/archives/mongodb2.html

Buy me a cup of coffee ☕.