热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

搭建高可用的MongoDB集群

http:www.csdn.netarticle2014-04-092819221-build-high-avialable-mongodb-cluster-part-11在大数据

http://www.csdn.net/article/2014-04-09/2819221-build-high-avialable-mongodb-cluster-part-1/1

在大数据的时代,传统的关系型数据库要能更高的服务必须要解决高并发读写、海量数据高效存储、高可扩展性和高可用性这些难题。不过就是因为这些问题Nosql诞生了。

NOSQL有这些优势:

  • 大数据量,可以通过廉价服务器存储大量的数据,轻松摆脱传统mysql单表存储量级限制。
  • 高扩展性,Nosql去掉了关系数据库的关系型特性,很容易横向扩展,摆脱了以往老是纵向扩展的诟病。
  • 高性能,Nosql通过简单的key-value方式获取数据,非常快速。还有NoSQL的Cache是记录级的,是一种细粒度的Cache,所以NoSQL在这个层面上来说就要性能高很多。
  • 灵活的数据模型,NoSQL无需事先为要存储的数据建立字段,随时可以存储自定义的数据格式。而在关系数据库里,增删字段是一件非常麻烦的事情。如果是非常大数据量的表,增加字段简直就是一个噩梦。
  • 高可用,NoSQL在不太影响性能的情况,就可以方便的实现高可用的架构。比如mongodb通过mongos、mongo分片就可以快速配置出高可用配置。

在nosql数据库里,大部分的查询都是键值对(key、value)的方式。MongoDB是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中最像关系数据库的。支持类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引。所以这个非常方便,我们可以用sql操作MongoDB,从关系型数据库迁移过来,开发人员学习成本会大大减少。如果再对底层的sql API做一层封装,开发基本可以感觉不到mongodb和关系型数据库的区别。同样MongoDB也是号称自己能够快速搭建一个高可用可扩展的的分布式集群,网上有很多搭建的文章,在我们搭建的时候还需要查找修改很多东西,所以把自己实战的步骤记录下来以备忘。我们看看如何一步一步搭建这个东东。

一、mongodb单实例。这种配置只适合简易开发时使用,生产使用不行,因为单节点挂掉整个数据业务全挂,如下图。

 

虽然不能生产使用,但这个模式可以快速搭建启动,并且能够用mongodb的命令操作数据库。下面列出在linux下安装单节点mongodb的步骤

1. 建立mongodb测试文件夹

[js] view plaincopy在CODE上查看代码片派生到我的代码片
  1. #存放整个mongodb文件  
  2. mkdir -p /data/mongodbtest/single  
  3.  
  4. #存放mongodb数据文件  
  5. mkdir -p /data/mongodbtest/single/data  
  6.  
  7. #进入mongodb文件夹  
  8. cd  /data/mongodbtest/single  


2. 下载mongodb的安装程序包

[js] view plaincopy在CODE上查看代码片派生到我的代码片
  1. wget "http://fastdl.mongodb.org/linux/mongodb-linux-x86_64-2.4.6.tgz">http://fastdl.mongodb.org/linux/mongodb-linux-x86_64-2.4.6.tgz  
  2.  
  3. #解压下载的压缩包    
  4. tar xvzf mongodb-linux-x86_64-2.4.6.tgz  
  5.  
  6. #进入mongodb程序执行文件夹  
  7. cd mongodb-linux-x86_64-2.4.6/bin/  


3. 启动单实例mongodb

[js] view plaincopy在CODE上查看代码片 

二、主从模式。使用mysql数据库时大家广泛用到,采用双机备份后主节点挂掉了后从节点可以接替主机继续服务。所以这种模式比单节点的高可用性要好很多。

 

下面看一下怎么一步步搭建一个mongodb的主从复制节点:

1. 准备两台机器 192.168.0.1 和 192.168.0.2。 192.168.0.1 当作主节点, 192.168.0.2作为从节点。

2. 分别下载mongodb安装程序包。在192.168.0.1上建立文件夹 /data/mongodbtest/master,192.168.0.2建立文件夹/data/mongodbtest/slave。

3. 在192.168.0.1启动mongodb主节点程序。注意后面的这个 “ –master ”参数,标示主节点。

mongod –dbpath /data/mongodbtest/master –master

输出日志如下,成功!

[initandlisten] MongoDB starting : pid=18285 port=27017 dbpath=/data/mongodbtest/master master=1

#日志显示主节点参数

[initandlisten] options: { dbpath: “/data/mongodbtest/master”, master: true } 
…….. 
[initandlisten] waiting for connections on port 27017

4. 在192.168.0.2启动mongodb从节点程序。关键配置,指定主节点ip地址和端口 –source 192.168.0.1:27017 和 标示从节点 –source 参数。

