什么是副本集呢?打魔兽世界总说打副本,其实这两个概念差不多一个意思。游戏里的副本是指玩家集中在高峰时间去一个场景打怪,会出现玩家暴多怪物少的情况,游戏开发商为了保证玩家的体验度,就为每一批玩家单独开放一个同样的空间同样的数量的怪物,这一个复制的场景就是一个副本,不管有多少个玩家各自在各自的副本里玩不会互相影响。 


       mongoDB的副本也是这个,主从模式其实就是一个单副本的应用,没有很好的扩展性和容错性。而副本集具有多个副本保证了容错性,就算一个副本挂掉了还有很多副本存在,并且解决了上面第一个问题“主节点挂掉了,整个集群内会自动切换”。

 

#######原理和工作机制#########

      主服务器负责整个副本集的读写,副本集定期同步数据备份,一但主节点挂掉,副本节点就会选举一个新的主服务器,副本集中的副本节点在主节点挂掉后通过心跳机制检测到后,就会在集群内发起主节点的选举机制,自动选举一位新的主服务器。

 

 

 搭建高可用的MongoDB集群副本集_第1张图片



 ##################################部署副本集####################################

 副本集机器数量为至少3个

 

 10.2.16.250 主节点

 10.2.16.254 副节点

 10.2.16.251 仲裁服务器

 

 2、分别在每台机器上建立mongodb副本集测试文件夹


#存放整个mongodb文件

mkdir -p /data/mongodbtest/replset 


#存放mongodb数据文件

mkdir -p /data/mongodbtest/replset/data


#进入mongodb文件夹

cd  /data/mongodbtest

 

 3、下载mongodb的安装程序包


wget http://fastdl.mongodb.org/linux/mongodb-linux-x86_64-2.4.8.tgz

 

#解压下载的压缩包  

[root@node1 ~]#tar xvzf mongodb-linux-x86_64-2.4.8.tgz


[root@node1 ~]#mv  mongodb-linux-x86_64-2.48  /usr/local/mongodb/


添加Mongodb命令的环境变量

[root@node1 ~]#vim /etc/profile

[root@node1 ~]#export PATH=/usr/local/mongodb/bin:$PATH


启动Mongodb,在两台服务器主和从同时操作:

[root@node1 ~]#mongod  --dbpath /data/mongodbtest/replset/data   --replSet repset (副本集名称)

 

 

 可以看到控制台上显示副本集还没有配置初始化信息。


Sun Dec 29 20:12:02.953 [rsStart] replSet can't get local.system.replset config from self or any seed (EMPTYCONFIG)  

Sun Dec 29 20:12:02.953 [rsStart] replSet info you may need to run  replSetInitiate -- rs.initiate() in the shell -- if that is not already done  

 

 5、初始化副本集


在三台机器上任意一台机器登陆mongodb


[root@node1 ~]#mongo


#使用admin数据库

use admin

 

#定义副本集配置变量,这里的 _id:”repset” 和上面命令参数“ –replSet repset” 要保持一样。


config = { _id:"repset", members:[

... {_id:0,host:"10.2.16.250:27017"},   #id为0表示设置此IP为主服务器

... {_id:1,host:"10.2.16.254:27017"},]#id为1表示设置为备服务器,如果想再添加备服务器则设置id为2,依次类推。

... }

 

 

 #输出

 

 {  

        "_id" : "repset",  

        "members" : [  

                {  

                        "_id" : 0,  

                        "host" : "10.2.16.250:27017"  

                },  

                {  

                        "_id" : 1,  

                        "host" : "10.2.16.254:27017"  

                }

        ]  

}  

 

 

#初始化副本集配置


rs.initiate(config);

 

#输出成功


[plain] view plaincopy

{  

        "info" : "Config now saved locally.  Should come online in about a minute.",  

        "ok" : 1  

}  

 

 

#查看mongodb输出日志,副本集启动成功后,250为主节点PRIMARY,254为副本节点SECONDARY



 #查看集群节点的状态 rs.status();

 

 repset:PRIMARY> rs.status();

{

"set" : "repset",

"date" : ISODate("2014-06-30T10:03:22Z"),

"myState" : 1,

"members" : [

{

"_id" : 0,

"name" : "10.2.16.250:27017",

"health" : 1,

"state" : 1,

"stateStr" : "PRIMARY",

"uptime" : 961,

"optime" : Timestamp(1404122493, 1),

"optimeDate" : ISODate("2014-06-30T10:01:33Z"),

"self" : true

},

{

"_id" : 1,

"name" : "10.2.16.254:27017",

"health" : 1,

"state" : 2,

"stateStr" : "SECONDARY",

"uptime" : 106,

"optime" : Timestamp(1404122493, 1),

"optimeDate" : ISODate("2014-06-30T10:01:33Z"),

"lastHeartbeat" : ISODate("2014-06-30T10:03:20Z"),

"lastHeartbeatRecv" : ISODate("2014-06-30T10:03:21Z"),

"pingMs" : 2,

"syncingTo" : "10.2.16.250:27017"

}

],

"ok" : 1

}

 

 

 6、测试副本集数据复制功能


