先看一张图:

先做一些解释:1.shard片:一般为一台单独的服务器,即为一个数据存储节点,这个存储节点需要做复制集,实现高可用以及自动故障转移,一般一个分片集群有多个shard片;

  1. config服务器:主要是记录shard的配置信息(元信息metadata),如数据存储目录,日志目录,端口号,是否开启了journal等信息,为了保证config服务器的可用性,也做了复制集处理,注意,一旦配置服务器无法使用,则整个集群就不能使用了,一般是独立的三台服务器实现冗余备份,这三台可能每一台是独立的复制集架构。3.Mongos路由进程(Router):应用程序通过驱动程序直接连接router,router启动时从配置服务器复制集中读取shared信息,然后将数据实际写入或读取(路由)到具体的shard中。

部署配置

注意:此处由于我这边只有一台服务器(PC机),所以这里实现的是一个伪集群,部署架构如图所示:MongoDB分片集群 - 图1

一个Shard(复制集模式),一个config进程,一个router进程,均在同一台服务器上面

沿用上一节的复制集作为shard,

启动:

配置服务器启动:

路由服务器启动

  1. mongos --logpath=D:\MongoDB\Server\3.2\logs\dbrouter.log --port=40004 --configdb=linfl-PC:40003

添加分片信息到集群:

报错了,原来的rs0由于已经存在config数据库了,去把他删掉:

  1. D:\MongoDB\Server\3.2\bin>mongo --port 40000
  2. 2017-02-27T16:14:51.454+0800 I CONTROL [main] Hotfix KB2731284 or later update
  3. is not installed, will zero-out data files
  4. MongoDB shell version: 3.2.9
  5. connecting to: 127.0.0.1:40000/test
  6. rs0:PRIMARY> show dbs
  7. cms 0.000GB
  8. config 0.000GB
  9. test 0.000GB
  10. rs0:PRIMARY> use config
  11. switched to db config
  12. rs0:PRIMARY> db.dropDatabase()
  13. { "dropped" : "config", "ok" : 1 }

然后重新连接到4004执行添加分片即可,看下状态:

  1. D:\MongoDB\Server\3.2\bin>mongo --port 40004
  2. is not installed, will zero-out data files
  3. MongoDB shell version: 3.2.9
  4. connecting to: 127.0.0.1:40004/test
  5. mongos> sh.addShard("rs0/linfl-PC:40000,linfl-PC:40001")
  6. { "shardAdded" : "rs0", "ok" : 1 }
  7. mongos> sh.status()
  8. --- Sharding Status ---
  9. sharding version: {
  10. "_id" : 1,
  11. "minCompatibleVersion" : 5,
  12. "currentVersion" : 6,
  13. "clusterId" : ObjectId("58b3d9df84493cb599359c8b")
  14. }
  15. shards:
  16. active mongoses:
  17. balancer:
  18. Currently enabled: yes
  19. Currently running: no
  20. Failed balancer rounds in last 5 attempts: 5
  21. Last reported error: remote client 192.168.56.1:51845 tried to initiali
  22. ze this host as shard rs0, but shard name was previously initialized as config
  23. Time of Reported error: Mon Feb 27 2017 16:15:48 GMT+0800
  24. Migration Results for the last 24 hours:
  25. No recent migrations
  26. databases:
  27. { "_id" : "cms", "primary" : "rs0", "partitioned" : false }
  28. { "_id" : "test", "primary" : "rs0", "partitioned" : false }

对config数据库中的各个集合做如下解释:

正常来讲,一个完整的生产环境至少需要9个Mongod实例,一个Mongos实例,10台机器才能组成,而由于可以对部分实例采用合并在一台机器上进行部署的操作(对资源消耗较低,或对资源消耗的类型不同),能够得到典型的部署结构如下:

以上部署方案使得每个shard(复制集)中的节点完全分开到不同的服务器上,并将config服务分开,使得其中任何一台服务器宕机,集群都可正常工作,当然我认为,最少只需要三台服务器即可构成稳定的集群(3坏1可正常工作),然而却非典型的架构了。

