一、集群的理论知识
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。
集群架构图:
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。
分裂的特点:
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数量差距非常大的时候才触发。
触发的阀值:
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的读写
写:只有集群的元数据发生变化时,才会更新config server的信息。比如加入、删除节点、分裂chunk。使用write majority方式写。
读:当mongos程序重启或集群的元数据发生了变化mongos才会读取元数据。使用read majority方式读。
1.6 系统架构
1.6.1 用于生产的架构
用于生产的架构必须要保证数据的冗余,mongos、shards、config server必须要做成replication sets。每个集群都必须有一个独立的config server(一台config server不能多用)。生产的架构示意:
说明:
不做replications set的话,shard节点是没有数据冗余的功能,如果数据丢失了,就找不回来(此时其它节点可以正常工作)。因此对于生产环境,做复制是十分必要的。同样,confige server也是一样,也要做复制。
1.6.2. 用于测试的架构
测试的架构可以不做replication sets,以节省机器为目的,可以把mongos和config server和shards放一起。测试架构示意:
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这样唯一性或者基数很大的键做为分布键,则所有数据被离散的分布到各个节点上,这样能显著的提高集群的写性能,适合存储大量数据的场合。
2)查询集群中的数据时,最快的方式是查询中包含了分片键,这样就能直接定位的到数据所在的节点,否则就要在所有节点上执行全表扫描。对于读操作,考虑以下两点:
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: [
{ _id: 0, host: "<host1>:<port1>" },
{ _id: 1, host: "<host2>:<port2>" },
{ _id: 2, host: "<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]# cat mongodb.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, waiting until server is ready for connections.
forked process: 3032
child process started successfully, parent exiting
注意,打开防火墙的37017端口
3)初始化config server
连接到其中一台configer server:
[root@node1 bin]# ./mongo --port 37017
执行以下初始化:
> rs.initiate( {
... _id: "configRS",
... configsvr: true,
... members: [
... { _id: 0, host: "192.168.75.10:37017" },
... { _id: 1, host: "192.168.75.11:37017" },
... { _id: 2, host: "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]# vi mongodb.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, waiting until server is ready for connections.
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> show collections;
test
mongos> db.test.insert({"name":"testshrads","msg":"ok"});
WriteResult({ "nInserted" : 1 })
mongos> db.test.insert({"name":"testshrads2","msg":"ok"});
WriteResult({ "nInserted" : 1 })
mongos> db.test.insert({"name":"testshrads3","msg":"ok"});
WriteResult({ "nInserted" : 1 })
mongos> db.test.insert({"name":"testshrads4","msg":"ok"});
WriteResult({ "nInserted" : 1 })
mongos> db.test.insert({"name":"testshrads5","msg":"ok"});
WriteResult({ "nInserted" : 1 })
mongos> db.test.insert({"name":"testshrads6","msg":"ok"});
WriteResult({ "nInserted" : 1 })
mongos> db.test.insert({"name":"testshrads7","msg":"ok"});
WriteResult({ "nInserted" : 1 })
mongos> db.test.insert({"name":"testshrads8","msg":"ok"});
WriteResult({ "nInserted" : 1 })
mongos> db.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" }
{ "_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
> db.test.find();
{ "_id" : ObjectId("56815a1217de6d7dfc1051b7"), "name" : "testshrads3", "msg" : "ok" }
{ "_id" : ObjectId("56815a2117de6d7dfc1051bb"), "name" : "testshrads7", "msg" : "ok" }
{ "_id" : ObjectId("56815a2917de6d7dfc1051bd"), "name" : "testshrads9", "msg" : "ok" }
发现数据分片正常.
mongoDB集群搭建完毕。