mongod –dbpath /data/mongodbtest/slave –slave –source 192.168.0.1:27017

输出日志如下,成功!

[initandlisten] MongoDB starting : pid=17888 port=27017 dbpath=/data/mongodbtest/slave slave=1 
…….. 
#日志显示从节点参数 
[initandlisten] options: { dbpath: “/data/mongodbtest/slave”, slave: true, source: “192.168.0.1:27017″ } 
…….. 
[initandlisten] waiting for connections on port 27017 
#日志显示从节点 从主节点同步复制数据 
[replslave] repl: from host:192.168.0.1:27017

5. 测试主从复制。

在主节点上连接到终端:

[js] view plaincopy
  1. mongo 127.0.0.1  
  2.  
  3. #建立test 数据库。  
  4. use test;  
  5.   
  6. 往testdb表插入数据。  
  7. > db.testdb.insert({"test1":"testval1"})  
  8.   
  9. 查询testdb数据看看是否成功。  
  10. > db.testdb.find();  
  11. "_id" : ObjectId("5284e5cb1f4eb215b2ecc463"), "test1" : "testval1" }  


可以看到主机的同步日志

[initandlisten] connection accepted from 192.168.0.2:37285 #3 (2 connections now open) 
[slaveTracking] update local.slaves query: { _id: ObjectId(’5284e6268ed115d6238bdb39′), config: { host: “192.168.0.2:35271″, upgradeNeeded: true }, ns: “local.oplog.$main” } update: { $set: { syncedTo: Timestamp 1384441570000|1 } } nscanned:1 nupdated:1 fastmod:1 keyUpdates:0 locks(micros) w:132015 132ms

检查从主机的数据。

mongo 127.0.0.1

查看当前数据库。 

[js] view plaincopy
  1. > show dbs;  
  2.   local   0.203125GB  
  3.   test    0.203125GB  
  4.   
  5. use test;  
  6. db.testdb.find();  
  7. "_id" : ObjectId("5284e5cb1f4eb215b2ecc463"), "test1" : "testval1" }  


查询后数据已经同步过来了。再看看日志,发现从主机确实从主机同步了数据。

[js] view plaincopy
  1. Thu Nov 14 23:05:13 [replslave] repl:   checkpoint applied 15 operations  
  2. Thu Nov 14 23:05:13 [replslave] repl:   syncedTo: Nov 14 23:08:10 5284e75a:1  


查看服务状态

[js] view plaincopy
  1. > db.printReplicationInfo();  
  2.           this is a slave, printing slave replication info.  
  3.           source:   192.168.0.1:27017  
  4.               syncedTo: Sun Nov 17 2013 16:04:02 GMT+0800 (CST)  
  5.                       = -54 secs ago (-0.01hrs)  


到此主从结构的mongodb搭建好了。 



故障转移测试

,现在两台服务器如果主服务器挂掉了,从服务器可以正常运转吗?

a. 先测试下从服务器可以当成主服务器吗,也就是往从服务器里写能够同步主服务器吗?在192.168.0.2上连接mongodb。

[js] view plaincopy
  1. mongo 127.0.0.1:27017  
  2. > db.testdb.insert({"test3":"testval3"});  
  3. not master  


可以看到 mongodb的从节点是不能提供写操作的,只能提供读操作。

b. 如果从服务器挂掉,主服务器还可以提供服务。如果主服务器挂掉了从服务器能否自动变为可写。 
测试一下!

先杀掉原来的mongodb主服务器。

[js] view plaincopy
  1. kill -3 `ps -ef|grep mongod|grep -v grep|awk '{print $2}'`  


测试从服务器能否可写。在192.168.0.2上连接mongodb测试。

[js] view plaincopy
  1. > db.testdb.insert({"test3":"testval3"});  
  2. not master  

看起来从服务器没有自动接替主服务器的功能,只有手工处理了!

停止从服务器,在原数据文件启动并添加主服务器标示。

[js] view plaincopy
  1. mongod  --dbpath /data/mongodbtest/slave --master  


等到启动成功(时间有点长)。在192.168.0.2 上 连接

[js] view plaincopy
  1. mongo 192.168.0.2:27017  


[js] view plaincopy
  1. > db.testdb.find();  
  2. "_id" : ObjectId("5288629e9b0318be4b20bd4c"), "test1" : "testval1" }  
  3. "_id" : ObjectId("528862d69b0318be4b20bd4d"), "test2" : "testval2" }  


成功! 

