MongoDb集群

NOSQL优势:

  • 大数据量,可以通过廉价服务器存储大量的数据。
  • 高扩展性,Nosql去掉了关系数据库的关系型特性,很容易横向扩展。
  • 高性能,Nosql通过简单的key-value方式获取数据,非常快速。
  • 灵活的数据模型,NoSQL无需事先为要存储的数据建立字段,随时可以存储自定义的数据格式。
  • 高可用,NoSQL在不太影响性能的情况,就可以方便的实现高可用的架构。


一、mongodb单实例。(生产环境很少用到,详见上一篇文章)

二、主从模式。(已不推荐使用)

使用mysql等传统关系型数据库时大家广泛用到,采用双机备份后主节点挂掉了后从节点可以接替主机继续服务。

 

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

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

2. 分别下载mongodb安装程序包。

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

mongod -f mongodb.master

master=true 
dbpath = /usr/local/mongodb2.6.3/mongodb_data/config/data 
logpath = /usr/local/mongodb2.6.3/mongodb_data/config/logs/mongodb.log 
logappend = true 
directoryperdb = true 
noauth = true 
port = 27017
maxConns = 1024 
fork = true 
quota = true 
quotaFiles = 1024 
nssize = 16 


4. 在192.168.0.2启动mongodb从节点程序。

mongod -f mongodb.slave

slave=true 
source=192.168.0.180:27017
dbpath = /usr/local/mongodb2.6.3/mongodb_data/config/data 
logpath = /usr/local/mongodb2.6.3/mongodb_data/config/logs/mongodb.log 
logappend = true 
directoryperdb = true 
noauth = true 
port = 27017
maxConns = 1024 
fork = true 
quota = true 
quotaFiles = 1024 
nssize = 16 


5. 测试主从复制。

在主节点上连接到终端:

mongo --port 27017

use users;

>db.users.save({age:22});

>db.users.find();

可以看到主机的同步日志

[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 --port 27017

show dbs;

use users;

db.users.find();

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

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


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

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

[js]  view plain copy
  1. mongo 127.0.0.1:27017  
  2. > db.testdb.insert({"test3":"testval3"});  
  3. not master  
可以看到 mongodb的从节点是不能提供写操作的,只能提供读操作。

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

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

[js]  view plain copy
  1. kill -3 `ps -ef|grep mongod|grep -v grep|awk '{print $2}'`  
测试从服务器能否可写。在192.168.0.2上连接mongodb测试。

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

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

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

[js]  view plain copy
  1. mongod  --dbpath /data/mongodbtest/slave --master  
等到启动成功(时间有点长)。在192.168.0.2 上 连接

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

成功! 

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

[js]  view plain copy
  1. mongod  --dbpath /data/mongodbtest/slave1 --slave  --port 27017 --source 192.168.0.1:27017。  
成功启动后通过mongodb连接测试:

[js]  view plain copy
  1. > db.testdb.find();  
  2. "_id" : ObjectId("5288629e9b0318be4b20bd4c"), "test1" : "testval1" }  
  3. "_id" : ObjectId("528862d69b0318be4b20bd4d"), "test2" : "testval2" }  
搭建了这套主从复制系统是不是就很稳健了,其实不然。。。看看这几个问题?

  • 主节点挂了能否自动切换连接?目前需要手工切换。
  • 主节点的写压力过大如何解决?
  • 从节点每个上面的数据都是对数据库全量拷贝,从节点压力会不会过大?
  • 就算对从节点路由实施路由访问策略能否做到自动扩展?
主从可能遇到的问题:
log日志输出:local.source 192.168.0.107:27017!=host:"192.168.0.180:27017
原因是设置的主节点与当前库中的source不同,需要更新local库中的source,具体如下:
use local
db.source.update({host:"192.168.0.107:27017"},{$set{host:"192.168.0.180:27017"}})


二、副本集:(主从的替代方式)

mongoDB官方已经不建议使用主从模式了,替代方案是采用副本集的模式,如图: 


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 <a href="http://fastdl.mongodb.org/linux/mongodb-linux-x86_64-2.4.8.tgz">http://fastdl.mongodb.org/linux/mongodb-linux-x86_64-2.4.8.tgz</a>

注意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 plain copy
  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 plain copy
  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 plain copy
  1. {  
  2.         "info" : "Config now saved locally.  Should come online in about a minute.",  
  3.         "ok" : 1  
  4. }  

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

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

#输出

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

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

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

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

