从图中能够看到有四个组件:mongos、config server、shard、replica set。java
mongos,数据库集群请求的入口,全部的请求都经过mongos进行协调,不须要在应用程序添加一个路由选择器,mongos本身就是一个请求分发中心,它负责把对应的数据请求请求转发到对应的shard服务器上。在生产环境一般有多mongos做为请求的入口,防止其中一个挂掉全部的mongodb请求都没有办法操做。linux
config server,顾名思义为配置服务器,存储全部数据库元信息(路由、分片)的配置。mongos自己没有物理存储分片服务器和数据路由信息,只是缓存在内存里,配置服务器则实际存储这些数据。mongos第一次启动或者关掉重启就会从 config server 加载配置信息,之后若是配置服务器信息变化会通知到全部的 mongos 更新本身的状态,这样 mongos 就能继续准确路由。在生产环境一般有多个 config server 配置服务器,由于它存储了分片路由的元数据,这个可不能丢失!就算挂掉其中一台,只要还有存货, mongodb集群就不会挂掉。sql
shard,这就是传说中的分片了。在互联网也是这样,一台普通的机器作不了的多台机器来作,以下图:mongodb
一台机器的一个数据表 Collection1 存储了 1T 数据,压力太大了!在分给4个机器后,每一个机器都是256G,则分摊了集中在一台机器的压力。也许有人问一台机器硬盘加大一点不就能够了,为何要分给四台机器呢?不要光想到存储空间,实际运行的数据库还有硬盘的读写、网络的IO、CPU和内存的瓶颈。在mongodb集群只要设置好了分片规则,经过mongos操做数据库就能自动把对应的数据操做请求转发到对应的分片机器上。在生产环境中分片的片键可要好好设置,这个影响到了怎么把数据均匀分到多个分片机器上,不要出现其中一台机器分了1T,其余机器没有分到的状况,这样还不如不分片shell
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台机器,在同一台机器只要端口不一样就能够,看一下物理部署图:bash
1
2
|
#存放mongodb数据文件
mkdir
-p
/data/mongodbtest
|
1
2
|
#进入mongodb文件夹
cd
/data/mongodbtest
|
1
|
wget http:
//fastdl
.mongodb.org
/linux/mongodb-linux-x86_64-2
.4.8.tgz
|
1
2
|
#解压下载的压缩包
tar
xvzf mongodb-linux-x86_64-2.4.8.tgz
|
1
2
|
#创建mongos目录
mkdir
-p
/data/mongodbtest/mongos/log
|
1
2
|
#创建config server 数据文件存放目录
mkdir
-p
/data/mongodbtest/config/data
|
1
2
|
#创建config server 日志文件存放目录
mkdir
-p
/data/mongodbtest/config/log
|
1
2
|
#创建config server 日志文件存放目录
mkdir
-p
/data/mongodbtest/mongos/log
|
1
2
|
#创建shard1 数据文件存放目录
mkdir
-p
/data/mongodbtest/shard1/data
|
1
2
|
#创建shard1 日志文件存放目录
mkdir
-p
/data/mongodbtest/shard1/log
|
1
2
|
#创建shard2 数据文件存放目录
mkdir
-p
/data/mongodbtest/shard2/data
|
1
2
|
#创建shard2 日志文件存放目录
mkdir
-p
/data/mongodbtest/shard2/log
|
1
2
|
#创建shard3 数据文件存放目录
mkdir
-p
/data/mongodbtest/shard3/data
|
1
2
|
#创建shard3 日志文件存放目录
mkdir
-p
/data/mongodbtest/shard3/log
|
1
|
/data/mongodbtest/mongodb-linux-x86_64-2
.4.8
/bin/mongod
--configsvr --dbpath
/data/mongodbtest/config/data
--port 21000 --logpath
/data/mongodbtest/config/log/config
.log --fork
|
1
|
/data/mongodbtest/mongodb-linux-x86_64-2
.4.8
/bin/mongos
--configdb 192.168.0.136:21000,192.168.0.137:21000,192.168.0.138:21000 --port 20000 --logpath
/data/mongodbtest/mongos/log/mongos
.log --fork
|
1
2
|
#在每一个机器里分别设置分片1服务器及副本集shard1
/data/mongodbtest/mongodb-linux-x86_64-2
.4.8
/bin/mongod
--shardsvr --replSet shard1 --port 22001 --dbpath
/data/mongodbtest/shard1/data
--logpath
/data/mongodbtest/shard1/log/shard1
.log --fork --nojournal --oplogSize 10
|
为了快速启动并节约测试环境存储空间,这里加上 nojournal 是为了关闭日志信息,在咱们的测试环境不须要初始化这么大的redo日志。一样设置 oplogsize是为了下降 local 文件的大小,oplog是一个固定长度的 capped collection,它存在于”local”数据库中,用于记录Replica Sets操做日志。注意,这里的设置是为了测试!服务器
1
2
|
#在每一个机器里分别设置分片2服务器及副本集shard2
/data/mongodbtest/mongodb-linux-x86_64-2
.4.8
/bin/mongod
--shardsvr --replSet shard2 --port 22002 --dbpath
/data/mongodbtest/shard2/data
--logpath
/data/mongodbtest/shard2/log/shard2
.log --fork --nojournal --oplogSize 10
|
1
2
|
#在每一个机器里分别设置分片3服务器及副本集shard3
/data/mongodbtest/mongodb-linux-x86_64-2
.4.8
/bin/mongod
--shardsvr --replSet shard3 --port 22003 --dbpath
/data/mongodbtest/shard3/data
--logpath
/data/mongodbtest/shard3/log/shard3
.log --fork --nojournal --oplogSize 10
|
分别对每一个分片配置副本集,深刻了解副本集参考本系列前几篇文章。网络
任意登录一个机器,好比登录192.168.0.136,链接mongodb
1
2
|
#设置第一个分片副本集
/data/mongodbtest/mongodb-linux-x86_64-2
.4.8
/bin/mongo
127.0.0.1:22001
|
1
2
|
#使用admin数据库
use admin
|
1
2
3
4
5
6
7
|
#定义副本集配置
config = { _id:
"shard1"
, members:[
{_id:0,host:
"192.168.0.136:22001"
},
{_id:1,host:
"192.168.0.137:22001"
},
{_id:2,host:
"192.168.0.138:22001"
,arbiterOnly:
true
}
]
}
|
1
2
|
#初始化副本集配置
rs.initiate(config);
|
1
2
|
#设置第二个分片副本集
/data/mongodbtest/mongodb-linux-x86_64-2
.4.8
/bin/mongo
127.0.0.1:22002
|
1
2
|
#使用admin数据库
use admin
|
1
2
3
4
5
6
7
|
#定义副本集配置
config = { _id:
"shard2"
, members:[
{_id:0,host:
"192.168.0.136:22002"
},
{_id:1,host:
"192.168.0.137:22002"
},
{_id:2,host:
"192.168.0.138:22002"
,arbiterOnly:
true
}
]
}
|
1
2
|
#初始化副本集配置
rs.initiate(config);
|
1
2
|
#设置第三个分片副本集
/data/mongodbtest/mongodb-linux-x86_64-2
.4.8
/bin/mongo
127.0.0.1:22003
|
1
2
|
#使用admin数据库
use admin
|
1
2
3
4
5
6
7
|
#定义副本集配置
config = { _id:
"shard3"
, members:[
{_id:0,host:
"192.168.0.136:22003"
},
{_id:1,host:
"192.168.0.137:22003"
},
{_id:2,host:
"192.168.0.138:22003"
,arbiterOnly:
true
}
]
}
|
1
2
|
#初始化副本集配置
rs.initiate(config);
|
1
2
|
#链接到mongos
/data/mongodbtest/mongodb-linux-x86_64-2
.4.8
/bin/mongo
127.0.0.1:20000
|
1
2
|
#使用admin数据库
user admin
|
1
2
|
#串联路由服务器与分配副本集1
db.runCommand( { addshard :
"shard1/192.168.0.136:22001,192.168.0.137:22001,192.168.0.138:22001"
});
|
如里shard是单台服务器,用 db.runCommand( { addshard : “[:]” } )这样的命令加入,若是shard是副本集,用db.runCommand( { addshard : “replicaSetName/[:port][,serverhostname2[:port],…]” });这样的格式表示 。
1
2
|
#串联路由服务器与分配副本集2
db.runCommand( { addshard :
"shard2/192.168.0.136:22002,192.168.0.137:22002,192.168.0.138:22002"
});
|
1
2
|
#串联路由服务器与分配副本集3
db.runCommand( { addshard :
"shard3/192.168.0.136:22003,192.168.0.137:22003,192.168.0.138:22003"
});
|
1
2
|
#查看分片服务器的配置
db.runCommand( { listshards : 1 } );
|
#内容输出
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
{
"shards" : [
{
"_id" : "shard1",
"host" : "shard1/192.168.0.136:22001,192.168.0.137:22001"
},
{
"_id" : "shard2",
"host" : "shard2/192.168.0.136:22002,192.168.0.137:22002"
},
{
"_id" : "shard3",
"host" : "shard3/192.168.0.136:22003,192.168.0.137:22003"
}
],
"ok" : 1
}
|
由于192.168.0.138是每一个分片副本集的仲裁节点,因此在上面结果没有列出来。
链接在mongos上,准备让指定的数据库、指定的集合分片生效。
1
2
|
#指定testdb分片生效
db.runCommand( { enablesharding :
"testdb"
});
|
1
2
|
#指定数据库里须要分片的集合和片键
db.runCommand( { shardcollection :
"testdb.table1"
,key : {
id
: 1} } )
|
咱们设置testdb的 table1 表须要分片,根据 id 自动分片到 shard1 ,shard2,shard3 上面去。要这样设置是由于不是全部mongodb 的数据库和表 都须要分片!
1
2
|
#链接mongos服务器
/data/mongodbtest/mongodb-linux-x86_64-2
.4.8
/bin/mongo
127.0.0.1:20000
|
1
2
|
#使用testdb
use testdb;
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
|
#插入测试数据
for
(var i = 1; i <= 100000; i++)
db.table1.save({
id
:i,
"test1"
:
"testval1"
});&
#91;/code]
&
#91;code lang="shell"]
#查看分片状况以下,部分无关信息省掉了
db.table1.stats();&
#91;/code]
&
#91;code lang="java" highlight="4,19,26,33"]
{
"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
}
&
#91;/code]
能够看到数据分到3个分片,各自分片数量为: shard1
"count"
: 42183,shard2
"count"
: 38937,shard3
"count"
: 18880。已经成功了!不过度的好像不是很均匀,因此这个分片仍是颇有讲究的,后续再深刻讨论。<
/li
>
<li> 十二、java程序调用分片集群,由于咱们配置了三个mongos做为入口,就算其中哪一个入口挂掉了都不要紧,使用集群客户端程序以下:
public class TestMongoDBShards {
public static void main(String[] args) {
try {
List<ServerAddress> addresses = new ArrayList<ServerAddress>();
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多分担点责任吧!架构能够这样调整,把机器的负载分的更加均衡一点,每一个机器既能够做为主节点、副本节点、仲裁节点,这样压力就会均衡不少了,如图:
固然生产环境的数据远远大于当前的测试数据,大规模数据应用状况下咱们不可能把所有的节点像这样部署,硬件瓶颈是硬伤,只能扩展机器。要用好mongodb还有不少机制须要调整,不过经过这个东东咱们能够快速实现高可用性、高扩展性,因此它仍是一个很是不错的Nosql组件。
再看看咱们使用的mongodb java 驱动客户端 MongoClient(addresses),这个能够传入多个mongos 的地址做为mongodb集群的入口,而且能够实现自动故障转移,可是负载均衡作的好很差呢?打开源代码查看:
它的机制是选择一个ping 最快的机器来做为全部请求的入口,若是这台机器挂掉会使用下一台机器。那这样。。。。确定是不行的!万一出现双十一这样的状况全部请求集中发送到这一台机器,这台机器颇有可能挂掉。一但挂掉了,按照它的机制会转移请求到下台机器,可是这个压力总量仍是没有减小啊!下一台仍是可能崩溃,因此这个架构还有漏洞!不过这个文章已经太长了,后续解决吧。
转载自LANCEYAN.COM