多个从节点。现在只是一个数据库服务器又提供写又提供读,机器承载会出现瓶颈。大家还记得mysql里的读写分离吗?把20%的写放到主节点,80%的读放到从节点分摊了减少了服务器的负载。但是大部分应用都是读操作带来的压力,一个从节点压力负载不了,可以把一个从节点变成多个节点。那mongodb的一主多从可以支持吗?答案是肯定的。 

 

为了方便测试,在192.168.0.2上再建立一个文件夹 /data/mongodbtest/slave1 作为另一个slave服务器。启动slave2服务,

[js] view plaincopy
  1. mongod  --dbpath /data/mongodbtest/slave1 --slave  --port 27017 --source 192.168.0.1:27017。  


成功启动后通过mongodb连接测试:

[js] view plaincopy
  1. > db.testdb.find();  
  2. "_id" : ObjectId("5288629e9b0318be4b20bd4c"), "test1" : "testval1" }  
  3. "_id" : ObjectId("528862d69b0318be4b20bd4d"), "test2" : "testval2" }  


搭建了这套主从复制系统是不是就很稳健了,其实不然。。。看看这几个问题?

  • 主节点挂了能否自动切换连接?目前需要手工切换。
  • 主节点的写压力过大如何解决?
  • 从节点每个上面的数据都是对数据库全量拷贝,从节点压力会不会过大?
  • 就算对从节点路由实施路由访问策略能否做到自动扩展?

NoSQL的产生就是为了解决大数据量、高扩展性、高性能、灵活数据模型、高可用性。但是光通过主从模式的架构远远达不到上面几点,由此MongoDB设计了副本集和分片的功能。这篇文章主要介绍副本集:

mongoDB官方已经不建议使用主从模式了,替代方案是采用副本集的模式, 点击查看,如图: 
mongorep1
那什么是副本集呢?打魔兽世界总说打副本,其实这两个概念差不多一个意思。游戏里的副本是指玩家集中在高峰时间去一个场景打怪,会出现玩家暴多怪物少的情况,游戏开发商为了保证玩家的体验度,就为每一批玩家单独开放一个同样的空间同样的数量的怪物,这一个复制的场景就是一个副本,不管有多少个玩家各自在各自的副本里玩不会互相影响。 mongoDB的副本也是这个,主从模式其实就是一个单副本的应用,没有很好的扩展性和容错性。而副本集具有多个副本保证了容错性,就算一个副本挂掉了还有很多副本存在,并且解决了上面第一个问题“主节点挂掉了,整个集群内会自动切换”。难怪mongoDB官方推荐使用这种模式。我们来看看mongoDB副本集的架构图:

mongorep2

由图可以看到客户端连接到整个副本集,不关心具体哪一台机器是否挂掉。主服务器负责整个副本集的读写,副本集定期同步数据备份,一但主节点挂掉,副本节点就会选举一个新的主服务器,这一切对于应用服务器不需要关心。我们看一下主服务器挂掉后的架构:

mongodb故障转移

副本集中的副本节点在主节点挂掉后通过心跳机制检测到后,就会在集群内发起主节点的选举机制,自动选举一位新的主服务器。看起来很牛X的样子,我们赶紧操作部署一下! 
官方推荐的副本集机器数量为至少3个,那我们也按照这个数量配置测试。

1、准备两台机器 192.168.1.136、192.168.1.137、192.168.1.138。 192.168.1.136 当作副本集主节点,192.168.1.137、192.168.1.138作为副本集副本节点

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

注意linux生产环境不能安装32位的mongodb,因为32位受限于操作系统最大2G的文件限制。

mongorep4

#解压下载的压缩包
tar xvzf mongodb-linux-x86_64-2.4.8.tgz

4、分别在每台机器上启动mongodb

/data/mongodbtest/mongodb-linux-x86_64-2.4.8/bin/mongod --dbpath /data/mongodbtest/replset/data --replSet repset

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

[plain] view plaincopy
  1. Sun Dec 29 20:12:02.953 [rsStart] replSet can't get local.system.replset config from self or any seed (EMPTYCONFIG)  
  2. 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

/data/mongodbtest/mongodb-linux-x86_64-2.4.8/bin/mongo#使用admin数据库
use admin

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

config = { _id:"repset", members:[
... {_id:0,host:"192.168.1.136:27017"},
... {_id:1,host:"192.168.1.137:27017"},
... {_id:2,host:"192.168.1.138:27017"}]
... }

#输出

[plain] view plaincopy
  1. {  
  2.         "_id" : "repset",  
  3.         "members" : [  
  4.                 {  
  5.                         "_id" : 0,  
  6.                         "host" : "192.168.1.136:27017"  
  7.                 },  
  8.                 {  
  9.                         "_id" : 1,  
  10.                         "host" : "192.168.1.137:27017"  
  11.                 },  
  12.                 {  
  13.                         "_id" : 2,  
  14.                         "host" : "192.168.1.138:27017"  
  15.                 }  
  16.         ]  
  17. }  