/data/mongodbtest/mongodb-linux-x86_64-2.4.8/bin/mongo 192.168.1.136:27017

repset:SECONDARY> rs.status();

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

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

List<ServerAddress> addresses = new ArrayList<ServerAddress>();
ServerAddress address1 = new ServerAddress("192.168.0.180" , 27017); 
ServerAddress address2 = new ServerAddress("192.168.0.181" , 27017); 
ServerAddress address3 = new ServerAddress("192.168.0.182" , 27017); 
addresses.add(address1);
addresses.add(address2); 
addresses.add(address3);
Mongo mg = new MongoClient(addresses);
DB db = mg.getDB("qg_cache");


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

看图说话:

mongorep5

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

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

[java]  view plain copy
  1. public class TestMongoDBReplSetReadSplit {  
  2.   
  3.         public static void main(String[] args) {  
  4.   
  5.                try {  
  6.                      List<ServerAddress> addresses = new ArrayList<ServerAddress>();  
  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早就有了相应的解决方案。

看图: 

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

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

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

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

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

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

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

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

三、分片方式 :(主要解决单独的节点数据量过大的问题)

在系统早期,数据量还小的时候不会引起太大的问题,但是随着数据量持续增多,后续迟早会出现一台机器硬件瓶颈问题的。而MongoDB主打的就是海量数据架构,他不能解决海量数据怎么行!“分片”就用这个来解决这个问题。

  传统数据库怎么做海量数据读写?其实一句话概括:分而治之。上图看看就清楚了,如下TaoBao岳旭强提到的架构图:

  

fenpian1

 

  上图中有个TDDL,是TaoBao的一个数据访问层组件,他主要的作用是SQL解析、路由处理。根据应用的请求的功能解析当前访问的sql判断是在哪个业务数据库、哪个表访问查询并返回数据结果。具体如图:

  

fenpian2

 

  说了这么多传统数据库的架构,那NoSQL怎么去做到了这些呢?MySQL要做到自动扩展需要加一个数据访问层用程序去扩展,数据库的增加、删除、备份还需要程序去控制。一但数据库的节点一多,要维护起来也是非常头疼的。不过MongoDB所有的这一切通过他自己的内部机制就可以搞定!还是上图看看MongoDB通过哪些机制实现路由、分片:

  

fenpian3

 

  从图中可以看到有四个组件:mongos、config server、shard、replica set。

  mongos,数据库集群请求的入口,所有的请求都通过mongos进行协调,不需要在应用程序添加一个路由选择器,mongos自己就是一个请求分发中心,它负责把对应的数据请求请求转发到对应的shard服务器上。在生产环境通常有多mongos作为请求的入口,防止其中一个挂掉所有的mongodb请求都没有办法操作。

  config server,顾名思义为配置服务器,存储所有数据库元信息(路由、分片)的配置。mongos本身没有物理存储分片服务器和数据路由信息,只是缓存在内存里,配置服务器则实际存储这些数据。mongos第一次启动或者关掉重启就会从 config server 加载配置信息,以后如果配置服务器信息变化会通知到所有的 mongos 更新自己的状态,这样 mongos 就能继续准确路由。在生产环境通常有多个 config server 配置服务器,因为它存储了分片路由的元数据,这个可不能丢失!就算挂掉其中一台,只要还有存货, mongodb集群就不会挂掉。

  shard,这就是传说中的分片了。上面提到一个机器就算能力再大也有天花板,就像军队打仗一样,一个人再厉害喝血瓶也拼不过对方的一个师。俗话说三个臭皮匠顶个诸葛亮,这个时候团队的力量就凸显出来了。在互联网也是这样,一台普通的机器做不了的多台机器来做,如下图:

  

fenpian4

 

  一台机器的一个数据表 Collection1 存储了 1T 数据,压力太大了!在分给4个机器后,每个机器都是256G,则分摊了集中在一台机器的压力。也许有人问一台机器硬盘加大一点不就可以了,为什么要分给四台机器呢?不要光想到存储空间,实际运行的数据库还有硬盘的读写、网络的IO、CPU和内存的瓶颈。在mongodb集群只要设置好了分片规则,通过mongos操作数据库就能自动把对应的数据操作请求转发到对应的分片机器上。在生产环境中分片的片键可要好好设置,这个影响到了怎么把数据均匀分到多个分片机器上,不要出现其中一台机器分了1T,其他机器没有分到的情况,这样还不如不分片!

  replica set,上两节已经详细讲过了这个东东,怎么这里又来凑热闹!其实上图4个分片如果没有 replica set 是个不完整架构,假设其中的一个分片挂掉那四分之一的数据就丢失了,所以在高可用性的分片架构还需要对于每一个分片构建 replica set 副本集保证分片的可靠性。生产环境通常是 2个副本 + 1个仲裁。

  说了这么多,还是来实战一下如何搭建高可用的mongodb集群:

  首先确定各个组件的数量,mongos 3个, config server 3个,数据分3片 shard server 3个,每个shard 有一个副本一个仲裁也就是 3 * 2 = 6 个,总共需要部署15个实例。这些实例可以部署在独立机器也可以部署在一台机器,我们这里测试资源有限,只准备了 3台机器,在同一台机器只要端口不同就可以,看一下物理部署图:

  

fenpian5

 

  架构搭好了,安装软件!

  1. 准备机器,IP分别设置为: 192.168.0.136、192.168.0.137、192.168.0.138。

  2. 分别在每台机器上建立mongodb分片对应测试文件夹。

#存放mongodb数据文件
  mkdir -p /data/mongodbtest
  #进入mongodb文件夹
  cd /data/mongodbtest

  3. 下载mongodb的安装程序包

  4. 分别在每台机器建立mongos 、config 、 shard1 、shard2、shard3 五个目录。

  因为mongos不存储数据,只需要建立日志文件目录即可。

  #建立mongos目录

  mkdir -p /usr/local/mongodb2.6.3/mongos/log

  #建立mongos数据文件存放目录

  mkdir -p /usr/local/mongodb2.6.3/mongos/data

  #建立config server 日志文件存放目录

  mkdir -p /usr/local/mongodb2.6.3/mongod/log

  #建立config server 日志文件存放目录

  mkdir -p /usr/local/mongodb2.6.3/mongodb/log

  #建立shard1 数据文件存放目录

  mkdir -p /usr/local/mongodb2.6.3/shard1/data

  #建立shard1 日志文件存放目录

  mkdir -p /usr/local/mongodb2.6.3/shard1/log

  #建立shard2 数据文件存放目录

  mkdir -p /usr/local/mongodb2.6.3/shard2/data

  #建立shard2 日志文件存放目录

  mkdir -p /usr/local/mongodb2.6.3/shard2/log

  #建立shard3 数据文件存放目录

  mkdir -p /usr/local/mongodb2.6.3/shard3/data

  #建立shard3 日志文件存放目录

  mkdir -p /usr/local/mongodb2.6.3/shard3/log

  5. 规划5个组件对应的端口号,由于一个机器需要同时部署 mongos、config server 、shard1、shard2、shard3,所以需要用端口进行区分。

  这个端口可以自由定义,在本文 mongos为 27000, config server 为 27017, shard1为 27001 , shard2为27002, shard3为27003.

  6. 在每一台服务器分别启动配置服务器。

configsvr = true
dbpath = /usr/local/mongodb2.6.3/mongodb_data/mongod/data 
logpath = /usr/local/mongodb2.6.3/mongodb_data/mongod/logs/mongodb.log 
logappend = true 
directoryperdb = true 
noauth = true 
port = 27017
maxConns = 1024 
fork = true 
quota = true 
quotaFiles = 1024 
nssize = 16 

  7. 在每一台服务器分别启动mongos服务器。

configdb = 192.168.0.180:27017,192.168.0.181:27017,192.168.0.182:27017
logpath = /usr/local/mongodb2.6.3/mongodb_data/mongos/logs/mongodb.log 
logappend = true 
port = 27000
maxConns = 1024 
fork = true 

  8. 配置各个分片的副本集。

  #在每个机器里分别设置分片1服务器及副本集shard1

shardsvr = true
replSet = shard1
dbpath = /usr/local/mongodb2.6.3/mongodb_data/shard1/data 
logpath = /usr/local/mongodb2.6.3/mongodb_data/shard1/logs/mongodb.log 
logappend = true 
port = 27001
maxConns = 800 
fork = true 
smallfiles = true

  #在每个机器里分别设置分片2服务器及副本集shard2

shardsvr = true
replSet = shard1
dbpath = /usr/local/mongodb2.6.3/mongodb_data/shard1/data 
logpath = /usr/local/mongodb2.6.3/mongodb_data/shard1/logs/mongodb.log 
logappend = true 
port = 27001
maxConns = 800 
fork = true 
smallfiles = true

  #在每个机器里分别设置分片3服务器及副本集shard3

shardsvr = true
replSet = shard1
dbpath = /usr/local/mongodb2.6.3/mongodb_data/shard1/data 
logpath = /usr/local/mongodb2.6.3/mongodb_data/shard1/logs/mongodb.log 
logappend = true 
port = 27001
maxConns = 800 
fork = true 
smallfiles = true

  任意登陆一个机器,比如登陆192.168.0.136,连接MongoDB

  #设置第一个分片副本集

  /data/mongodbtest/mongodb-linux-x86_64-2.4.8/bin/mongo 127.0.0.1:27001

  #使用admin数据库

  use admin

  #定义副本集配置

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

  {_id:0,host:"192.168.0.136:27001"},

  {_id:1,host:"192.168.0.137:27001"},

  {_id:2,host:"192.168.0.138:27001",arbiterOnly:true}

  ]

  }

  #初始化副本集配置

  rs.initiate(config);

  #设置第二个分片副本集

  /data/mongodbtest/mongodb-linux-x86_64-2.4.8/bin/mongo 127.0.0.1:27002

  #使用admin数据库

  use admin

  #定义副本集配置

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

  {_id:0,host:"192.168.0.136:27002"},

  {_id:1,host:"192.168.0.137:27002"},

  {_id:2,host:"192.168.0.138:27002",arbiterOnly:true}

  ]

  }

  #初始化副本集配置

  rs.initiate(config);

  #设置第三个分片副本集

  /data/mongodbtest/mongodb-linux-x86_64-2.4.8/bin/mongo 127.0.0.1:27003

  #使用admin数据库

  use admin

  #定义副本集配置

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

  {_id:0,host:"192.168.0.136:27003"},

  {_id:1,host:"192.168.0.137:27003"},

  {_id:2,host:"192.168.0.138:27003",arbiterOnly:true}

  ]

  }

  #初始化副本集配置

  rs.initiate(config);

  9. 目前搭建了mongodb配置服务器、路由服务器,各个分片服务器,不过应用程序连接到 mongos 路由服务器并不能使用分片机制,还需要在程序里设置分片配置,让分片生效。

  #连接到mongos /data/mongodbtest/mongodb-linux-x86_64-2.4.8/bin/mongo 127.0.0.1:27000

  #使用admin数据库 user admin

  #串联路由服务器与分配副本集1

  db.runCommand( { addshard : "shard1/192.168.0.136:27001,192.168.0.137:27001,192.168.0.138:27001"});

  如里shard是单台服务器,用 db.runCommand( { addshard : “ [: ]” } )这样的命令加入,如果shard是副本集,用db.runCommand( { addshard : “replicaSetName/ [:port][,serverhostname2[:port],…]” });这样的格式表示 。

  #串联路由服务器与分配副本集2

  db.runCommand( { addshard : "shard2/192.168.0.136:27002,192.168.0.137:27002,192.168.0.138:27002"});

  #串联路由服务器与分配副本集3

  db.runCommand( { addshard : "shard3/192.168.0.136:27003,192.168.0.137:27003,192.168.0.138:27003"});

  #查看分片服务器的配置

  db.runCommand( { listshards : 1 } );

  #内容输出

  [plain] view plaincopy{

  "shards" : [

  {

  "_id" : "shard1",

  "host" : "shard1/192.168.0.136:27001,192.168.0.137:27001"

  },

  {

  "_id" : "shard2",

  "host" : "shard2/192.168.0.136:27002,192.168.0.137:27002"

  },

  {

  "_id" : "shard3",

  "host" : "shard3/192.168.0.136:27003,192.168.0.137:27003"

  }

  ],

  "ok" : 1

  }

  因为192.168.0.138是每个分片副本集的仲裁节点,所以在上面结果没有列出来。

  10. 目前配置服务、路由服务、分片服务、副本集服务都已经串联起来了,但我们的目的是希望插入数据,数据能够自动分片,就差那么一点点,一点点。。。

  连接在mongos上,准备让指定的数据库、指定的集合分片生效。

  #指定testdb分片生效

  db.runCommand( { enablesharding :"testdb"});

  #指定数据库里需要分片的集合和片键(片键是为了设置当前记录分布在哪个分片中;这里采用hashed是尽量保证各个分片的数据量平均)

  db.runCommand( { shardcollection : "testdb.table1",key : {"_id": "hashed"} } )

  我们设置testdb的 table1 表需要分片,根据“_id”自动分片到 shard1 ,shard2,shard3 上面去。要这样设置是因为不是所有mongodb 的数据库和表 都需要分片!这里的_id是mongodb自动生成的,不需要人为指定

  11. 测试分片配置结果。

  #连接mongos服务器

  /data/mongodbtest/mongodb-linux-x86_64-2.4.8/bin/mongo 127.0.0.1:27000

  #使用testdb use testdb;

  #插入测试数据

  for (var i = 1; i <= 100000; i++)

  db.table1.save({id:i,"test1":"testval1"});

  #查看分片情况如下,部分无关信息省掉了

  db.table1.stats();

  [java] view plaincopy{

  "sharded" : true,

  "ns" : "testdb.table1",

  "count" : 100000,

  "numExtents" : 13,

  "size" : 5600000,

  "storageSize" : 22372352,

  "totalIndexSize" : 6213760,

  "indexSizes" : {

  "_id_" : 3335808,

  "id_1" : 2877952

  },

  "avgObjSize" : 56,

  "nindexes" : 2,

  "nchunks" : 3,

  "shards" : {

  "shard1" : {

  "ns" : "testdb.table1",

  "count" : 42183,

  "size" : 0,

  ...

  "ok" : 1

  },

  "shard2" : {

  "ns" : "testdb.table1",

  "count" : 38937,

  "size" : 2180472,

  ...

  "ok" : 1

  },

  "shard3" : {

  "ns" : "testdb.table1",

  "count" :18880,

  "size" : 3419528,

  ...

  "ok" : 1

  }

  },

  "ok" : 1

  }

  可以看到数据分到3个分片,各自分片数量为: shard1 “count” : 42183,shard2 “count”: 38937,shard3 “count” : 18880。已经成功了!不过分的好像不是很均匀,所以这个分片还是很有讲究的,后续再深入讨论。

  12. Java程序调用分片集群,因为我们配置了三个mongos作为入口,就算其中哪个入口挂掉了都没关系,使用集群客户端程序如下:

