参考:http://www.lanceyan.com/tech/arch/mongodb_shard1.html

一、mongodb分片简介

        在系统早期,数据量还小的时候不会引起太大的问题,但是随着数据量持续增多,后续迟早会出现一台机器硬件瓶颈问题的。而mongodb主打的就是海量数据架构,他不能解决海量数据怎么行!不行!“分片”就用这个来解决这个问题。 传统数据库怎么做海量数据读写?其实一句话概括:分而治之。上图看看就清楚了,如下 taobao岳旭强在infoq中提到的 架构图:

MongoDB(五)之分片_mongodb、mongodb分片

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

MongoDB(五)之分片_mongodb、mongodb分片_02

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

MongoDB(五)之分片_mongodb、mongodb分片_03

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

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

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

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

MongoDB(五)之分片_mongodb、mongodb分片_04

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


分片的一些名词解释:

片键 :当设置分片时,需要从集合里面选择一个或几个键,把选择出来的键作为数据拆分的依据,这个键叫做片键或复合片键。 选好片键后,MongoDB将不允许插入没有片键的文档,但是允许不同文档的片键类型不一样。 


块(chunk) :在一个shard server内部,MongoDB还是会把数据分为chunks,每个chunk代表这个shard server内部一部分数据。chunk的产生,会有以下两个用途: 

  • Splitting: 当一个chunk的大小超过配置中的chunk size时,MongDB的后台进程会把这个chunk切分成更小的chunk,从而避免chunk过大的情况。

  • Balancing: 在MongoDB中,balancer是一个后台进程,负责chunk的迁移,从而均衡各个shard server的负载。


二、环境搭建

1、实验环境介绍

MongoDB(五)之分片_mongodb、mongodb分片_05

2、同步时间

[root@www.example.com ~]#  ntpdate 202.120.2.101
[root@hpf-linux ~]# ntpdate 202.120.2.101
[root@node1.example.com ~]# ntpdate 202.120.2.101
[root@node2.example.com ~]# ntpdate 202.120.2.101

3、安装mongodb

注意剩下三台前面已经安装了mongodb这里就不列举了。

[root@hpf-linux ~]#  
yum localinstall -y mongo-10gen-2.4.14-mongodb_1.x86_64.rpm mongo-10gen-server-2.4.14-mongodb_1.x86_64.rpm

4、配置config server

[root@hpf-linux ~]# vim /etc/mongod.conf 
dbpath=/data/mongodb
configsvr = true
[root@hpf-linux ~]# mkdir -p /data/mongodb
[root@hpf-linux ~]# chown -R mongod.mongod /data/mongodb
[root@hpf-linux ~]# service mongod start
Starting mongod: about to fork child process, waiting until server is ready for connections.
forked process: 130046
all output going to: /var/log/mongo/mongod.log
    
child process started successfully, parent exiting
                                                           [确定]
[root@hpf-linux ~]# ss -tnlp |grep mongod
LISTEN     0      128                       *:28019                    *:*      users:(("mongod",130046,10))
LISTEN     0      128                       *:27019                    *:*      users:(("mongod",130046,9))

5、配置mongos

注意在前面做副本集实验时已经使用该机器,这里需要将在做副本集实验时的配置删除,并按以下配置。

[root@www.example.com ~]# vim /etc/mongod.conf 
#dbpath=/data/mongodb
configdb = 192.168.1.6:27019
[root@www.example.com ~]# mongos -f /etc/mongod.conf
Sat Sep 26 21:50:57.282 warning: running with 1 config server should be done only for testing purposes and is not recommended for production
about to fork child process, waiting until server is ready for connections.
forked process: 7485
all output going to: /var/log/mongo/mongod.log
child process started successfully, parent exiting
[root@www.example.com ~]# ss -tnlp |grep mongos
LISTEN     0      128                       *:28017                    *:*      users:(("mongos",7485,8))
LISTEN     0      128                       *:27017                    *:*      users:(("mongos",7485,6))

6、shard节点配置并启动

node1和node2节点也是在前面做副本集的实验中使用过,这里需要将在前面所增加的配置给删除,并按以下配置。

[root@node1.example.com ~]# vim /etc/mongod.conf 
dbpath=/data/mongodb
[root@node1.example.com ~]# service mongod start
[root@node2.example.com ~]# vim /etc/mongod.conf 
dbpath=/data/mongodb
[root@node2.example.com ~]# service mongod start

7、连入mongos

[root@www.example.com ~]# mongo --host 192.168.1.8
MongoDB shell version: 2.4.14
connecting to: 192.168.1.8:27017/test
mongos> sh.status()
--- Sharding Status ---
  sharding version: {
 "_id" : 1,
 "version" : 3,
 "minCompatibleVersion" : 3,
 "currentVersion" : 4,
 "clusterId" : ObjectId("5606a2c30b2c688bc754432d")
}
  shards:
  databases:
 {  "_id" : "admin",  "partitioned" : false,  "primary" : "config" }

添加shard节点:
mongos> sh.addShard("192.168.1.9:27017")
{ "shardAdded" : "shard0000", "ok" : 1 }
mongos> sh.addShard("192.168.1.10:27017")
{ "shardAdded" : "shard0001", "ok" : 1 }
mongos> sh.status()
--- Sharding Status ---
  sharding version: {
 "_id" : 1,
 "version" : 3,
 "minCompatibleVersion" : 3,
 "currentVersion" : 4,
 "clusterId" : ObjectId("5606a2c30b2c688bc754432d")
}
  shards:
 {  "_id" : "shard0000",  "host" : "192.168.1.9:27017" }
 {  "_id" : "shard0001",  "host" : "192.168.1.10:27017" }
  databases:
 {  "_id" : "admin",  "partitioned" : false,  "primary" : "config" }
 {  "_id" : "test",  "partitioned" : false,  "primary" : "shard0000" }