#初始化副本集配置
rs.initiate(config);

#输出成功

[plain] view plaincopy
  1. {  
  2.         "info" : "Config now saved locally.  Should come online in about a minute.",  
  3.         "ok" : 1  
  4. }  

#查看日志,副本集启动成功后,138为主节点PRIMARY,136、137为副本节点SECONDARY

[plain] view plaincopy
  1. Sun Dec 29 20:26:13.842 [conn3] replSet replSetInitiate admin command  
  2.             received from client Sun Dec 29 20:26:13.842 [conn3] replSet replSetInitiate  
  3.             config object parses ok, 3 members specified Sun Dec 29 20:26:13.847 [conn3]  
  4.             replSet replSetInitiate all members seem up Sun Dec 29 20:26:13.848 [conn3]  
  5.             ****** Sun Dec 29 20:26:13.848 [conn3] creating replication oplog of size:  
  6.             990MB... Sun Dec 29 20:26:13.849 [FileAllocator] allocating new datafile  
  7.             /data/mongodbtest/replset/data/local.1, filling with zeroes... Sun Dec  
  8.             29 20:26:13.862 [FileAllocator] done allocating datafile /data/mongodbtest/replset/data/local.1,  
  9.             size: 1024MB, took 0.012 secs Sun Dec 29 20:26:13.863 [conn3] ****** Sun  
  10.             Dec 29 20:26:13.863 [conn3] replSet info saving a newer config version  
  11.             to local.system.replset Sun Dec 29 20:26:13.864 [conn3] replSet saveConfigLocally  
  12.             done Sun Dec 29 20:26:13.864 [conn3] replSet replSetInitiate config now  
  13.             saved locally. Should come online in about a minute. Sun Dec 29 20:26:23.047  
  14.             [rsStart] replSet I am 192.168.1.138:27017 Sun Dec 29 20:26:23.048 [rsStart]  
  15.             replSet STARTUP2 Sun Dec 29 20:26:23.049 [rsHealthPoll] replSet member  
  16.             192.168.1.137:27017 is up Sun Dec 29 20:26:23.049 [rsHealthPoll] replSet  
  17.             member 192.168.1.136:27017 is up Sun Dec 29 20:26:24.051 [rsSync] replSet  
  18.             SECONDARY Sun Dec 29 20:26:25.053 [rsHealthPoll] replset info 192.168.1.136:27017  
  19.             thinks that we are down Sun Dec 29 20:26:25.053 [rsHealthPoll] replSet  
  20.             member 192.168.1.136:27017 is now in state STARTUP2 Sun Dec 29 20:26:25.056  
  21.             [rsMgr] not electing self, 192.168.1.136:27017 would veto with 'I don't  
  22.             think 192.168.1.138:27017 is electable' Sun Dec 29 20:26:31.059 [rsHealthPoll]  
  23.             replset info 192.168.1.137:27017 thinks that we are down Sun Dec 29 20:26:31.059  
  24.             [rsHealthPoll] replSet member 192.168.1.137:27017 is now in state STARTUP2  
  25.             Sun Dec 29 20:26:31.062 [rsMgr] not electing self, 192.168.1.137:27017  
  26.             would veto with 'I don't think 192.168.1.138:27017 is electable' Sun Dec  
  27.             29 20:26:37.074 [rsMgr] replSet info electSelf 2 Sun Dec 29 20:26:38.062  
  28.             [rsMgr] replSet PRIMARY Sun Dec 29 20:26:39.071 [rsHealthPoll] replSet  
  29.             member 192.168.1.137:27017 is now in state RECOVERING Sun Dec 29 20:26:39.075  
  30.             [rsHealthPoll] replSet member 192.168.1.136:27017 is now in state RECOVERING  
  31.             Sun Dec 29 20:26:42.201 [slaveTracking] build index local.slaves { _id:  
  32.             1 } Sun Dec 29 20:26:42.207 [slaveTracking] build index done. scanned 0  
  33.             total records. 0.005 secs Sun Dec 29 20:26:43.079 [rsHealthPoll] replSet  
  34.             member 192.168.1.136:27017 is now in state SECONDARY Sun Dec 29 20:26:49.080  
  35.             [rsHealthPoll] replSet member 192.168.1.137:27017 is now in state SECONDARY  

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

#输出