MongoDB的分片是基于集合(表)来进行的,要对一个集合分片,就要像使其所在的数据库支持分片。

MongoDB的分片是基于返回的,即任何一个文档一定位于指定片键的某个范围内,一单片键选择好以后,chunks就会按照片键将一部分documents从逻辑上组合在一起如:对users集合选择city作为片键来分片,如果city字段中存在 ‘beijing’,’guangzhou’,’changsha’,初始时随机的向集群中插入文档,由于初始时chunks比较小,没有达到阈值64MB或10000个文档,不需要分片,随着继续插入,超过chunk阈值后chunk被分割成两个,最终分步可能如下所示:

开始键值 — 结束键值 —所在分片开始键值 结束键值 所在分片

需要注意的是:chunks所包含的文档并非物理上包含,而是一种逻辑包含,指标是带有片键的文档会落在那个范围内。

使集合支持分片,必须先使数据库支持分片:

  1. sh.enableSharding('cms')

对于已有的集合如果要分片,那么选定片键后,需要在片键上面首先创建索引,如果集合初始状态为空,则自动创建索引查看rs.status()可以看到,如下信息片段:

  1. databases:
  2. { "_id" : "cms", "primary" : "rs0", "partitioned" : true }
  3. { "_id" : "test", "primary" : "rs0", "partitioned" : false }

证明 cms已经支持分片

在已存在的集合中分片先创建索引:

注意:由于我本地是单台PC,做分片后看不到效果,但如果有两个分片,分别为rs0,与rs1,往users中插入数据,最后的结果会是

MongoDB分片集群 - 图2

可以看到具体的块分割过程,当存储的chunk小于64MB时,不分割,当大于64MB时,开始分割成两部分,-∞~beijing,beijing~∞,随着数据的进一步增大,beijing~∞被继续分割成beijing~guangzhou,guangzhou~∞,这样,rs0就有三个chunk了,随后,-∞到beijing的chunk开始发生转移,从rs0迁移到rs1上面去,依次类推,MongDB就是这样实现了海量数据的分布式存储的。

集群平衡器

在上一步的操作中最后讲到了块chunk从rs0迁移到rs1的操作,这个动作是由 平衡器的后台进程自动完成的。

当一个被分片的集合的所有chunk在集群中分布不均衡时(如200个chunk在rs0,50个在rs1上),平衡器就会将chunk从最多的片上迁移到最少的片上,直到chunk数量基本相等为止。平衡器会根据chunk数量的不同,有不同的规则触发块迁移,默认配置为chunk<20,阈值为2,2080,阈值为8,这也就是为什么我们在刚才要在chunk为3时才能看到迁移过程的变化。基本的chunk迁移流程如下所示:

本示例中源片指的是rs0,目标片指的是rs1

集群的读、写与单个MongoDB的读写是一样的,对应用程序时透明的,这里主要看分片集群对性能的影响。

片键是决定查询落到哪一个chunk上的依据,这个信息保存在配置服务器上,而索引是针对每个片上的集合来说的,每个片都会为自己的集合创建索引数据,因此,查询语句中是否包含片键与索引对查询性能影响较大。可以通过explain解释执行查看,需要注意几个点:

nscanned:查询用到索引时,索引中扫描的文档数nscannedObjects:在集合中扫描的文档数nscannedAllPlans:所有查询计划中扫描的文档总数nscanned:选定的某一种查询计划下扫描的文档总数

片键选择策略

好的片键应该具有如下特质:

  1. 分发写操作
  2. 读操作不能太过随机化(尽量局部化)

通常我们需要由几个字段组合作为片键,如city+_id,既能够保证同一city下的文档尽量分布在同一个片上,_id则保证了chunk能够一直被分割。

在实际业务中可以考虑order表中 company_id与create_date或id共同组成片键。

只有当数据量很大、读写请求很高时才适合用分片集群。