一、集群的理论知识

1.1 集群成员 



MongoDB的集群类似于GreenPlum集群,由一个入口节点负责任务分发与结果统计,分片结节负责执行任务。不同GP,多了一个config servers。



集群有三个组件:



A。shards:分片,即数据结点,存储数据和执行计算。为了保证高可用和数据一致性,生产环境中shards应该做成



        replicasets(防止丢失数据)。集群中有一个primary shards,执行非分片的任务。



B。mongos(query routers):查询路由,负责client的连接,并把任务分给shards,然后收集结果。一个集群中可以有多个query



         routers(replica sets),以分担客户端请求(负载均衡)。



C。config server:配置服务器。保存了集群的元数据(比如数据放在哪个shards上),query router通过config server中的配置信 



      息决定把任务分配到哪个shards上。从3.2开始,config servers可以做成replica sets。



 



集群架构图:




mongodb 单节点扩容副本集集群 mongodb集群搭建的三种方式_运维


1.2 数据的分片


MongoDB在集合层面做数据分片,以shard key来分片。


shard key应该是一个索引字段或者复合索引字段。


MongoDB根据shard key创建chunks,然后把chunks均衡的分布在shards上。


两种分区方式:range和hash.结果全oracle里range分区和hash分区理解一下。


 


1.3 分片的性能


range分片:适合range query,能够使用批量I/O。但是如果分区键不是均匀分布的的,则可能只用到集群中的少数据结点,不能发挥集群的性能。


hash分片:数据平均分布到各个节点上。但是范围查询时需要各个节点一起读,效率比range分片低。


MongoDB支持数据标签,用户可以指定某个标签的数据分布到特定节点上。


1.4数据的平衡

1.4.1 分裂(splite)


    当一个chunk的大小超过chunk的最大值大时,这个chunk会分裂为两个。该过程只修改元数据,不迁移数据。该过程是后台进程完成,不需要人工执行。chunk默认值64M。


mongodb 单节点扩容副本集集群 mongodb集群搭建的三种方式_数据_02


     分裂的特点:


    1)默认的chunk是64M。小的chunk好处是数据平衡性,坏处是经常要做数据平衡。大的chunk则正好相反


    2)只有在insert或者update时才会发生chunk的分裂。如果人为修改chunk的大小,是不会发生分裂的。


    3)某个特定的shard key values的chunk可能大小默认的chunk大小,并且无法分裂


 


1.4.2 平衡(balance)


    如果集群中的某个节点保存的数据太多,就会自动的把数据分配给其它节点。该过程是后台进程完成,不需要人工执行。


在平衡数据时,先从原始节点复制数据到新节点,然后更新复制的数据。只有在平衡完成后,才会删除原始节点上的数据。如果发生了错误,则不影响原始节点数据。


    增加和删除节点都会造成数据的平衡。


    注意:并非所有的数据不均匀都会自动平衡,一般来说,一个节点可以至少存储数百个chunk(其它节点可以一个chunk都没有)而不触发平衡操作。


    数据平衡的特点:

    1)自动化的,默认是开启的

    2)每次只移动一个chunk,但可能在源上的chunk没有删除前就开始新的移动

    3)只有在节点的chunk数量差距非常大的时候才触发。

        触发的阀值:

mongodb 单节点扩容副本集集群 mongodb集群搭建的三种方式_数据_03

    4)一直迁移到任何两个节点上的chunk数量相差小于2为止。

    5)如果设置了shard可以使用的磁盘最大小值 ,如果节点的磁盘使用超过了这个值,将不会balance到这个节点上。

    6)平衡完毕后,会修改confige server的信息

    7)_secondaryThrottle默认为true,当定入至少一个secondary。


1.5 config server

1.5.1 config serve配置成replicatsion sets的限制


     config servers配置成replicate sets有以下限制:


        1)没有仲裁节点
        2)没有延迟节点


        3)build indexes必须为true


1.5.2 config server的读写