[plain] view plaincopy
  1. {  
  2.         "set" : "repset",  
  3.         "date" : ISODate("2013-12-29T12:54:25Z"),  
  4.         "myState" : 1,  
  5.         "members" : [  
  6.                 {  
  7.                         "_id" : 0,  
  8.                         "name" : "192.168.1.136:27017",  
  9.                         "health" : 1,  
  10.                         "state" : 2,  
  11.                         "stateStr" : "SECONDARY",  
  12.                         "uptime" : 1682,  
  13.                         "optime" : Timestamp(1388319973, 1),  
  14.                         "optimeDate" : ISODate("2013-12-29T12:26:13Z"),  
  15.                         "lastHeartbeat" : ISODate("2013-12-29T12:54:25Z"),  
  16.                         "lastHeartbeatRecv" : ISODate("2013-12-29T12:54:24Z"),  
  17.                         "pingMs" : 1,  
  18.                         "syncingTo" : "192.168.1.138:27017"  
  19.                 },  
  20.                 {  
  21.                         "_id" : 1,  
  22.                         "name" : "192.168.1.137:27017",  
  23.                         "health" : 1,  
  24.                         "state" : 2,  
  25.                         "stateStr" : "SECONDARY",  
  26.                         "uptime" : 1682,  
  27.                         "optime" : Timestamp(1388319973, 1),  
  28.                         "optimeDate" : ISODate("2013-12-29T12:26:13Z"),  
  29.                         "lastHeartbeat" : ISODate("2013-12-29T12:54:25Z"),  
  30.                         "lastHeartbeatRecv" : ISODate("2013-12-29T12:54:24Z"),  
  31.                         "pingMs" : 1,  
  32.                         "syncingTo" : "192.168.1.138:27017"  
  33.                 },  
  34.                 {  
  35.                         "_id" : 2,  
  36.                         "name" : "192.168.1.138:27017",  
  37.                         "health" : 1,  
  38.                         "state" : 1,  
  39.                         "stateStr" : "PRIMARY",  
  40.                         "uptime" : 2543,  
  41.                         "optime" : Timestamp(1388319973, 1),  
  42.                         "optimeDate" : ISODate("2013-12-29T12:26:13Z"),  
  43.                         "self" : true  
  44.                 }  
  45.         ],  
  46.         "ok" : 1  
  47. }  

整个副本集已经搭建成功了。

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

#在主节点192.168.1.138 上连接到终端:
mongo 127.0.0.1#建立test 数据库。
use test;往testdb表插入数据。
> db.testdb.insert({"test1":"testval1"})#在副本节点 192.168.1.136、192.168.1.137 上连接到mongodb查看数据是否复制过来。
/data/mongodbtest/mongodb-linux-x86_64-2.4.8/bin/mongo 192.168.1.136:27017#使用test 数据库。
repset:SECONDARY> use test;repset:SECONDARY> show tables;

#输出

[plain] view plaincopy
  1. 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
  1. #输出  
  2. { "_id" : ObjectId("52c028460c7505626a93944f"), "test1" : "testval1" }  

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

先停掉主节点mongodb 138,查看136、137的日志可以看到经过一系列的投票选择操作,137 当选主节点,136从137同步数据过来。

[plain] view plaincopy
  1. Sun Dec 29 22:03:05.351 [rsBackgroundSync] replSet sync source problem:  
  2.                     10278 dbclient error communicating with server: 192.168.1.138:27017 Sun  
  3.                     Dec 29 22:03:05.354 [rsBackgroundSync] replSet syncing to: 192.168.1.138:27017  
  4.                     Sun Dec 29 22:03:05.356 [rsBackgroundSync] repl: couldn't connect to server  
  5.                     192.168.1.138:27017 Sun Dec 29 22:03:05.356 [rsBackgroundSync] replSet  
  6.                     not trying to sync from 192.168.1.138:27017, it is vetoed for 10 more seconds  
  7.                     Sun Dec 29 22:03:05.499 [rsHealthPoll] DBClientCursor::init call() failed  
  8.                     Sun Dec 29 22:03:05.499 [rsHealthPoll] replset info 192.168.1.138:27017  
  9.                     heartbeat failed, retrying Sun Dec 29 22:03:05.501 [rsHealthPoll] replSet  
  10.                     info 192.168.1.138:27017 is down (or slow to respond): Sun Dec 29 22:03:05.501  
  11.                     [rsHealthPoll] replSet member 192.168.1.138:27017 is now in state DOWN  
  12.                     Sun Dec 29 22:03:05.511 [rsMgr] not electing self, 192.168.1.137:27017  
  13.                     would veto with '192.168.1.136:27017 is trying to elect itself but 192.168.1.138:27017  
  14.                     is already primary and more up-to-date' Sun Dec 29 22:03:07.330 [conn393]  
  15.                     replSet info voting yea for 192.168.1.137:27017 (1) Sun Dec 29 22:03:07.503  
  16.                     [rsHealthPoll] replset info 192.168.1.138:27017 heartbeat failed, retrying  
  17.                     Sun Dec 29 22:03:08.462 [rsHealthPoll] replSet member 192.168.1.137:27017  
  18.                     is now in state PRIMARY Sun Dec 29 22:03:09.359 [rsBackgroundSync] replSet  
  19.                     syncing to: 192.168.1.137:27017 Sun Dec 29 22:03:09.507 [rsHealthPoll]  
  20.                     replset info 192.168.1.138:27017 heartbeat failed, retrying  