将testdb库分片:

mongos> sh.enableSharding("testdb")
{ "ok" : 1 }
mongos> use testdb
switched to db testdb
mongos> sh.shardCollection("testdb.testcoll",{Age: 1,Name: 1})
{ "collectionsharded" : "testdb.testcoll", "ok" : 1 }
mongos> sh.status()
--- Sharding Status ---
  sharding version: {
 "_id" : 1,
 "version" : 3,
 "minCompatibleVersion" : 3,
 "currentVersion" : 4,
 "clusterId" : ObjectId("5606a2c30b2c688bc754432d")
}
  shards:
 {  "_id" : "shard0000",  "host" : "192.168.1.9:27017" }
 {  "_id" : "shard0001",  "host" : "192.168.1.10:27017" }
  databases:
 {  "_id" : "admin",  "partitioned" : false,  "primary" : "config" }
 {  "_id" : "test",  "partitioned" : false,  "primary" : "shard0000" }
 {  "_id" : "testdb",  "partitioned" : true,  "primary" : "shard0001" }
  testdb.testcoll
   shard key: { "Age" : 1, "Name" : 1 }
   chunks:
    shard0001 1
   { "Age" : { "$minKey" : 1 }, "Name" : { "$minKey" : 1 } } -->> { "Age" : { "$maxKey" : 1 }, "Name" : { "$maxKey" : 1 } } on : shard0001 Timestamp(1, 0)

增加数据用来查看是否能分片:

mongos> for(i=1;i<=1000000;i++) db.testcoll.insert({Name: "User"+i,Age:(i%150),Address: "xinyang"})

另一台机器接入mongos,查看输入存放状态:

[root@hpf-linux ~]# mongo --host 192.168.1.8
MongoDB shell version: 2.4.14
connecting to: 192.168.1.8:27017/test
mongos> use testdb
switched to db testdb
mongos> sh.status()
--- Sharding Status ---
  sharding version: {
 "_id" : 1,
 "version" : 3,
 "minCompatibleVersion" : 3,
 "currentVersion" : 4,
 "clusterId" : ObjectId("5606a2c30b2c688bc754432d")
}
  shards:
 {  "_id" : "shard0000",  "host" : "192.168.1.9:27017" }
 {  "_id" : "shard0001",  "host" : "192.168.1.10:27017" }
  databases:
 {  "_id" : "admin",  "partitioned" : false,  "primary" : "config" }
 {  "_id" : "test",  "partitioned" : false,  "primary" : "shard0000" }
 {  "_id" : "testdb",  "partitioned" : true,  "primary" : "shard0001" }
  testdb.testcoll
   shard key: { "Age" : 1, "Name" : 1 }
   chunks:
    shard0000 1
    shard0001 3
   { "Age" : { "$minKey" : 1 }, "Name" : { "$minKey" : 1 } } -->> { "Age" : 1, "Name" : "User1" } on : shard0000 Timestamp(2, 0)
   { "Age" : 1, "Name" : "User1" } -->> { "Age" : 64, "Name" : "User92614" } on : shard0001 Timestamp(2, 2)
   { "Age" : 64, "Name" : "User92614" } -->> { "Age" : 149, "Name" : "User899" } on : shard0001 Timestamp(2, 3)
   { "Age" : 149, "Name" : "User899" } -->> { "Age" : { "$maxKey" : 1 }, "Name" : { "$maxKey" : 1 } } on : shard0001 Timestamp(1, 4) 
mongos> db.testcoll.find({Age: {$gt: 140}}).limit(3)
{ "_id" : ObjectId("5606b672ef76b4db97a7d713"), "Name" : "User100041", "Age" : 141, "Address" : "xinyang" }
{ "_id" : ObjectId("5606b672ef76b4db97a7d7a9"), "Name" : "User100191", "Age" : 141, "Address" : "xinyang" }
{ "_id" : ObjectId("5606b672ef76b4db97a7d83f"), "Name" : "User100341", "Age" : 141, "Address" : "xinyang" }
mongos> sh.getBalancerState()
true

总结:

        这里只是简单的演示了mongodb的分片机制,在该实验中有几点问题需要解决:

  • shard节点出现故障就会出现数据丢失,故shard节点需要做副本集来增加高可用。

  • 同样config server也需要做到高可用。

        以上就是我所想到的问题点,然而实际中还是有很多问题需要解决,本人水平有限,这里就不多说了,下面一个连接讲的架构就是利用三台机器做到的shard的高可用,但实际中还是不建议这么玩,在所有的分布式系统中建议还是增加主机使用,若机器较少,还是使用集中式的解决方案。毕竟关系型数据库技术是很成熟的,而所有的nosql也是这几年火起来的,若想使用还是要看公司的研发能力。个人认为在使用任何一个新技术术时一定不要盲目使用,需要看其缺点,而不是只看优点,同时根据自身的需求选择软件。只有最合适的没有最好的!我在使用四台虚拟机做该实验时本本是4G的内存,CPU是i5-5200U,可是还是卡的要死,所以伙伴们还是根据自身的硬件在玩mongodb吧,我这只是抛砖引玉。

MongoDB(五)之分片_mongodb、mongodb分片_06