使用write majority方式写。


使用read majority方式读。


1.6 系统架构 

1.6.1 用于生产的架构


    用于生产的架构必须要保证数据的冗余,mongos、shards、config server必须要做成replication sets。每个集群都必须有一个独立的config server(一台config server不能多用)。生产的架构示意:


mongodb 单节点扩容副本集集群 mongodb集群搭建的三种方式_数据库_04


    说明:


        不做replications set的话,shard节点是没有数据冗余的功能,如果数据丢失了,就找不回来(此时其它节点可以正常工作)。因此对于生产环境,做复制是十分必要的。同样,confige server也是一样,也要做复制。


 


1.6.2. 用于测试的架构


    测试的架构可以不做replication sets,以节省机器为目的,可以把mongos和config server和shards放一起。测试架构示意:


mongodb 单节点扩容副本集集群 mongodb集群搭建的三种方式_mongodb 单节点扩容副本集集群_05


1.7 分片键shards key


分片键中的列必须是index key或者组合index key,分片键也可以组合的(比如key(c1,c2))。分片键在数据插入后不能改变。


1.7.1 hash 分片


    hash key能够很好的将数据均匀的分布在不同的节点上。选择做为hash key的键应该有很大的基数,一般将object_id或者timestamp列作为hash 键。


    对于空集合,hash 分片会自动的在每个节点上创建两个chunk,通过修改shardCollection的numInitialChunks参数来决定空集合的chunk个数。


 


1.7.2 分片键对性能的影响


     不同的分片键到性能有不同的影响。


     1)使用object_id这样唯一性或者基数很大的键做为分布键,则所有数据被离散的分布到各个节点上,这样能显著的提高集群的写性能,适合存储大量数据的场合。


查询集群中的数据时,最快的方式是查询中包含了分片键,这样就能直接定位的到数据所在的节点,否则就要在所有节点上执行全表扫描。对于读操作,考虑以下两点:


 A.确定最常用被查询的列


 B.考虑这些列哪个对于性能影响最大


如果最后选定的这个列基数比较小,那么在分片皱中加入二级键,做一二级键组成的集合基数小即可(对比oracle中的组合索引)。


 


1.8 集群的高可用

 1.8.1 shards节点


    如果shars节点没有做replication sets,该节点如果宕机,这部分数据就不可访问。因此为了业务的持续性,对于shards节点有必要做成replication sets.


1.8.2 config server


   1) 和shards一样,如果没有做replication set此时又宕机的话,整个集群都不能用。如果做了replications sets,当其中的一台机宕机后,会自动选出主节点,整个集群还可以用。如果宕机的过多无法选出主节点,整个集群依然可以用(读写),但是不能有chunk操作(chunk的创建、移动)。


   2)如果不用replication set用mirror的方式来做配置config server,在confige server宕机后,则需要重启所有的集群成员来连接镜像confige server。解决重启的办法是配置DNS


1.8.3 mongos


    mongos是应用程序的入口,如果mongos宕机了,应用程序就无法使用集群。因此一般也做也replication Set。mongos使用的资源相对于shards和confige server是最小的,因此可以和应用服务器放一台机上。当mongos宕机修复后,会自动从config server读取数据.


 


1.9 集群的查询

1.9.1 查询路径


    mongos是应用程序的接口。mongos通过config server中的信息查询数据在哪个节点从而分配任务。如果结果有中sort(),primary shard会合并shards的数据然后排序返回给mongos再返回给client.limit()操作直接在shards上完成。skip()不会发送给shards来执行。


1.9.2 mongos标识


    应用程序连接到集群后,执行isMaster()命令,返回:

{ "ismaster":true, "msg":"isdbgrid", "maxBsonObjectSize":16777216, "ok":1 }

则表示是mongos。如果msg不是 isdbgrid则不是mongos。


 


二、集群的搭建

2.1集群搭建的步骤


 