查看整个集群的状态,可以看到138为状态不可达。

/data/mongodbtest/mongodb-linux-x86_64-2.4.8/bin/mongo 192.168.1.136:27017repset:SECONDARY> rs.status();

#输出

[plain] view plaincopy
  1. {  
  2.         "set" : "repset",  
  3.         "date" : ISODate("2013-12-29T14:28:35Z"),  
  4.         "myState" : 2,  
  5.         "syncingTo" : "192.168.1.137:27017",  
  6.         "members" : [  
  7.                 {  
  8.                         "_id" : 0,  
  9.                         "name" : "192.168.1.136:27017",  
  10.                         "health" : 1,  
  11.                         "state" : 2,  
  12.                         "stateStr" : "SECONDARY",  
  13.                         "uptime" : 9072,  
  14.                         "optime" : Timestamp(1388324934, 1),  
  15.                         "optimeDate" : ISODate("2013-12-29T13:48:54Z"),  
  16.                         "self" : true  
  17.                 },  
  18.                 {  
  19.                         "_id" : 1,  
  20.                         "name" : "192.168.1.137:27017",  
  21.                         "health" : 1,  
  22.                         "state" : 1,  
  23.                         "stateStr" : "PRIMARY",  
  24.                         "uptime" : 7329,  
  25.                         "optime" : Timestamp(1388324934, 1),  
  26.                         "optimeDate" : ISODate("2013-12-29T13:48:54Z"),  
  27.                         "lastHeartbeat" : ISODate("2013-12-29T14:28:34Z"),  
  28.                         "lastHeartbeatRecv" : ISODate("2013-12-29T14:28:34Z"),  
  29.                         "pingMs" : 1,  
  30.                         "syncingTo" : "192.168.1.138:27017"  
  31.                 },  
  32.                 {  
  33.                         "_id" : 2,  
  34.                         "name" : "192.168.1.138:27017",  
  35.                         "health" : 0,  
  36.                         "state" : 8,  
  37.                         "stateStr" : "(not reachable/healthy)",  
  38.                         "uptime" : 0,  
  39.                         "optime" : Timestamp(1388324934, 1),  
  40.                         "optimeDate" : ISODate("2013-12-29T13:48:54Z"),  
  41.                         "lastHeartbeat" : ISODate("2013-12-29T14:28:35Z"),  
  42.                         "lastHeartbeatRecv" : ISODate("2013-12-29T14:28:23Z"),  
  43.                         "pingMs" : 0,  
  44.                         "syncingTo" : "192.168.1.137:27017"  
  45.                 }  
  46.         ],  
  47.         "ok" : 1  
  48. }  

再启动原来的主节点 138,发现138 变为 SECONDARY,还是137 为主节点 PRIMARY。

[plain] view plaincopy
  1. Sun Dec 29 22:21:06.619 [rsStart] replSet I am 192.168.1.138:27017  
  2. Sun Dec 29 22:21:06.619 [rsStart] replSet STARTUP2  
  3. Sun Dec 29 22:21:06.627 [rsHealthPoll] replset info 192.168.1.136:27017 thinks that we are down  
  4. Sun Dec 29 22:21:06.627 [rsHealthPoll] replSet member 192.168.1.136:27017 is up  
  5. Sun Dec 29 22:21:06.627 [rsHealthPoll] replSet member 192.168.1.136:27017 is now in state SECONDARY  
  6. Sun Dec 29 22:21:07.628 [rsSync] replSet SECONDARY  
  7. Sun Dec 29 22:21:08.623 [rsHealthPoll] replSet member 192.168.1.137:27017 is up  
  8. Sun Dec 29 22:21:08.624 [rsHealthPoll] replSet member 192.168.1.137:27017 is now in state PRIMARY  

8、java程序连接副本集测试。三个节点有一个节点挂掉也不会影响应用程序客户端对整个副本集的读写!

