一 MongoDB 复制(副本集)
1.1 复制概述
MongoDB复制是将数据同步在多个服务器的过程。
复制提供了数据的冗余备份,并在多个服务器上存储数据副本,提高了数据的可用性, 并可以保证数据的安全性。
复制还允许从硬件故障和服务中断中恢复数据。
1.2 复制意义
- 保障数据的安全性
- 数据高可用性 (24*7)
- 灾难恢复
- 无需停机维护(如备份,重建索引,压缩)
- 分布式读取数据
注意:副本集不是为了提高读性能存在的,在进行oplog的时候,读操作是被阻塞的;
提高读取性能应该使用分片和索引,它的存在更多是作为数据冗余,备份;
尤其当主库本来就面临着大量的写入压力,对于副本集的节点,也同样会面临写的压力。
1.3 MongoDB复制原理
mongodb的复制至少需要两个节点。其中一个是主节点,负责处理客户端请求,其余的都是从节点,负责复制主节点上的数据。
mongodb各个节点常见的搭配方式为:一主一从、一主多从。
主节点记录在其上的所有操作oplog,从节点定期轮询主节点获取这些操作,然后对自己的数据副本执行这些操作,从而保证从节点的数据与主节点一致。
MongoDB复制结构图如下所示:
添加Heartbeat监控副本之间的心跳架构:
设置一个仲裁节点架构:
说明:客户端从主节点读取数据,在客户端写入数据到主节点时, 主节点与从节点进行数据交互保障数据的一致性。
1.4 MongoDB复制过程
Primary节点写入数据,Secondary通过读取Primary的oplog得到复制信息,开始复制数据并且将复制信息写入到自己的oplog。如果某个操作失败,则备份节点停止从当前数据源复制数据。如果某个备份节点由于某些原因挂掉了,当重新启动后,就会自动从oplog的最后一个操作开始同步。同步完成后,将信息写入自己的oplog,由于复制操作是先复制数据,复制完成后再写入oplog,有可能相同的操作会同步两份,不过MongoDB在设计之初就考虑到这个问题,将oplog的同一个操作执行多次,与执行一次的效果是一样的。
当Primary节点完成数据操作后,Secondary的数据同步过程如下:
-
检查自己local库的oplog.rs集合找出最近的时间戳。
- 检查Primary节点local库oplog.rs集合,找出大于此时间戳的记录。
- 将找到的记录插入到自己的oplog.rs集合中,并执行这些操作。
副本集的同步和主从同步一样,都是异步同步的过程,不同的是副本集有个自动故障转移的功能。其原理是:slave端从primary端获取日志,然后在自己身上完全顺序的执行日志所记录的各种操作(该日志是不记录查询操作的),这个日志就是local数据库中的oplog.rs表,默认在64位机器上这个表是比较大的,占磁盘大小的5%,oplog.rs的大小可以在启动参数中设 定:--oplogSize 1000,单位是M。
注意:在副本集的环境中,要是所有的Secondary都宕机了,只剩下Primary。最后Primary会变成Secondary,不能提供服务。
1.5 副本集
副本集特征:
- N 个节点的集群;
- 任何节点可作为主节点;
- 所有写入操作都在主节点上;
- 自动故障转移;
- 自动恢复。
集群中没有特定的主库,主库是选举产生,如果主库down了,会再选举出一台主库。
mongoDB也可以配置成主从模式,但官方已经不建议使用主从模式了,替代方案是采用副本集的模式。
副本集有以下特点:
- 最小构成是:primary,secondary,arbiter,一般部署是:primary,2 secondary。
- 成员数应该为奇数,如果为偶数的情况下添加arbiter,arbiter不保存数据,只投票。
- 最大50 members,但是只能有 7 voting members,其他是non-voting members。
注意:在副本集的环境中,如果所有的Secondary都宕机了,只剩下Primary。最后Primary会变成Secondary,且不能提供服务。
二 前期准备
-
NTP同步;
- 关闭SELinux;
- 关闭防火墙或放通相应规则;
- 所有节点安装MongoDB,参考《002.MongoDB社区版安装》;
节点规划:
追加解析:
1 [root@mongodb01 ~]# vi /etc/hosts 2 172.24.8.71 mongodb01 3 172.24.8.72 mongodb02 4 172.24.8.73 mongodb03 5 172.24.8.74 mongodb04 6 172.24.8.75 arbite
提示:所有节点均建议添加以上解析。
三 副本集正式部署
3.1 开启远程连接及副本集
1 [root@mongodb01 ~]# vi /etc/mongod.conf 2 …… 3 net: 4 port: 27017 5 bindIp: 172.24.8.71 #根据不同节点配置 6 …… 7 replication: 8 replSetName: my_rep #开启副本集,所有节点必须一致 9 …… 10 [root@mongodb01 ~]# systemctl start mongod.service 11 [root@mongodb01 ~]# systemctl enable mongod.service
注意:在完成复制集初始化、新建用户等操作之前必须保持security.authorization:enabled为注释状态。
3.2 配置复制级成员
1 [root@mongodb01 ~]# mongo --host 172.24.8.71 2 > config = { _id: "my_rep", members: [ 3 {_id: 0, host: "172.24.8.71:27017"}, 4 {_id: 1, host: "172.24.8.72:27017"}, 5 {_id: 2, host: "172.24.8.73:27017"}] 6 }
参数解释:
"_id": 副本集的名称
"members": 副本集的服务器列表
"_id": 服务器的唯一ID
"host": 服务器主机
"priority": 是优先级,默认为1,优先级0为被动节点,不能成为活跃节点。优先级不为0则按照有大到小选出活跃节点。
"arbiterOnly": 仲裁节点,只参与投票,不接收数据,也不能成为活跃节点。
3.3 初始化副本集
1 > rs.initiate(config) 2 my_rep1:PRIMARY> rs.status() #查看集群状态
3.4 创建管理员用户
1 [root@mongodb01 ~]# mongo --host 172.24.8.71 2 my_rep:PRIMARY> use admin #进入admin数据库 3 my_rep:PRIMARY> db.createUser({ user: "admin", pwd: "admin", roles: [{ role: "userAdminAnyDatabase", db: "admin" }] }) 4 my_rep:PRIMARY> db.auth("admin", "admin") #验证创建结果 5 1
提示:以上为建议项,建议创建一个管理员用于内部管理MongoDB。
mongodb中的用户是基于身份role的,该管理员账户的 role是 userAdminAnyDatabase。 ‘userAdmin’代表用户管理身份,’AnyDatabase’ 代表可以管理任何数据库。
db.auth()可以验证 用户。
1 my_rep:PRIMARY> db.getUsers() 2 > db.system.users.find().pretty() #查看全局所有账户
提示:mongo shell 可通过.pretty() 对输出进行JSON格式化,以便提高输出的可读性。
3.5 创建集群用户
1 [root@mongodb01 ~]# mongo --host 172.24.8.71 -u admin -p admin 2 my_rep1:PRIMARY> use admin 3 switched to db admin 4 my_rep1:PRIMARY> db.createUser({user:"clusteradmin",pwd:"clusteradmin",roles:[{role:"clusterAdmin",db:"admin"}]})
提示:3.2——3.5仅需在集群任何一台节点操作即可。
3.6 确认验证
1 [root@mongodb01 ~]# mongo --host 172.24.8.72 -u clusteradmin -p clusteradmin 2 my_rep:SECONDARY> rs.status() 3 my_rep:SECONDARY> db.serverStatus().repl.primary #查看主节点 4 172.24.8.73:27017
四 集群开启权限
建议使用keyfile访问控制的方式建立各个节点间的安全认证机制。
4.1 配置key
1 [root@mongodb01 ~]# mkdir -p /usr/local/keyfile 2 [root@mongodb01 ~]# openssl rand -base64 100 > /usr/local/keyfile/mongodb_keyfile 3 [root@mongodb01 ~]# chmod 600 /usr/local/keyfile/mongodb_keyfile 4 [root@mongodb01 ~]# chown -R mongod:mongod /usr/local/keyfile/ 5 [root@mongodb01 ~]# scp -rp /usr/local/keyfile/ root@mongodb02:/usr/local/ 6 [root@mongodb01 ~]# scp -rp /usr/local/keyfile/ root@mongodb03:/usr/local/ 7 [root@mongodb01 ~]# scp -rp /usr/local/keyfile/ root@mongodb04:/usr/local/ 8 [root@mongodb01 ~]# scp -rp /usr/local/keyfile/ root@arbiter:/usr/local/ 9 10 [root@mongodb02 ~]# chown -R mongod:mongod /usr/local/keyfile/ 11 [root@mongodb03 ~]# chown -R mongod:mongod /usr/local/keyfile/ 12 [root@mongodb04 ~]# chown -R mongod:mongod /usr/local/keyfile/ 13 [root@arbiter~]# chown -R mongod:mongod /usr/local/keyfile/
4.2 开启权限
1 [root@mongodb01 ~]# vi /etc/mongod.conf 2 …… 3 security: #取消注释 4 authorization: enabled #开启验证 5 keyFile: /usr/local/keyfile/mongodb_keyfile #key文件 6 …… 7 [root@mongodb01 ~]# systemctl restart mongod.service
提示:需要在所有节点进行开启操作。
4.3 测试登陆
1 [root@mongodb01 ~]# mongo --host 172.24.8.71 -u clusteradmin -p clusteradmin 2 my_rep:PRIMARY> rs.status() 3 my_rep:PRIMARY> db.serverStatus().repl.primary #查看主节点 4 172.24.8.71:27017
五 成员管理
5.1 SECONDARY节点增加
1 [root@mongodb01 ~]# scp -rp /etc/mongod.conf root@mongodb04:/etc/mongod.conf 2 [root@mongodb04 ~]# vi /etc/mongod.conf #修改IP即可 3 net: 4 port: 27017 5 bindIp: 172.24.8.74 6 [root@mongodb04 ~]# systemctl restart mongod.service 7 [root@mongodb04 ~]# systemctl enable mongod.service 8 [root@mongodb01 ~]# mongo --host 172.24.8.71 -u clusteradmin -p clusteradmin 9 my_rep:PRIMARY> rs.add("172.24.8.74:27017")
5.2 节点删除
1 my_rep:PRIMARY> rs.remove("172.24.8.74:27017")
提示:修改副本集成员配置时的限制:
- 不能修改_id;
- 不能将当前执行rs.reconfig命令的成员的优先级设置为 0;
- 不能将仲裁者成员变为非仲裁者成员,反正亦然;
- 不能将buildIndexes由false改为 true。
5.3 Secondary开放临时读
默认情况下,Secondary是不提供服务的,即不能读和写。在特殊情况下需要读的可执行rs.slaveOk() ,只对当前连接有效。
5.4 节点提权
默认所有的节点priority都为1,自动选举primary,可通过以下方式手动在已运行的副本集中指定primary。
注意:以下方式更换主节点必须在当前primary节点操作。
1 my_rep:SECONDARY> db.serverStatus().repl.primary #查看主节点 2 172.24.8.72:27017 3 [root@mongodb02 ~]# mongo --host 172.24.8.72 -u clusteradmin -p clusteradmin 4 my_rep:PRIMARY> rs.status() #查看当前副本集状态
1 my_rep:PRIMARY> rs.conf()
1 my_rep:PRIMARY> newcfg=rs.conf() #当前conf写入变量 2 my_rep:PRIMARY> newcfg.members[0].priority=2 #修改conf中members序号0,即172.24.8.71优先级为2 3 2 4 my_rep:PRIMARY> rs.reconfig(newcfg #重新载入conf 5 [root@mongodb01 ~]# mongo --host 172.24.8.71 -u clusteradmin -p clusteradmin 6 my_rep:PRIMARY> db.serverStatus().repl.primary #再次登录查看primary节点 7 172.24.8.71:27017
5.5 ARBITER节点添加
1 [root@mongodb01 ~]# scp -rp /etc/mongod.conf root@arbiter:/etc/mongod.conf 2 [root@mongodb04 ~]# vi /etc/mongod.conf #修改IP即可 3 net: 4 port: 27017 5 bindIp: 172.24.8.75 6 [root@mongodb04 ~]# systemctl restart mongod.service 7 [root@mongodb04 ~]# systemctl enable mongod.service 8 [root@mongodb01 ~]# mongo --host 172.24.8.71 -u clusteradmin -p clusteradmin 9 my_rep:PRIMARY> rs.addArb("172.24.8.75:27017") 10 my_rep:PRIMARY> rs.status()
1 my_rep:PRIMARY> rs.conf()
提示:副本集要求参与选举投票(vote)的节点数为奇数,当我们实际环境中因为机器等原因限制只有两个(或偶数)的节点,这时为了实现 Automatic Failover引入另一类节点:仲裁者(arbiter),仲裁者只参与投票不拥有实际的数据,并且不提供任何服务,因此它对物理资源要求不严格。
5.6 修改成员状态
1 my_rep:PRIMARY> rs.stepDown()
提示:rs.stepDown(60)表示让主节点退化为备份节点,并维持60秒。如果60s内没有新的主节点被选举出来,那么当前节点可以要求重新参与进行选举。
5.7 锁定主节点
将所有的备份节点的priority和votes都设置为0,这样只有主节点有投票权所以无论备份节点是否存在都不会导致主节点的状态由primary变成other。
1 my_rep:PRIMARY> newconf=rs.config() 2 my_rep:PRIMARY> newconf.members[1].priority=0 3 my_rep:PRIMARY> newconf.members[1].votes=0 4 my_rep:PRIMARY> rs.reconfig(newconf)
5.8 阻止成员选举
如果需要对主节点进行维护操作,但是不希望这段时间内其它成员选举为主节点,可以在每个备份节点上执行freeze命令,以强制它们始终处于备份节点的状态。命令以秒为单位。
1 my_rep:SECONDARY> rs.freeze(3600) #保持1个小时处于备份节点状态。 2 my_rep:SECONDARY> rs.freeze(0) #再次在备份节点执行且将时间指定为0就是“释放”备份节点。
注意:如果在退位的备份节点上执行rs.freeze(0),可以让退位的备份节点重新变为主节点。
六 副本集管理
6.1 查看复制情况
1 my_rep:PRIMARY> db.printSlaveReplicationInfo() 2 source: 172.24.8.72:27017 3 syncedTo: Tue May 28 2019 19:43:40 GMT+0800 (CST) 4 0 secs (0 hrs) behind the primary 5 source: 172.24.8.73:27017 6 syncedTo: Tue May 28 2019 19:43:40 GMT+0800 (CST) 7 0 secs (0 hrs) behind the primary
解释:
source:从库的ip和端口。
syncedTo:目前的同步情况,以及最后一次同步的时间。
在数据库内容不变的情况下是不同步的,数据库变动就会马上同步。
6.2 副本集复制链配置
MongoDB根据ping时间选择同步源,一个成员向另一个成员发送心跳请求,获取心跳请求所耗费的时间(rs.status()中的"pingMs"记录了成员到达相关成员的所花费的平均时间)。
MongosDB维护着不同成员间请求的平均花费时间。选择同步源时,会选择一个离自己比较近而且数据比自己新的成员。但是同一数据中心的成员可能会从同一数据中心的其他成员处复制,而不是从位于另一个数据中心的主节点处复制(这样可以减少网络流量),所以会出现复制链的情况,复制链越长会导致主节点的操作复制到所有的服务器所花费的时间越长,从而影响一定的性能。
1 [root@mongodb03 ~]# mongo --host 172.24.8.73 -u clusteradmin -p clusteradmin 2 my_rep:SECONDARY> db.adminCommand({"replSetGetStatus":1})['syncingTo']; #查看备份节点的复制源 3 172.24.8.71:27017 4 my_rep:SECONDARY> db.adminCommand({"replSetSyncFrom":"172.24.8.72:27017"}) #配置复制源
6.3 查看副本集状态
1 my_rep:PRIMARY> rs.status()
6.4 查看副本集配置
1 my_rep:PRIMARY> rs.conf() #查看节点配置
6.5 强制重新配置副本集
如果副本集无法选出新的主节点,这时需要重新配置副本集。可以在备份节点上调用rs.reconfig(conf,{"force":ture})强制重新配置副本集。
备份节点收到新的配置文件之后,就会修改自身的配置,并且将新的配置发送给副本集中的其他成员。副本集的其他成员收到新的配置文件之后,会判断配置文件的发送者是否是它们当前配置中的一个成员,如果是,才会用新的配置文件对自己进行重新配置。
所以,如果新的配置修改了某些成员的主机名,则应该关闭被修改主机名的节点,并以单机模式启动,手动修改locak.system.replset文档,然后以副本集的方式重新启动。
注意:conf必须是正确、有效的配置。而且强制重新配置只允许在备份节点执行。
6.6 查看副本集log状态
1 my_rep:PRIMARY> rs.printReplicationInfo() 2 configured oplog size: 1194.595947265625MB 3 log length start to end: 14027secs (3.9hrs) 4 oplog first event time: Tue May 28 2019 17:21:54 GMT+0800 (CST) 5 oplog last event time: Tue May 28 2019 21:15:41 GMT+0800 (CST) 6 now: Tue May 28 2019 21:15:48 GMT+0800 (CST)
解释:
configured oplog size:oplog配置的大小
log length start to end:oplog包含的操作时长。
oplog first event time:oplog第一条操作的时间。
oplog last event time:oplog最后一条操作的时间。
now:当前时间。
注意:oplog中第一条操作与最后一条操作的时间差就是操作日志的长度。
6.7 查看复制延时
1 my_rep:PRIMARY> rs.printSlaveReplicationInfo() 2 source: 172.24.8.72:27017 3 syncedTo: Tue May 28 2019 21:17:40 GMT+0800 (CST) 4 0 secs (0 hrs) behind the primary 5 source: 172.24.8.73:27017 6 syncedTo: Tue May 28 2019 21:17:40 GMT+0800 (CST) 7 0 secs (0 hrs) behind the primary 8 source: 172.24.8.74:27017 9 syncedTo: Tue May 28 2019 21:17:40 GMT+0800 (CST) 10 0 secs (0 hrs) behind the primary
6.8 副本集信息查看(监控)命令汇总
1.复制集状态查询:rs.status()
2.查看当前副本集oplog状态:rs.printReplicationInfo()
3.查看复制延迟:rs.printSlaveReplicationInfo()
4.查看服务状态详情:db.serverStatus()
5.查询副本集配置:rs.conf()
6.主副本查询:db.isMaster()
6.9 其他常见维护命令
通过rs.help()命令,可以查看副本集相关操作命令:
1 replSetHO:PRIMARY> rs.help() 2 rs.status() #查看副本集整体健康状态 3 rs.initiate() #使用默认配置初始化副本集 4 rs.initiate(cfg) #使用指定配置,初始化副本集;这是我们使用的命令 5 rs.conf() #从local.system.replset获取副本集当前配置信息 6 rs.reconfig(cfg) #指定配置信息重置副本集;指定第二个参数{force:true},来强制更新 7 rs.add(hostportstr) #使用默认配置,给副本集添加新成员 8 rs.add(membercfgobj) #使用指定配置,给副本集添加新成员 9 rs.addArb(hostportstr) #给副本集添加一个仲裁节点,只投票,不会成为数据节点 10 rs.stepDown([stepdownSecs, catchUpSecs]) #给PRIMARY降权,使之在指定时间内成为SECONDARY,会恢复 11 rs.syncFrom(hostportstr) #使SECONDARY从指定的服务器同步数据 12 rs.freeze(secs) #让自己在指定秒数内不会成为PRIMARY 13 rs.remove(hostportstr) #从副本集删除指定节点 14 rs.slaveOk() #SECONDARY节点默认是不能查询的,需要执行该命令,使之可以查询 15 rs.printReplicationInfo() #查看操作日志以及日志时间 16 rs.printSlaveReplicationInfo() #查看所有SECONDARY延迟情况 17 db.isMaster() #查询当前PRIMARY信息
提示:更多副本集选举参考官方:https://docs.mongodb.com/manual/core/replica-set-elections/。
七 副本集复制功能测试
7.1 授权
1 [root@mongodb01 ~]# mongo --host 172.24.8.71 -u admin -p admin 2 my_rep:PRIMARY> use admin 3 my_rep:PRIMARY> db.grantRolesToUser( "admin",[{ role: "dbOwner",db:"mydb" }])
7.2 插入数据
1 my_rep:PRIMARY> use mydb 2 my_rep:PRIMARY> db.age01.insert({name: 'zhangsan', 3 age: '18', 4 tel: '123456781', 5 love: ['apple','banana'] 6 }
7.3 查看同步
1 [root@mongodb02 ~]# mongo --host 172.24.8.72 -u admin -p admin 2 my_rep:SECONDARY> rs.slaveOk() 3 my_rep:SECONDARY> use mydb 4 my_rep:SECONDARY> db.age01.count() 5 1
八 副本集故障转移功能测试
8.1 关闭主节点
1 [root@mongodb01 ~]# mongo --host 172.24.8.71 -u clusteradmin -p clusteradmin 2 my_rep:PRIMARY> rs.status() 3 my_rep:PRIMARY> use admin 4 switched to db admin 5 my_rep:PRIMARY> db.shutdownServer() 6 [root@mongodb02 ~]# mongo --host 172.24.8.72 -u clusteradmin -p clusteradmin #登录任意非关闭节点 7 my_rep:PRIMARY> rs.status() 8 my_rep:PRIMARY> db.serverStatus().repl.primary #查看主节点 9 172.24.8.72:27017
8.2 插入数据
1 [root@mongodb02 ~]# mongo --host 172.24.8.72 -u admin -p admin 2 my_rep:PRIMARY> use mydb 3 db.age01.insertOne({name: 'wanger', age: '15', tel: '123456783', love: ['pear','orange']})
8.3 开启关闭节点
1 [root@mongodb01 ~]# systemctl restart mongod.service 2 [root@mongodb01 ~]# mongo --host 172.24.8.71 -u admin -p admin 3 my_rep:SECONDARY> use mydb 4 switched to db mydb 5 my_rep:SECONDARY> rs.slaveOk() 6 my_rep:SECONDARY> db.age01.find().pretty()
提示:当关闭节点mongo服务重启启动后,能正常以SECONDARY加入副本集,并且自动同步相关数据。
注意:所有的Secondary都宕机、或则副本集中只剩下一个节点,则该节点只能为Secondary节点,也就意味着整个集群只能进行读操作而不能进行写操作,当其他节点恢复时,之前的primary节点仍然是primary节点。
当某个节点宕机后重新启动该节点会有一段的时间(时间长短视集群的数据量和宕机时间而定)导致整个集群中所有节点都成为secondary而无法进行写操作(如果应用程序没有设置相应的ReadReference也可能不能进行读取操作)。
官方推荐的最小的副本集也应该具备一个primary节点和两个secondary节点。两个节点的副本集不具备真正的故障转移能力。
参考:https://www.cnblogs.com/zhoujinyi/p/3554010.html
https://www.cnblogs.com/chenmh/p/8681867.html