搭建高可用mongodb集群(四)—— 分片

搭建高可用mongodb集群(四)—— 分片

Posted on 29 三月, 2014 by lanceyan | 104 Replieshtml

按照上一节中《搭建高可用mongodb集群(三)—— 深刻副本集》搭建后还有两个问题没有解决:java

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

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

传统数据库怎么作海量数据读写?其实一句话归纳:分而治之。上图看看就清楚了,以下 taobao岳旭强在infoq中提到的 架构图:linux

fenpian1

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

fenpian2

说了这么多传统数据库的架构,那Nosql怎么去作到了这些呢?mysql要作到自动扩展须要加一个数据访问层用程序去扩展,数据库的增长、删除、备份还须要程序去控制。一但数据库的节点一多,要维护起来也是很是头疼的。不过mongodb全部的这一切经过他本身的内部机制就能够搞定!顿时石化了,这么牛X!仍是上图看看mongodb经过哪些机制实现路由、分片:mongodb

fenpian3

从图中能够看到有四个组件:mongos、config server、shard、replica set。shell

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

架构搭好了,安装软件!

  • 一、准备机器,IP分别设置为: 192.168.0.13六、192.168.0.13七、192.168.0.138。
  • 二、分别在每台机器上创建mongodb分片对应测试文件夹。

    1

    2

    #存放mongodb数据文件

    mkdir -p /data/mongodbtest

    1

    2

    #进入mongodb文件夹

    cd  /data/mongodbtest

  • 三、下载mongodb的安装程序包

    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

  • 四、分别在每台机器创建mongos 、config 、 shard1 、shard二、shard3 五个目录。
    由于mongos不存储数据,只须要创建日志文件目录便可。

     

    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

  • 五、规划5个组件对应的端口号,因为一个机器须要同时部署 mongos、config server 、shard一、shard二、shard3,因此须要用端口进行区分。
    这个端口能够自由定义,在本文 mongos为 20000, config server 为 21000, shard1为 22001 , shard2为22002, shard3为22003.
  • 六、在每一台服务器分别启动配置服务器。

    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

  • 七、在每一台服务器分别启动mongos服务器。

    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);

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

    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&#93;

     

    &#91;code lang="shell"&#93;

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

    db.table1.stats();&#91;/code&#93;

     

    &#91;code lang="java" highlight="4,19,26,33"&#93;

                  {

                          "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&#93;

     

                  能够看到数据分到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多分担点责任吧!架构能够这样调整,把机器的负载分的更加均衡一点,每一个机器既能够做为主节点、副本节点、仲裁节点,这样压力就会均衡不少了,如图:

fenpian6

固然生产环境的数据远远大于当前的测试数据,大规模数据应用状况下咱们不可能把所有的节点像这样部署,硬件瓶颈是硬伤,只能扩展机器。要用好mongodb还有不少机制须要调整,不过经过这个东东咱们能够快速实现高可用性、高扩展性,因此它仍是一个很是不错的Nosql组件。

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

fenpian7

它的机制是选择一个ping 最快的机器来做为全部请求的入口,若是这台机器挂掉会使用下一台机器。那这样。。。。确定是不行的!万一出现双十一这样的状况全部请求集中发送到这一台机器,这台机器颇有可能挂掉。一但挂掉了,按照它的机制会转移请求到下台机器,可是这个压力总量仍是没有减小啊!下一台仍是可能崩溃,因此这个架构还有漏洞!不过这个文章已经太长了,后续解决吧。

参考:
http://docs.mongodb.org/manual/core/sharding-introduction/

原创文章,转载请注明: 转载自LANCEYAN.COM

相关文章
相关标签/搜索