导航

Mongodb集群搭建之 Replica Set

Posted on 2018-08-07 16:09  许爱琪  阅读(612)  评论(0编辑  收藏  举报

Mongodb集群搭建之 Replica Set

Replica Set

       中文翻译叫做副本集,不过我并不喜欢把英文翻译成中文,总是感觉怪怪的。其实简单来说就是集群当中包含了多份数据,保证主节点挂掉了,备节点能继续提供数据服务,提供的前提就是数据需要和主节点一致。如下图:

  Mongodb(M)表示主节点,Mongodb(S)表示备节点,Mongodb(A)表示仲裁节点。主备节点存储数据,仲裁节点不存储数据。客户端同时连接主节点与备节点,不连接仲裁节点。

       默认设置下,主节点提供所有增删查改服务,备节点不提供任何服务。但是可以通过设置使备节点提供查询服务,这样就可以减少主节点的压力,当客户端进行数据查询时,请求自动转到备节点上。这个设置叫做Read Preference Modes,同时Java客户端提供了简单的配置方式,可以不必直接对数据库进行操作。

       仲裁节点是一种特殊的节点,它本身并不存储数据,主要的作用是决定哪一个备节点在主节点挂掉之后提升为主节点,所以客户端不需要连接此节点。这里虽然只有一个备节点,但是仍然需要一个仲裁节点来提升备节点级别。我开始也不相信必须要有仲裁节点,但是自己也试过没仲裁节点的话,主节点挂了备节点还是备节点,所以咱们还是需要它的。

介绍完了集群方案,那么现在就开始搭建了。


鉴于实验条件有限,本文都是通过Docker虚拟机实现

方案一:快速搭建 简单了解副本集集群(简单命令自动创建)

一、创建数据持久化本地目录:

mkdir -p /root/application/program/mongodb/data

二、快速启动Docker创建容器(Docker image已安装好MongoDB:3.6.3)

docker run -d -v `pwd`/data/master:/mongodb -p 27017:27017 docker.io/mongodb:3.6.3 /usr/sbin/init

三:启动mongo shell并启动相关进程

mongo --nodb

#创建一个包含三个服务器的副本集:一个主服务器和两个备份服务器

replicaSet = new ReplSetTest({"nodes" : 3})

#启动3个mongod进程

replicaSet.startSet()

#配置复制功能

replicaSet.initiate()

现在就已经有了3个mongod进程,分别运行在20000、20001和20002端口。

这样一套包含一个主服务器和两个备份服务器的副本集集群 就搭建好了


方案二:手动创建(可以在一台机器上通过不同端口搭建,也可以使用三台独立的虚拟机搭建)

1.建立数据文件夹

一般情况下不会把数据目录建立在mongodb的解压目录下,不过这里方便起见,就建在mongodb解压目录下吧。

#三个目录分别对应主,备,仲裁节点  

mkdir -p /root/application/program/mongodb/data/master   

mkdir -p /root/application/program/mongodb/data/slaver   

mkdir -p /root/application/program/mongodb/data/arbiter    


2.快速创建并启动三个Docker容器(可以理解为3台虚拟机,有独立的IP)

docker run -d -v `pwd`/data/master:/mongodb -p 27017:27017 docker.io/mongodb:3.6.3 /usr/sbin/init

docker run -d -v `pwd`/data/slaver:/mongodb -p 27018:27017 docker.io/mongodb:3.6.3 /usr/sbin/init

docker run -d -v `pwd`/data/arbiter:/mongodb -p 27019:27017 docker.io/mongodb:3.6.3 /usr/sbin/init


3.建立配置文件

由于配置比较多,所以我们将配置写到文件里。

#master.conf 

  1. dbpath=/mongodb/data/master  

  2. logpath=/mongodb/log/master.log  

  3. pidfilepath=/mongodb/master.pid  

  4. directoryperdb=true  

  5. logappend=true  

  6. replSet=testrs  

  7. bind_ip=172.17.0.2  

  8. port=27017  

  9. oplogSize=10000  

  10. fork=true  

  11. noprealloc=true  


#slaver.conf  

  1. dbpath=/mongodb/data/slaver  

  2. logpath=/mongodb/log/slaver.log  

  3. pidfilepath=/mongodb/slaver.pid  

  4. directoryperdb=true  

  5. logappend=true  

  6. replSet=testrs  

  7. bind_ip=172.17.0.3

  8. port=27017  

  9. oplogSize=10000  

  10. fork=true  

  11. noprealloc=true  


#arbiter.conf  

  1. dbpath=/mongodb/data/arbiter  

  2. logpath=/mongodb/log/arbiter.log  

  3. pidfilepath=/mongodb/arbiter.pid  

  4. directoryperdb=true  

  5. logappend=true  

  6. replSet=testrs  

  7. bind_ip=172.17.0.4 

  8. port=27017  

  9. oplogSize=10000  

  10. fork=true  

  11. noprealloc=true 


参数解释:

dbpath:数据存放目录

logpath:日志存放路径

pidfilepath:进程文件,方便停止mongodb

directoryperdb:为每一个数据库按照数据库名建立文件夹存放

logappend:以追加的方式记录日志

replSet:replica set的名字

bind_ip:mongodb所绑定的ip地址

port:mongodb进程所使用的端口号,默认为27017

oplogSize:mongodb操作日志文件的最大大小。单位为Mb,默认为硬盘剩余空间的5%

fork:以后台方式运行进程

noprealloc:不预先分配存储


4.启动mongodb

进入每个mongodb节点的bin目录下

中间省略使用docker exec -it container_id bash 进入docker容器的步骤

  1. ./monood -f master.conf  

  2. ./mongod -f slaver.conf  

  3. ./mongod -f arbiter.conf  

注意配置文件的路径一定要保证正确,可以是相对路径也可以是绝对路径。

5.配置主,备,仲裁节点

可以通过客户端连接mongodb,也可以直接在三个节点中选择一个连接mongodb。


  1. ./mongo --host 172.17.0.2 --port 27017   #ip和port是某个节点的地址  

  2. >use admin  

  3. >config={ _id:"testrs", members:[ {_id:0,host:'172.17.0.2:27017',priority:2}, {_id:1,host:'172.17.0.3:27017',priority:1},{_id:2,host:'172.17.0.4:27017',arbiterOnly:true}] };

  4. >rs.initiate(config)             #使配置生效  

    或者

  5. >db.adminCommand({"replSetInitiate" : config})

    config是可以任意的名字,当然最好不要是mongodb的关键字,conf,config都可以。最外层的_id表示replica set的名字,members里包含的是所有节点的地址以及优先级。优先级最高的即成为主节点,即这里的172.17.0.2:27017。特别注意的是,对于仲裁节点,需要有个特别的配置——arbiterOnly:true。这个千万不能少了,不然主备模式就不能生效。


      配置的生效时间根据不同的机器配置会有长有短,配置不错的话基本上十几秒内就能生效,有的配置需要一两分钟。如果生效了,执行rs.status()命令会看到如下信息:


    testrs:SECONDARY> rs.status()

    {

    "set" : "testrs",

    "date" : ISODate("2018-08-07T07:29:24.076Z"),

    "myState" : 1,

    "term" : NumberLong(1),

    "heartbeatIntervalMillis" : NumberLong(2000),

    "optimes" : {

    "lastCommittedOpTime" : {

    "ts" : Timestamp(1533626954, 1),

    "t" : NumberLong(1)

    },

    "readConcernMajorityOpTime" : {

    "ts" : Timestamp(1533626954, 1),

    "t" : NumberLong(1)

    },

    "appliedOpTime" : {

    "ts" : Timestamp(1533626954, 1),

    "t" : NumberLong(1)

    },

    "durableOpTime" : {

    "ts" : Timestamp(1533626954, 1),

    "t" : NumberLong(1)

    }

    },

    "members" : [

    {

    "_id" : 0,

    "name" : "172.17.0.2:27017",

    "health" : 1,

    "state" : 1,

    "stateStr" : "PRIMARY",

    "uptime" : 364,

    "optime" : {

    "ts" : Timestamp(1533626954, 1),

    "t" : NumberLong(1)

    },

    "optimeDate" : ISODate("2018-08-07T07:29:14Z"),

    "infoMessage" : "could not find member to sync from",

    "electionTime" : Timestamp(1533626952, 1),

    "electionDate" : ISODate("2018-08-07T07:29:12Z"),

    "configVersion" : 1,

    "self" : true

    },

    {

    "_id" : 1,

    "name" : "172.17.0.3:27017",

    "health" : 1,

    "state" : 2,

    "stateStr" : "SECONDARY",

    "uptime" : 22,

    "optime" : {

    "ts" : Timestamp(1533626954, 1),

    "t" : NumberLong(1)

    },

    "optimeDurable" : {

    "ts" : Timestamp(1533626954, 1),

    "t" : NumberLong(1)

    },

    "optimeDate" : ISODate("2018-08-07T07:29:14Z"),

    "optimeDurableDate" : ISODate("2018-08-07T07:29:14Z"),

    "lastHeartbeat" : ISODate("2018-08-07T07:29:22.187Z"),

    "lastHeartbeatRecv" : ISODate("2018-08-07T07:29:22.807Z"),

    "pingMs" : NumberLong(0),

    "syncingTo" : "172.17.0.2:27017",

    "configVersion" : 1

    },

    {

    "_id" : 2,

    "name" : "172.17.0.4:27017",

    "health" : 1,

    "state" : 7,

    "stateStr" : "ARBITER",

    "uptime" : 22,

    "lastHeartbeat" : ISODate("2018-08-07T07:29:22.186Z"),

    "lastHeartbeatRecv" : ISODate("2018-08-07T07:29:23.681Z"),

    "pingMs" : NumberLong(0),

    "configVersion" : 1

    }

    ],

    "ok" : 1,

    "operationTime" : Timestamp(1533626954, 1),

    "$clusterTime" : {

    "clusterTime" : Timestamp(1533626954, 1),

    "signature" : {

    "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),

    "keyId" : NumberLong(0)

    }

    }

如果配置正在生效,其中会包含如下信息:


  1. "stateStr" : "RECOVERING"  


同时可以查看对应节点的日志,发现正在等待别的节点生效或者正在分配数据文件。


       现在基本上已经完成了集群的所有搭建工作。至于测试工作,可以留给大家自己试试。一个是往主节点插入数据,能从备节点查到之前插入的数据(查询备节点可能会遇到某个问题,可以自己去网上查查看)。二是停掉主节点,备节点能变成主节点提供服务。三是恢复主节点,备节点也能恢复其备的角色,而不是继续充当主的角色。二和三都可以通过rs.status()命令实时查看集群的变化。


5.其它操作


添加、删除新成员

1.新启动一个mongod进程

mongod --replSet spock --dbpath /mongodb/mongodb/bin/datas/m4 --logpath /mongodb/mongodb/bin/logs/m4/mongodb.log --bind_ip 172.17.0.5 --port 27020 --fork 

 

2. 登录其中一个节点,并执行rs.add命令

 mongo 172.17.0.3:27017

 rs.add("172.17.0.5:27020")

 删除的话 执行  rs.remove("172.17.0.5:27020")

 

或者

config = {

"_id" : "spock",

"members" : [

{"_id" : 0, "host" : "172.17.0.2:27017"},

{"_id" : 1, "host" : "172.17.0.3:27018"},

{"_id" : 2, "host" : "172.17.0.4:27019"},

{"_id" : 3, "host" : "172.17.0.5:27020"}

]

}

rs.reconfig(config)

 

MongoDb的“not master and slaveok=false”错误及解决方法

这是正常的,因为SECONDARY是不允许读写的,如果非要解决,方法如下:

SECONDARY> rs.slaveOk();

思考:

1.尽可能在副本集中使用奇数个数据成员,而不是使用仲裁者;

2. 建议大多数成员放在同一个数据中心,保证其中一个数据中心挂掉后,另一个数据中心满足“大多数”;

3.MongoDB只支持单一主节点;

4.设置延迟备份节点,要求优先级置为0(priority : 0),隐藏成员(Hidden: true),避免误操作;

5.推荐配置:两个数据中心使用偶数个数据成员,另外一台配置低的机器做仲裁者 (arbiteronly : true)