[java] view plaincopy
  1. public class TestMongoDBReplSet { public static void main(String[] args)  
  2.                         { try { List addresses = new ArrayList();  
  3.                         ServerAddress address1 = new ServerAddress("192.168.1.136" , 27017); ServerAddress  
  4.                         address2 = new ServerAddress("192.168.1.137" , 27017); ServerAddress address3  
  5.                         = new ServerAddress("192.168.1.138" , 27017); addresses.add(address1);  
  6.                         addresses.add(address2); addresses.add(address3); MongoClient client =  
  7.                         new MongoClient(addresses); DB db = client.getDB( "test"); DBCollection  
  8.                         coll = db.getCollection( "testdb"); // 插入 BasicDBObject object = new BasicDBObject();  
  9.                         object.append( "test2""testval2" ); coll.insert(object); DBCursor dbCursor  
  10.                         = coll.find(); while (dbCursor.hasNext()) { DBObject dbObject = dbCursor.next();  
  11.                         System. out.println(dbObject.toString()); } } catch (Exception e) { e.printStackTrace();  
  12.                         } } }  

目前看起来支持完美的故障转移了,这个架构是不是比较完美了?其实还有很多地方可以优化,比如开头的第二个问题:主节点的读写压力过大如何解决?常见的解决方案是读写分离,mongodb副本集的读写分离如何做呢?

看图说话:

mongorep5

常规写操作来说并没有读操作多,所以一台主节点负责写,两台副本节点负责读。

1、设置读写分离需要先在副本节点SECONDARY 设置 setSlaveOk。 
2、在程序中设置副本节点负责读操作,如下代码:

[java] view plaincopy
  1. public class TestMongoDBReplSetReadSplit {  
  2.   
  3.         public static void main(String[] args) {  
  4.   
  5.                try {  
  6.                      List addresses = new ArrayList();  
  7.                      ServerAddress address1 = new ServerAddress("192.168.1.136" , 27017);  
  8.                      ServerAddress address2 = new ServerAddress("192.168.1.137" , 27017);  
  9.                      ServerAddress address3 = new ServerAddress("192.168.1.138" , 27017);  
  10.                      addresses.add(address1);  
  11.                      addresses.add(address2);  
  12.                      addresses.add(address3);  
  13.   
  14.                      MongoClient client = new MongoClient(addresses);  
  15.                      DB db = client.getDB( "test" );  
  16.                      DBCollection coll = db.getCollection( "testdb" );  
  17.   
  18.   
  19.                      BasicDBObject object = new BasicDBObject();  
  20.                      object.append( "test2" , "testval2" );  
  21.   
  22.                       //读操作从副本节点读取  
  23.                      ReadPreference preference = ReadPreference. secondary();  
  24.                      DBObject dbObject = coll.findOne(object, null , preference);  
  25.   
  26.                      System. out .println(dbObject);  
  27.   
  28.   
  29.               } catch (Exception e) {  
  30.                      e.printStackTrace();  
  31.               }  
  32.        }  
  33. }  

读参数除了secondary一共还有五个参数:primary、primaryPreferred、secondary、secondaryPreferred、nearest。

mongorep6

primary:默认参数,只从主节点上进行读取操作; 
primaryPreferred:大部分从主节点上读取数据,只有主节点不可用时从secondary节点读取数据。 
secondary:只从secondary节点上进行读取操作,存在的问题是secondary节点的数据会比primary节点数据“旧”。 
secondaryPreferred:优先从secondary节点进行读取操作,secondary节点不可用时从主节点读取数据; 
nearest:不管是主节点、secondary节点,从网络延迟最低的节点上读取数据。

好,读写分离做好我们可以数据分流,减轻压力解决了“主节点的读写压力过大如何解决?”这个问题。不过当我们的副本节点增多时,主节点的复制压力会加大有什么办法解决吗?mongodb早就有了相应的解决方案。

看图: 
mongorep7

其中的仲裁节点不存储数据,只是负责故障转移的群体投票,这样就少了数据复制的压力。是不是想得很周到啊,一看mongodb的开发兄弟熟知大数据架构体系,其实不只是主节点、副本节点、仲裁节点,还有Secondary-Only、Hidden、Delayed、Non-Voting。

Secondary-Only:不能成为primary节点,只能作为secondary副本节点,防止一些性能不高的节点成为主节点。 
Hidden:这类节点是不能够被客户端制定IP引用,也不能被设置为主节点,但是可以投票,一般用于备份数据。 
Delayed:可以指定一个时间延迟从primary节点同步数据。主要用于备份数据,如果实时同步,误删除数据马上同步到从节点,恢复又恢复不了。 
Non-Voting:没有选举权的secondary节点,纯粹的备份数据节点。

到此整个mongodb副本集搞定了两个问题:

  • 主节点挂了能否自动切换连接?目前需要手工切换。
  • 主节点的读写压力过大如何解决?

还有这两个问题后续解决:

  • 从节点每个上面的数据都是对数据库全量拷贝,从节点压力会不会过大?
  • 数据压力大到机器支撑不了的时候能否做到自动扩展?