view plaincopypublic class TestMongoDBShards { 
public static void main(String[] args)
  { 
try { 
List addresses = new ArrayList();
  ServerAddress address1 = new ServerAddress("192.168.0.136" , 20000); 
ServerAddress address2 = new ServerAddress("192.168.0.137" , 20000); 
ServerAddress address3 = new ServerAddress("192.168.0.138" , 20000); 
addresses.add(address1);addresses.add(address2); addresses.add(address3); 
MongoClient client = new MongoClient(addresses); 
DB db = client.getDB( "testdb" ); 
DBCollection coll = db.getCollection( "table1" ); 
BasicDBObject object = new BasicDBObject();
object.append( "id" , 1); 
DBObject dbObject = coll.findOne(object); 
System.out .println(dbObject); 
} catch (Exception e) { e.printStackTrace(); }} }


  整个分片集群搭建完了,思考一下我们这个架构是不是足够好呢?其实还有很多地方需要优化,比如我们把所有的仲裁节点放在一台机器,其余两台机器承担了全部读写操作,但是作为仲裁的192.168.0.138相当空闲。让机器3 192.168.0.138多分担点责任吧!架构可以这样调整,把机器的负载分的更加均衡一点,每个机器既可以作为主节点、副本节点、仲裁节点,这样压力就会均衡很多了,如图:

  

fenpian6

 

  当然生产环境的数据远远大于当前的测试数据,大规模数据应用情况下我们不可能把全部的节点像这样部署,硬件瓶颈是硬伤,只能扩展机器。要用好mongodb还有很多机制需要调整,不过通过这个东东我们可以快速实现高可用性、高扩展性,所以它还是一个非常不错的Nosql组件。

  再看看我们使用的mongodb java 驱动客户端 MongoClient(addresses),这个可以传入多个mongos 的地址作为mongodb集群的入口,并且可以实现自动故障转移,但是负载均衡做的好不好呢?打开源代码查看:

  

fenpian7

 

   它的机制是选择一个ping 最快的机器来作为所有请求的入口,如果这台机器挂掉会使用下一台机器。那这样。。。。肯定是不行的!万一出现双十一这样的情况所有请求集中发送到这一台机器,这台机器很有可能挂掉。一但挂掉了,按照它的机制会转移请求到下台机器,但是这个压力总量还是没有减少啊!下一台还是可能崩溃,所以这个架构还有漏洞!限于文章篇幅,请待后续解决。




参考:

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


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值