#在主节点10.2.16.250 上连接到终端:

[root@node1 ~]#mongo 


#建立test 数据库。

repset:PRIMARY>use test;


往testdb表插入数据。

repset:PRIMARY>> db.testdb.insert({"test1":"testval1"})


#在副本节点10.2.16.254 上连接到mongodb查看数据是否复制过来。


[root@node2 ~]#mongo 


#使用test 数据库。

repset:SECONDARY> use test;


repset:SECONDARY> show tables;


#输出

[plain] view plaincopy

Sun Dec 29 21:50:48.590 error: { "$err" : "not master and slaveOk=false", "code" : 13435 } at src/mongo/shell/query.js:128  

          

#设置副本节点可以读: mongodb默认是从主节点读写数据的,副本节点上不允许读,需要设置可读

repset:SECONDARY> db.getMongo().setSlaveOk();


#可以看到数据已经复制到了副本集。

repset:SECONDARY> db.testdb.find();

[plain] view plaincopy

#输出  

{ "_id" : ObjectId("52c028460c7505626a93944f"), "test1" : "testval1" }  

 

 

 添加仲裁服务器:

 

 

现在仲裁服务器10.2.16.251上装好mongodb,然后再:


#建立仲裁文件夹

mkdir /data/arb


#启动仲裁

mongod --port 30000 --dbpath /data/arb --replSet repset(副本集名称)

 


在主服务器上添加仲裁

 

 repset:PRIMARY> rs.addArb("10.2.16.251:30000")

{ "ok" : 1 }

 

 

 

 7、测试副本集故障转移功能


先停掉主节点mongodb 250,查看仲裁服务器和254从服务器的日志可以看到经过一系列的选择操作,254 当选主节点。




查看整个集群的状态:


mongo 


repset:PRIMARY> rs.status();


#输出


{

"set" : "repset",

"date" : ISODate("2014-06-30T13:58:06Z"),

"myState" : 1,

"members" : [

{

"_id" : 0,

"name" : "10.2.16.250:27017",

"health" : 1,

"state" : 1,

"stateStr" : "PRIMARY",

"uptime" : 6095,

"optime" : Timestamp(1404131596, 1),

"optimeDate" : ISODate("2014-06-30T12:33:16Z"),

"self" : true

},

{

"_id" : 1,

"name" : "10.2.16.254:27017",

"health" : 1,

"state" : 2,

"stateStr" : "SECONDARY",

"uptime" : 5542,

"optime" : Timestamp(1404131596, 1),

"optimeDate" : ISODate("2014-06-30T12:33:16Z"),

"lastHeartbeat" : ISODate("2014-06-30T13:58:06Z"),

"lastHeartbeatRecv" : ISODate("2014-06-30T13:58:05Z"),

"pingMs" : 0,

"syncingTo" : "10.2.16.250:27017"

},

{

"_id" : 2,

"name" : "10.2.16.251:30000",

"health" : 1,

"state" : 7,

"stateStr" : "ARBITER",

"uptime" : 11,

"lastHeartbeat" : ISODate("2014-06-30T13:58:05Z"),

"lastHeartbeatRecv" : ISODate("2014-06-30T13:58:06Z"),

"pingMs" : 8

}

],

"ok" : 1

}

 

 

当250节点启动之后,会自动加入副本集并且变回primary节点.


 ####################至此即为配置成功!!#########################

 

 

 

 

状态参数一共有五个:primary、primaryPreferred、secondary、secondaryPreferred、nearest。

 

 

primary:默认参数,只从主节点上进行读取操作; 

primaryPreferred:大部分从主节点上读取数据,只有主节点不可用时从secondary节点读取数据。 

secondary:只从secondary节点上进行读取操作,存在的问题是secondary节点的数据会比primary节点数据“旧”。 

secondaryPreferred:优先从secondary节点进行读取操作,secondary节点不可用时从主节点读取数据; 

nearest:不管是主节点、secondary节点,从网络延迟最低的节点上读取数据。

 

 

 

 

 

 

 ##############mongodb结合keepalived的脚本#####################

#!/bin/bash

MONGO=/usr/local/mongodb/bin/mongo

MONGO_HOST=127.0.0.1

MONGO_PORT=27017

CHECK_TIME=3

#mysql  is working MYSQL_OK is 1 , mysql down MYSQL_OK is 0

MONGO_OK=1

function check_mysql_helth (){

$MONGO  $MONGO_HOST:$MONGO_PORT >/dev/null 2>&1

if [ $? = 0 ] ;then

     MONGO_OK=1

else

     MONGO_OK=0

fi

     return $MONGO_OK

}

while [ $CHECK_TIME -ne 0 ]

do

     let "CHECK_TIME -= 1"

     check_mysql_helth

if [ $MONGO_OK = 1 ] ; then

     CHECK_TIME=0

     exit 0

fi


if [ $MONGO_OK -eq 0 ] &&  [ $CHECK_TIME -eq 0 ]

then

     pkill keepalived

exit 1

fi

sleep 1

done