MongoDB副本集配置和数据迁移实战
环境:Ubuntu 16.04, MongoDB 3.6
基本概念
MongoDB 的副本集就是有自动故障恢复功能的 MongoDB 主从集群。由于 MongoDB 的主从复制功能不支持高可用,所以从 3.2 版本开始已经被废弃了,转而用副本集来代替。一个副本集总会有一个活跃节点(Primary)和若干个备份节点(Secondary),还有一个可选的一个仲裁者(Arbiter)节点来实现HA中的故障切换。
准备工作
- 准备三台服务器(虚拟机亦可),系统全部安装Ubuntu 16.04
例如:
- Primary 192.168.10.58
- Secondary 192.168.10.59
- Arbiter 192.168.10.57
- 三台服务器上都安装好 MongoDB 3.6
参考官方的安装文档 https://docs.mongodb.com/manual/tutorial/install-mongodb-on-ubuntu/
配置副本集 Primary 和 Secondary 节点
- 创建数据目录
$ mkdir -p /mnt/mongodb/replset
- 启动名为“my-repl”的副本集,端口为27017,绑定到任意IP(也可以指定IP)
$ mongod --dbpath /mnt/mongodb/replset --port 27017 --replSet "my-repl" --bind_ip_all
- 初始化副本集
- 用mongo客户端连接 Primary 节点:
$ mongo
- 执行初始化脚本来创建副本集:
输出结果:> rs.initiate({ _id:"my-repl", members:[ {_id:0, host:"192.168.10.58:27017"}, {_id:1, host:"192.168.10.59:27017"} ] });
{ "ok" : 1, "operationTime" : Timestamp(1523237919, 1), "$clusterTime" : { "clusterTime" : Timestamp(1523237919, 1), "signature" : { "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="), "keyId" : NumberLong(0) } } }
- 查看配置结果
配置过程非常简单,可以看到在副本集成员中有0和1两个节点,此时主从两个服务器已经可以工作了,服务器0(Primary)有任何数据的变化都会同步到服务器1(Secondary)上。但是,此时的副本集只是提供了数据备份的功能,并不能达到高可用。如果要达到这一点,那么需要配置一个仲裁者节点(Arbiter)> rs.conf(); { "_id" : "my-repl", "version" : 1, "protocolVersion" : NumberLong(1), "members" : [ { "_id" : 0, "host" : "192.168.10.58:27017", "arbiterOnly" : false, "buildIndexes" : true, "hidden" : false, "priority" : 1, "tags" : { }, "slaveDelay" : NumberLong(0), "votes" : 1 }, { "_id" : 1, "host" : "192.168.10.59:27017", "arbiterOnly" : false, "buildIndexes" : true, "hidden" : false, "priority" : 1, "tags" : { }, "slaveDelay" : NumberLong(0), "votes" : 1 } ], "settings" : { "chainingAllowed" : true, "heartbeatIntervalMillis" : 2000, "heartbeatTimeoutSecs" : 10, "electionTimeoutMillis" : 10000, "catchUpTimeoutMillis" : -1, "catchUpTakeoverDelayMillis" : 30000, "getLastErrorModes" : { }, "getLastErrorDefaults" : { "w" : 1, "wtimeout" : 0 }, "replicaSetId" : ObjectId("5acac41fded47067da446ddd") } }
配置仲裁者(Arbiter)
仲裁者在 Primary 节点发生故障时,参与副本集的选举投票决定哪个副本成为 Primary 节点。仲裁节点不保存数据也不会成为 Primary 节点。
-
仲裁者通常不部署在大磁盘空间的服务器上,因此为了最小化默认创建数据,修改配置:
$ vim /etc/mongod.conf
storage.journal.enabled=false storage.mmapv1.smallFiles = true.
-
创建仲裁者目录并启动服务
$ mkdir /mnt/mongodb/arbiter $ mongod --port 27017 --dbpath /mnt/mongodb/arbiter --replSet 'my-repl' --bind_ip_all
-
把仲裁者添加到副本集中
-
连接至 Primary 服务器
$mongo --host 192.168.10.58
my-repl:PRIMARY> rs.addArb("192.168.10.57:27017") { "ok" : 1, "operationTime" : Timestamp(1523326877, 1), "$clusterTime" : { "clusterTime" : Timestamp(1523326877, 1), "signature" : { "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="), "keyId" : NumberLong(0) } } }
-
-
查看副本集的效果:
>rs.status(); my-repl:PRIMARY> rs.status(); { "set" : "my-repl", "date" : ISODate("2018-04-10T02:21:44.826Z"), "myState" : 1, "term" : NumberLong(2), "heartbeatIntervalMillis" : NumberLong(2000), "optimes" : { "lastCommittedOpTime" : { "ts" : Timestamp(1523326895, 1), "t" : NumberLong(2) }, "readConcernMajorityOpTime" : { "ts" : Timestamp(1523326895, 1), "t" : NumberLong(2) }, "appliedOpTime" : { "ts" : Timestamp(1523326895, 1), "t" : NumberLong(2) }, "durableOpTime" : { "ts" : Timestamp(1523326895, 1), "t" : NumberLong(2) } }, "members" : [ { "_id" : 0, "name" : "192.168.10.58:27017", "health" : 1, "state" : 1, "stateStr" : "PRIMARY", "uptime" : 2891, "optime" : { "ts" : Timestamp(1523326895, 1), "t" : NumberLong(2) }, "optimeDate" : ISODate("2018-04-10T02:21:35Z"), "electionTime" : Timestamp(1523324284, 1), "electionDate" : ISODate("2018-04-10T01:38:04Z"), "configVersion" : 2, "self" : true }, { "_id" : 1, "name" : "192.168.10.59:27017", "health" : 1, "state" : 2, "stateStr" : "SECONDARY", "uptime" : 2624, "optime" : { "ts" : Timestamp(1523326895, 1), "t" : NumberLong(2) }, "optimeDurable" : { "ts" : Timestamp(1523326895, 1), "t" : NumberLong(2) }, "optimeDate" : ISODate("2018-04-10T02:21:35Z"), "optimeDurableDate" : ISODate("2018-04-10T02:21:35Z"), "lastHeartbeat" : ISODate("2018-04-10T02:21:43.080Z"), "lastHeartbeatRecv" : ISODate("2018-04-10T02:21:43.083Z"), "pingMs" : NumberLong(0), "syncingTo" : "192.168.10.58:27017", "configVersion" : 2 }, { "_id" : 2, "name" : "192.168.10.57:27017", "health" : 1, "state" : 7, "stateStr" : "ARBITER", "uptime" : 27, "lastHeartbeat" : ISODate("2018-04-10T02:21:43.079Z"), "lastHeartbeatRecv" : ISODate("2018-04-10T02:21:42.088Z"), "pingMs" : NumberLong(0), "configVersion" : 2 } ], "ok" : 1, "operationTime" : Timestamp(1523326895, 1), "$clusterTime" : { "clusterTime" : Timestamp(1523326895, 1), "signature" : { "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="), "keyId" : NumberLong(0) } } }
可以看到状态显示:服务器0为 Primary, 服务器1为 Secondary, 服务器2为 Arbiter。
此时带有高可用的副本集已经配置完成,Arbiter 会监控 Primary 节点的运行情况,如果服务器0发生了宕机,那么仲裁者 Arbiter 节点会发起选举,最终选取多个 Secondary 中的某一个来作为 Primary 节点。我们测试的架构中只有一个 Secondary 节点,那么此服务器1就会成为 Primary。而当服务器0恢复工作时,它会被当成 Secondary 来运行。 -
副本优先级
副本集会在 Primary 节点出现故障时把 Secondary 提升为 Primary,但是很多情况下 Secondary 只是作为备用节点,我们不希望它长期作为 Primary 节点运行。那么为了达到这个目的,我们修改副本的优先级。- 连接 Primary 节点,执行以下脚本:
cfg = rs.conf() cfg.members[0].priority = 10 cfg.members[1].priority = 5 rs.reconfig(cfg) { "ok" : 1, "operationTime" : Timestamp(1523411797, 2), "$clusterTime" : { "clusterTime" : Timestamp(1523411797, 2), "signature" : { "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="), "keyId" : NumberLong(0) } } }
- 如果在非 Prmiary 上比如Arbiter上运行,会报下面的错误:
{ "ok" : 0, "errmsg" : "replSetReconfig should only be run on PRIMARY, but my state is ARBITER; use the \"force\" argument to override", "code" : 10107, "codeName" : "NotMaster" }
- 以上操作让服务器0的优先级高于服务器1,那么当服务器0从故障中恢复时,它会重新成为 Primary 节点来提供服务。
数据迁移
在配置副本集之前,你可能已经存在了一个单独的 MongoDB 实例存储了一些数据,你需要把原来实例中的数据迁移到新的副本集中。(你也可以一开始就把原来的单个实例配置成副本集的 Primary 节点,然后新增副本来同步数据,此方法不在本文讨论范围之内)
-
登录原实例所在的服务器,导出数据:
$ mongoexport -h localhost -p 27017 -u xxx -p xxx -d MY_DB_NAME -c MY_COLLECTION_NAME -o MY_COLLECTION_NAME.dmp
在阿里云上测试了一个导出数据大约为1.2G大小的集合,导出性能如下:
- 时间:3分11秒
- 导出1728423条记录,每秒读取记录数=1728423/191=9050条/秒
- 导出1264441038字节,每秒处理字节数=1264441038/191=6.6M/秒
-
将导出的数据文件 MY_COLLECTION_NAME.dmp 已任何方式(比如scp)同步到 Primary 节点所在的服务器0上
-
导入数据至副本集 Primay 节点
mongoimport -h my-repl/192.168.10.58:27017 -d MY_DB_NAME -c MY_COLLECTION_NAME --file MY_COLLECTION_NAME.dmp
测试导入性能
- 时间:82秒
- 导入1728423条记录,每秒写入记录数=1728423/82=21078条/秒
- 导入1264441038字节,每秒处理字节数=1264441038/82=14.7M/秒
注意:由于不是相同的服务器,所以不能通过这个来比较导入和导出的性能差别,只能所谓一个参考:
-
总结:
- 在 Primary 节点导入数据后,登录 Secondary 节点查看,可以看到一百七十多万条数据全部复制过去了,主从复制成功。
- 从性能分析结果来看,MongoDB的读取和写入性能是比较好的,特别是导入的同时还要和副本之间同步数据。
转载于:https://my.oschina.net/u/168138/blog/1838207