MongoDB副本集配置和数据迁移实战

Wesley13
• 阅读 815

MongoDB副本集配置和数据迁移实战

https://gitee.com/et/ops/blob/master/MongoDB副本集配置和数据迁移实战.md

环境:Ubuntu 16.04, MongoDB 3.6

基本概念

MongoDB 的副本集就是有自动故障恢复功能的 MongoDB 主从集群。由于 MongoDB 的主从复制功能不支持高可用,所以从 3.2 版本开始已经被废弃了,转而用副本集来代替。一个副本集总会有一个活跃节点(Primary)和若干个备份节点(Secondary),还有一个可选的一个仲裁者(Arbiter)节点来实现HA中的故障切换。

准备工作

配置副本集 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)
            }
        }
    }
    
    • 查看配置结果

      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")
      }
      

      }

    配置过程非常简单,可以看到在副本集成员中有0和1两个节点,此时主从两个服务器已经可以工作了,服务器0(Primary)有任何数据的变化都会同步到服务器1(Secondary)上。但是,此时的副本集只是提供了数据备份的功能,并不能达到高可用。如果要达到这一点,那么需要配置一个仲裁者节点(Arbiter)

配置仲裁者(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的读取和写入性能是比较好的,特别是导入的同时还要和副本之间同步数据。
点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
4个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Wesley13 Wesley13
3年前
NoSQL之MongoDB
1.mongodb介绍2.mongodb安装3.连接mongodb4.mongodb用户管理5.mongodb创建集合、数据管理6.php的mongodb扩展7.php的mongo扩展8.mongodb副本集介绍9.mongodb副本集搭建10.mongodb副本集测试11.mongodb分片介
Wesley13 Wesley13
3年前
MongoDB 副本集之入门篇
前言:mongodb因为高性能、高可用性、支持分片等特性,作为非关系型数据库被大家广泛使用。其高可用性主要是体现在mongodb的副本集上面(可以简单理解为一主多从的集群),本篇文章主要从副本集介绍、本地搭建副本集、副本集读写数据这三个方面来带大家认识下mongodb副本集。一、mongodb副本集介绍—
Wesley13 Wesley13
3年前
MongoDB——主从复制&副本集
MongoDB——主从复制&副本集博客分类:javamongodb前面我说到了,MongoDB数据库支持服务器之间的数据复制。支持主从模式及服务器之间的相互复制。目的当然是实现负载均衡与故障恢复。Mongo中内置了两种技术:主从复制、副本集。下面就简单介绍这两种模式的配置。一、   主从复制主从复制是一个简单的数
Stella981 Stella981
3年前
MongoDb分片集群认证
本文主要基于已经搭建好的未认证集群,结合上篇Mongodb副本集分片集群模式环境部署(https://www.cnblogs.com/woxingwoxue/p/9875878.html),MongoDb分片集群认证几个主要流程1.在分片集群环境中,副本集内成员之间需要用keyFile认证,mongos与配置服务器,副本集之间也要keyFi
Stella981 Stella981
3年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Easter79 Easter79
3年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移