2.1.1 配置confige server


    以下代码是搭建一个三个结点的replication sets的config server:

1).创建replications sets mongod --configsvr --replSet configReplSet --port <port>--dbpath <path> 或者使用配置文件 sharding: clusterRole: configsvr replication: replSetName: configReplSet net: port:<port> storage: dbpath:<path> 2).初始化。进入其中一个的mongo shell: rs.initiate({ _id:"configReplSet", configsvr:true, members:[ {:0,:"<host1>:<port1>"}, {:1,:"<host2>:<port2>"}, {:2,:"<host3>:<port3>"} ] })

2.1.2 创建mongos实例(路由实例)

mongos --configdb configReplSet/<cfgsvr1:port1>,<cfgsvr2:port2>,<cfgsvr3:port3>

2.1.3 加入 shards


1)连接mongos实例:


mongo --host <hostname of machine running mongos> --port <port mongos listens on>


2)在其中一台mongos上加入节点:


sh.addShard( "rs1/mongodb0.example.net:27017" )     (replications sets只需要加入rs中一个节点即可)


sh.addShard( "mongodb0.example.net:27017" )           (单机)


可能需要一段时间来迁移数据


2.1.4 设置分片
2.1.4.1 设置数据库分片


在设置集合分片之前,必须设置要分片的数据库。连接mongos:


mongo --host <hostname of machine running mongos> --port <port mongos listens on>


执行:


sh.enableSharding("<database>")或者db.runCommand( { enableSharding: <database> } )


2.1.4.2 设置集合分片


1)确定集合的shard key。如果集合已经有数据,那么在shard key上创建index。如果没有数据,集群会自动为shard key创建索引


2)将集合加入分片


sh.shardCollection("<database>.<collection>", shard-key-pattern)


如:


sh.shardCollection("records.people", { "zipcode": 1, "name": 1 } )  shard key 为zipcode,如果有相同的zipcode再根据name来分


sh.shardCollection("people.addresses", { "state": 1, "_id": 1 } )      同上


sh.shardCollection("assets.chairs", { "type": 1, "_id": 1 } )               同上


sh.shardCollection("events.alerts", { "_id": "hashed" } )                  hash分片


2.1.5 配置镜像config server


注意:不推荐使用镜像,请使用replication sets。


在每个config server上启动mongod实例:


mongod --configsvr --dbpath /data/configdb --port 27019


每个路由节点启动mongs,--configdb后面的连接字符串要一致


mongos --configdb cfg0.example.net:27019,cfg1.example.net:27019,cfg2.example.net:27019


2.2 实验

2.2.1实验环境


node1

192.168.75.10

config server1(configRS 37017)       mongos1( 27017)     shard1(47017)

node2

192.168.75.11

config server2(configRS 37017)       mongos2(27017)     shard2(47017)

node3

192.168.75.12

config server3(configRS 37017)                                        shard3( 47017)


在三台主机上,分别安mongoConfig、 mongoShard 、mongoRouter三个实例


2.2.2.配置config server


1)配置文件


在三台主机配置configution文件:

[root@node1 mongoConfig]#.config dbpath=/usr/local/mongoConfig/data logpath=/usr/local/mongoConfig/log/mongo.log port=37017 fork=true #master=true replSet=configRS configsvr=true

2)在三台机器上启动config server实例

[root@node1 bin]#./mongod -f /usr/local/mongoConfig/mongodb.conf about to fork child process,for. forked process:3032 child process started successfully, parent exiting

注意,打开防火墙的37017端口


3)初始化config server


连接到其中一台configer server:

[root@node1 bin]#./mongo --port 37017

执行以下初始化:

>.initiate({ ...:"configRS", ...:true, ...:[ ...{:0,:"192.168.75.10:37017"}, ...{:1,:"192.168.75.11:37017"}, ...{:2,:"192.168.75.12:37017"} ...] ...}); {"ok":1}

2.2.3.配置mongos


在每台机器上执行:

./mongos --configdb configRS/192.168.75.10:37017,192.168.75.11:37017,192.168.75.12:37017--port 27017--fork --logpath=/usr/local/mongoRouter/log/mongo.log

2.2.4.启动三个shard实例


在三台机器修改配置文件:

[root@node1 mongoShard]#.config dbpath=/usr/local/mongoShard/data logpath=/usr/local/mongoShard/log/mongo.log port=47017 fork=true

在三台机器上启动实例:

[root@node1 bin]#./mongod -f /usr/local/mongoShard/mongodb.config about to fork child process,for. forked process:17508 child process started successfully, parent exiting

2.2.5.将shards加入集群


在一台机器上连接mongos实例

./mongo --port 27017

执行:

sh.addShard("192.168.75.10:47017") sh.addShard("192.168.75.11:47017") sh.addShard("192.168.75.12:47017")

2.2.6.将数据库加入分片

sh.enableSharding("testShards")

2.2.7.将集合加入分片

sh.shardCollection("testShards.test",{"_id":"hashed"});

2.2.8.插入数据


在某个mongos上执行:

mongos> use testShards      switched to db testShards      mongos>;      test      mongos>.test.insert({"name":"testshrads","msg":"ok"});      WriteResult({"nInserted":1})      mongos>.test.insert({"name":"testshrads2","msg":"ok"});      WriteResult({"nInserted":1})      mongos>.test.insert({"name":"testshrads3","msg":"ok"});      WriteResult({"nInserted":1})      mongos>.test.insert({"name":"testshrads4","msg":"ok"});      WriteResult({"nInserted":1})      mongos>.test.insert({"name":"testshrads5","msg":"ok"});      WriteResult({"nInserted":1})      mongos>.test.insert({"name":"testshrads6","msg":"ok"});      WriteResult({"nInserted":1})      mongos>.test.insert({"name":"testshrads7","msg":"ok"});      WriteResult({"nInserted":1})      mongos>.test.insert({"name":"testshrads8","msg":"ok"});      WriteResult({"nInserted":1})      mongos>.test.insert({"name":"testshrads9","msg":"ok"});      WriteResult({"nInserted":1})

 


查看数据:


mongos> db.test.find(); { "_id" : ObjectId("56815a0617de6d7dfc1051b5"), "name" : "testshrads", "msg" : "ok" } { "_id" : ObjectId("56815a0e17de6d7dfc1051b6"), "name" : "testshrads2", "msg" : "ok" } { "_id" : ObjectId("56815a1717de6d7dfc1051b8"), "name" : "testshrads4", "msg" : "ok" } { "_id" : ObjectId("56815a1b17de6d7dfc1051b9"), "name" : "testshrads5", "msg" : "ok" } { "_id" : ObjectId("56815a1e17de6d7dfc1051ba"), "name" : "testshrads6", "msg" : "ok" } { "_id" : ObjectId("56815a2617de6d7dfc1051bc"), "name" : "testshrads8", "msg" : "ok" } { "_id" : ObjectId("56815a1217de6d7dfc1051b7"), "name" : "testshrads3", "msg" : "ok" } { "_id" : ObjectId("56815a2117de6d7dfc1051bb"), "name" : "testshrads7", "msg" : "ok" } { "_id" : ObjectId("56815a2917de6d7dfc1051bd"), "name" : "testshrads9", "msg" : "ok" }


2.2.9.测试数据


连接到某个shards上:

#./mongod --port 47017      执行:      > use testShards      switched to db testShards      >.test.find();      {"_id":ObjectId("56815a1217de6d7dfc1051b7"),"name":"testshrads3","msg":"ok"}      {"_id":ObjectId("56815a2117de6d7dfc1051bb"),"name":"testshrads7","msg":"ok"}      {"_id":ObjectId("56815a2917de6d7dfc1051bd"),"name":"testshrads9","msg":"ok"}

发现数据分片正常.


mongoDB集群搭建完毕。