做了副本集发现又一些问题:

  • 副本集故障转移,主节点是如何选举的?能否手动干涉下架某一台主节点。
  • 官方说副本集数量最好是奇数,为什么?
  • mongodb副本集是如何同步的?如果同步不及时会出现什么情况?会不会出现不一致性?
  • mongodb的故障转移会不会无故自动发生?什么条件会触发?频繁触发可能会带来系统负载加重



推荐阅读
  • 解决Bootstrap DataTable Ajax请求重复问题
    在最近的一个项目中,我们使用了JQuery DataTable进行数据展示,虽然使用起来非常方便,但在测试过程中发现了一个问题:当查询条件改变时,有时查询结果的数据不正确。通过FireBug调试发现,点击搜索按钮时,会发送两次Ajax请求,一次是原条件的请求,一次是新条件的请求。 ... [详细]
  • 深入解析 Lifecycle 的实现原理
    本文将详细介绍 Android Jetpack 中 Lifecycle 组件的实现原理,帮助开发者更好地理解和使用 Lifecycle,避免常见的内存泄漏问题。 ... [详细]
  • 解决Only fullscreen opaque activities can request orientation错误的方法
    本文介绍了在使用PictureSelectorLight第三方框架时遇到的Only fullscreen opaque activities can request orientation错误,并提供了一种有效的解决方案。 ... [详细]
  • Python 数据可视化实战指南
    本文详细介绍如何使用 Python 进行数据可视化,涵盖从环境搭建到具体实例的全过程。 ... [详细]
  • 开机自启动的几种方式
    0x01快速自启动目录快速启动目录自启动方式源于Windows中的一个目录,这个目录一般叫启动或者Startup。位于该目录下的PE文件会在开机后进行自启动 ... [详细]
  • ARM汇编基础基于Keil创建STM32汇编程序的编写
    文章目录一、新建项目(1)工具介绍(2)创建项目:二、配置环境(1)配置芯片&#x ... [详细]
  • 在软件开发过程中,经常需要将多个项目或模块进行集成和调试,尤其是当项目依赖于第三方开源库(如Cordova、CocoaPods)时。本文介绍了如何在Xcode中高效地进行多项目联合调试,分享了一些实用的技巧和最佳实践,帮助开发者解决常见的调试难题,提高开发效率。 ... [详细]
  • 在尝试对 QQmlPropertyMap 类进行测试驱动开发时,发现其派生类中无法正常调用槽函数或 Q_INVOKABLE 方法。这可能是由于 QQmlPropertyMap 的内部实现机制导致的,需要进一步研究以找到解决方案。 ... [详细]
  • 在《Cocos2d-x学习笔记:基础概念解析与内存管理机制深入探讨》中,详细介绍了Cocos2d-x的基础概念,并深入分析了其内存管理机制。特别是针对Boost库引入的智能指针管理方法进行了详细的讲解,例如在处理鱼的运动过程中,可以通过编写自定义函数来动态计算角度变化,利用CallFunc回调机制实现高效的游戏逻辑控制。此外,文章还探讨了如何通过智能指针优化资源管理和避免内存泄漏,为开发者提供了实用的编程技巧和最佳实践。 ... [详细]
  • 您的数据库配置是否安全?DBSAT工具助您一臂之力!
    本文探讨了Oracle提供的免费工具DBSAT,该工具能够有效协助用户检测和优化数据库配置的安全性。通过全面的分析和报告,DBSAT帮助用户识别潜在的安全漏洞,并提供针对性的改进建议,确保数据库系统的稳定性和安全性。 ... [详细]
  • Spring – Bean Life Cycle
    Spring – Bean Life Cycle ... [详细]
  • 本文详细介绍了在 CentOS 7 系统中配置 fstab 文件以实现开机自动挂载 NFS 共享目录的方法,并解决了常见的配置失败问题。 ... [详细]
  • Spring Boot 中配置全局文件上传路径并实现文件上传功能
    本文介绍如何在 Spring Boot 项目中配置全局文件上传路径,并通过读取配置项实现文件上传功能。通过这种方式,可以更好地管理和维护文件路径。 ... [详细]
  • 本文详细介绍了如何使用OpenSSL自建CA证书的步骤,包括准备工作、生成CA证书、生成服务器待签证书以及证书签名等过程。 ... [详细]
  • Framework7:构建跨平台移动应用的高效框架
    Framework7 是一个开源免费的框架,适用于开发混合移动应用(原生与HTML混合)或iOS&Android风格的Web应用。此外,它还可以作为原型开发工具,帮助开发者快速创建应用原型。 ... [详细]
author-avatar
LinHrU
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有