当前位置:   article > 正文

MongoDB分片介绍与部署

MongoDB分片介绍与部署

        大家好,MongoDB是一个面向文档的NoSQL数据库,以其灵活性、性能和可扩展性而闻名。而分片(Sharding)是MongoDB提供的一种水平扩展数据的方式,允许将数据分布在多个物理服务器上,以应对大规模数据的存储和处理需求。

前面跟大家分享了关于 MongoDB副本集介绍与部署,可以参考:

MongoDB副本集介绍与部署

一、分片介绍

        分片(shard)是集群中负责管理数据的各个子集的一台或多台服务器。一个分片可以由多台服务器组成。在分片中,不同的服务器负责保存数据的不同部分,它们共同组成了整个数据集。

        为了保证数据在各个分片间的均匀分布,MongoDB会自动在不同的分片之间移动数据子集。这些数据子集的移动是基于片键(shard key)来决定的。

1、分片数据

(1)一分片一区间

        分片最简单的方式就是每个分片负责存储一个区间段的数据,如用户名由4个分片进行存储,由用户名作为分片的依据那么将是如下情况:

        这种方式有一个问题,在数据迁移时,可能会造成级联效应,即如果要将第一分片中的数据均衡,可能要影响很多分片,造成移动的数据量过大,影响整个系统的正常运行。原因很简单,每个分片要保持一区间模式。

(2)一分片多区间

        这种方式可以有效的避免一分片一区间的数据迁移问题,宗旨是每个分片可以存储多个区间的数据。例子,如果[a, f)和[f, n)区间的数据比后面的两个分片都大,需要进行数据移动,则可以将[a, f)分为[a, c)和[c, f),[f, n)分为[f, j)和[j, n),并进行数据移动,则最终成为下面的分片状态:

在添加新分片后,MongoDB可以从每个分片取出部分数据,移动到新分片上。

2、创建块

        在决定如何分配数据时,必须选择一个键来定义数据的块区间,这个键被称为片键(shard key)。片键可以是任意字段或字段的组合。

        在最初的情况下,MongoDB只会创建一个数据块,该数据块的区间覆盖了整个数据集。只有当数据量达到一定程度时,MongoDB才会进行分片。一旦设置了分片配置,就需要指定片键。您可以通过使用命令 sh.shardCollection 来完成这一设置。

3、平衡

        如果存在多个可用的分片,只要块的数量足够多,MongoDB就会将数据迁移到其他分片上。这个迁移过程被称为平衡,由平衡器进程负责执行。

        触发平衡器的条件是:一个分片的块数量必须比最少块的分片多至少9个。此时,块将从拥挤的分片迁移到其他分片,直到各分片达到平衡为止。

        平衡器并不会过于敏感,否则,即使分片之间稍微不平衡,也会频繁触发平衡操作,导致系统资源的过度浪费。

以下是一些方便验证配置的方法:

(1)设置块大小(chunkSize): 可以通过设置块的大小来快速观察数据迁移过程。例如,使用 --chunkSize 1 可以将块的大小设置为1MB。这样,在插入10MB的数据后,就会触发数据迁移。

(2)递增块大小: 可以设置递增的块大小。这样,在创建前十几个块时,MongoDB会自动将块的大小从200MB逐渐降低到64MB。

4、mongos

        mongos是用户与MongoDB分片集群之间的交互点,它充当了用户访问集群的唯一入口,将复杂的处理流程隐藏在后台。简言之,所有对集群的操作都通过mongos服务进行,mongos会将用户的请求转发到相应的分片上去处理。

5、集群中的角色

构建一个MongoDB Sharding Cluster需要三种角色:

(1)Shard Server(分片服务器): 这些是mongod实例,负责存储实际的数据块。在实际生产环境中,一个Shard Server角色通常由多台机器组成一个Replica Set来承担,以防止主机单点故障。

(2)Config Server(配置服务器): 这些也是mongod实例,它们存储了整个集群的元数据,包括Chunk信息等。

(3)Route Server(路由服务器): 这些是mongos实例,充当前端路由,客户端通过它们接入集群。它们让整个集群对客户端看起来像单一的数据库,使得前端应用可以透明地使用集群。

        这三种角色共同协作,构成了一个MongoDB Sharding Cluster,能够实现数据的水平扩展,并在处理大数据量时表现出色。

分片集群的架构:

二、分片配置

        由于项目需要,要部署一个副本集的分片集群,有四个服务器用来部署mongoDB,我们将数据分为三个副本集分片,并分别部署三个config server和三个mongos, 则具体分配如下:

  1. Server 218.30.117.193
  2. 10001 shard1
  3. 10002 shard2
  4. 10003 shard3
  5. 20001 config1
  6. Server 218.30.117.195
  7. 10001 shard1
  8. 10002 shard2
  9. 10003 shard3
  10. 20002 config2
  11. Server 218.30.117.196
  12. 10001 shard1
  13. 10002 shard2
  14. 10003 shard3
  15. 20003 config3
  16. Server 218.30.117.197
  17. 30001 mongos1
  18. 30002 mongos2
  19. 30003 mongos3

1、启动Config server实例

        配置数据库(Config Server)是存放集群元数据的mongod实例。使用 `--configsvr` 选项可以将一个mongod实例指定为配置服务器。每个配置服务器都保存了集群的完整元数据。

        在生产环境中,必须部署三个配置服务实例,每个实例运行在不同的服务器上,以确保良好的运行时间和数据安全。然而,在测试环境中,你可以在一台独立的服务器上运行三个配置服务实例。

        配置服务实例接收相对较小的流量,并且占用系统资源较少。因此,你可以在运行配置服务实例的系统上同时运行其他实例,以最大程度地利用系统资源。

(1)    为每个配置服务实例创建一个数据目录。默认的情况下,配置服务将数据文件存储在/data/configdb目录下。

(2)    运行三个配置服务实例。使用下面命令:

mongod --configsvr --dbpath <path> --port <port> --logpath <logpath>

如端口为20001,数据存放目录为/mongo-data/config1,日志路径为/mongo-data/config1/log.log,并创建服务进程,命令如下:

mongod --fork --configsvr --port 20001 --dbpath /mongo-data/config1/ --logpath /mongo-data/config1/log.log

2. 启动mongos实例

        Mongos实例是轻量级的,不需要数据目录。你可以在运行其他集群组件的系统上运行一个mongos实例,例如一个应用服务或者一个mongod进程。Mongos的默认端口是27017。

        当你启动一个mongos实例时,你需要在配置文件或者命令行中指定那三个config server的主机名。为了操作的灵活性,最好使用配置服务器的DNS名而不是IP地址。如果你没有使用可解析的主机名,在没有重启每个mongos和mongod实例的情况下,无法更改配置服务器的名称或IP地址。

启动一个mongos实例,命令行如下:

mongos --configdb <config server hostnames>

举个例子,启动一个mongos连接以下配置服务器:

  1. cfg0.example.net
  2. cfg1.example.net
  3. cfg2.example.net

将运行下面的命令:

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

3. 向集群中添加分片

一个分片可以是一个单独的mongod或者是一个副本集。在生产环境中,每个分片应该是一个副本集。

(1)    从mongo shell连接到mongos实例上。执行下面的命令:

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

例如:

mongo --host mongos0.example.net --port 27017

(2)    使用sh.addShard()命令将每个分片添加到集群中,如下面的例子所示。为每个分片单独的使用sh.addShard()命令。如果分片是一个副本集,则要指定副本集名并指定组的一个成员。

下面的例子使用了sh.addShard()命令添加一个分片:

  • 通过一个运行在mongodb0.example.net上的27017端口的成员,将一个名为rs1的副本集分片添加到集群中,命令如下:
    sh.addShard(“rs1/mongodb0.example.net:27017”)

    版本2.0.3改变的。

    在这个版本之前,必须指定所有的副本集成员,如:

    sh.addShard( "rs1/mongodb0.example.net:27017,mongodb1.example.net:27017,mongodb2.example.net:27017" )
  • 添加一个运行在mongodb0.example.net服务器上的27017端口的,命令如下:
    sh.addShard( "mongodb0.example.net:27017" )

4. 开启一个数据库的分片功能

        在对一个集合进行分片之前,你必须先启用该集合所在的数据库的分片功能。启用分片功能并不会重新分配数据,但是它确保了集合可以被分片。

        一旦你启用了数据库的分片功能,MongoDB会为其分配一个主分片,使得MongoDB在进行分片之前会将所有的数据保存在该数据库上。

  • 在mongo shell中连接一个mongos实例,使用以下命令语法:
    mongo --host <hostname of machine running mongos> --port <port mongos listens on>
  • 执行sh.enableSharding()方法,指定想要开启分片功能的数据库的名字。使用下面的语法:
    sh.enableSharding("<database>")

    也可以使用enableSharding命令,语法如下:

    db.runCommand( { enableSharding : <database> } )

可以对每个collection开启分片功能。

(1)    决定使用什么作为片键。片键的选择会影响分片的性能。

(2)    如果一个collection已经包含数据,则必须在片键的字段上使用ensureIndex()命令建立一个索引。如果collection是空的,则MongoDB将在sh.shardCollection()阶段建立一个索引。

(3)    通过在mongo shell中执行sh.shardCollection()方法开启一个collection的分片功能。这个方法的使用语法如下:

db.shardCollection(“<database>.<collection>”, shard-key-pattern)

用你的数据库命名空间,由数据库名,一个点和collection的名字组成,替换<database>.<collection>字符串。Shard-key-pattern代表你的片键,你可以使用与一个索引相同的模式。

示例:

  1. sh.shardCollection("records.people", { "zipcode": 1, "name": 1 } )
  2. sh.shardCollection("people.addresses", { "state": 1, "_id": 1 } )
  3. sh.shardCollection("assets.chairs", { "type": 1, "_id": 1 } )
  4. db.alerts.ensureIndex( { _id : "hashed" } )
  5. sh.shardCollection("events.alerts", { "_id": "hashed" } )

这些分片操作按顺序依次表示为:

  • 数据库records 中的People集合使用了片键{ “zipcode” : 1, “name” : 1 }

这个片键通过字段zipcode的值进行数据分配。如果这个有大量相同的值,则会根据name字段的值对块进行分裂。

  • 数据库people的addresses集合使用了片键{ “state” : 1, “_id” : 1 }

这个片键通过字段state的值进行数据分配。如果这个有大量相同的值,则会根据_id字段的值对块进行分裂。

  • 数据库assets的chairs集合使用了片键{ “type” : 1, “_id” : 1 }

这个片键通过字段type的值进行数据分配。如果这个有大量相同的值,则会根据_id字段的值对块进行分裂。

  • 数据库events的alerts集合使用了片键{ “_id” : “hashed” }

在版本2.4中最新出现的。

这个片键通过字段_id的散列值进行数据分配。MongoDB计算_id字段的散列值作为散列索引,它将提供集群中文档的均匀分布。

三、分片部署

1、启动mongod

  1. Server 218.30.117.193
  2. # /usr/local/mongodb/bin/mongod --fork --shardsvr --port 10001 --dbpath /usr/tmp/shard1/ --logpath /usr/tmp/shard1/log.log --replSet shard1 --rest
  3. # /usr/local/mongodb/bin/mongod --fork --shardsvr --port 10002 --dbpath /usr/tmp/shard2/ --logpath /usr/tmp/shard2/log.log --replSet shard2 --rest
  4. # /usr/local/mongodb/bin/mongod --fork --shardsvr --port 10003 --dbpath /usr/tmp/shard3/ --logpath /usr/tmp/shard3/log.log --replSet shard3 --rest
  5. Server 218.30.117.195
  6. # /usr/local/mongodb/bin/mongod --fork --shardsvr --port 10001 --dbpath /usr/tmp/shard1/ --logpath /usr/tmp/shard1/log.log --replSet shard1 --rest
  7. # /usr/local/mongodb/bin/mongod --fork --shardsvr --port 10002 --dbpath /usr/tmp/shard2/ --logpath /usr/tmp/shard2/log.log --replSet shard2 --rest
  8. # /usr/local/mongodb/bin/mongod --fork --shardsvr --port 10003 --dbpath /usr/tmp/shard3/ --logpath /usr/tmp/shard3/log.log --replSet shard3 --rest
  9. Server 218.30.117.196
  10. # /usr/local/mongodb/bin/mongod --fork --shardsvr --port 10001 --dbpath /usr/tmp/shard1/ --logpath /usr/tmp/shard1/log.log --replSet shard1 --rest
  11. # /usr/local/mongodb/bin/mongod --fork --shardsvr --port 10002 --dbpath /usr/tmp/shard2/ --logpath /usr/tmp/shard2/log.log --replSet shard2 --rest
  12. # /usr/local/mongodb/bin/mongod --fork --shardsvr --port 10003 --dbpath /usr/tmp/shard3/ --logpath /usr/tmp/shard3/log.log --replSet shard3 --rest

2、启动config Server

  1. Server 218.30.117.193
  2. # /usr/local/mongodb/bin/mongod --fork --configsvr --port 20001 --dbpath /usr/tmp/config1/ --logpath /usr/tmp/config1/log.log --rest
  3. Server 218.30.117.195
  4. # /usr/local/mongodb/bin/mongod --fork --configsvr --port 20002 --dbpath /usr/tmp/config1/ --logpath /usr/tmp/config1/log.log --rest
  5. Server 218.30.117.196
  6. # /usr/local/mongodb/bin/mongod --fork --configsvr --port 20003 --dbpath /usr/tmp/config1/ --logpath /usr/tmp/config1/log.log --rest

3、启动mongos

这里为了方便的看到分片过程,将chunkSize设置为1,也就是每个块大小为1MB。

  1. 218.30.117.197
  2. # /usr/local/mongodb/bin/mongos --fork --port 30001 --logpath /usr/tmp/route1/log.log --chunkSize 1 --configdb 218.30.117.193:20001,218.30.117.195:20002,218.30.117.196:20003
  3. # /usr/local/mongodb/bin/mongos --fork --port 30002 --logpath /usr/tmp/route2/log.log --chunkSize 1 --configdb 218.30.117.193:20001,218.30.117.195:20002,218.30.117.196:20003
  4. # /usr/local/mongodb/bin/mongos --fork --port 30003 --logpath /usr/tmp/route3/log.log --chunkSize 1 --configdb 218.30.117.193:20001,218.30.117.195:20002,218.30.117.196:20003

4、在mongod上配置副本集

先连接到某个副本集的成员上,然后初始化副本集配置:

连接到218.30.117.193:10001上初始化副本集shard1:

  1. # /usr/local/mongodb/bin/mongo 218.30.117.193:10001/admin
  2. MongoDB shell version: 2.4.2
  3. connecting to: 218.30.117.193:10001/admin
  4. > config={_id:'shard1', members:[{_id:0, host:'218.30.117.193:10001'}, {_id:1, host:'218.30.117.195:10001'}, {_id:2, host:'218.30.117.196:10001'}]}
  5. > rs.initiate(config)

连接到218.30.117.195:10002上初始化副本集shard2:

  1. # /usr/local/mongodb/bin/mongo 218.30.117.195:10002/admin
  2. MongoDB shell version: 2.4.2
  3. connecting to: 218.30.117.195:10002/admin
  4. > config={_id:'shard2', members:[{_id:0, host:'218.30.117.193:10002'}, {_id:1, host:'218.30.117.195:10002'}, {_id:2, host:'218.30.117.196:10002'}]}
  5. > rs.initiate(config)

连接到218.30.117.196:10003上初始化副本集shard3:

  1. # /usr/local/mongodb/bin/mongo 218.30.117.196:10003/admin
  2. MongoDB shell version: 2.4.2
  3. connecting to: 218.30.117.196:10003/admin
  4. > config={_id:'shard3', members:[{_id:0, host:'218.30.117.193:10003'}, {_id:1, host:'218.30.117.195:10003'}, {_id:2, host:'218.30.117.196:10003'}]}
  5. > rs.initiate(config)

可以通过rs.status()命令查看副本集配置结果:

  1. > rs.status()
  2. {
  3. "set" : "shard3",
  4. "date" : ISODate("2013-05-14T17:44:05Z"),
  5. "myState" : 1,
  6. "members" : [
  7. {
  8. "_id" : 0,
  9. "name" : "218.30.117.193:10003",
  10. "health" : 1,
  11. "state" : 2,
  12. "stateStr" : "SECONDARY",
  13. "uptime" : 147,
  14. "optime" : {
  15. "t" : 1368553298,
  16. "i" : 1
  17. },
  18. "optimeDate" : ISODate("2013-05-14T17:41:38Z"),
  19. "lastHeartbeat" : ISODate("2013-05-14T17:44:03Z"),
  20. "lastHeartbeatRecv" : ISODate("2013-05-14T17:44:05Z"),
  21. "pingMs" : 0,
  22. "syncingTo" : "218.30.117.196:10003"
  23. },
  24. {
  25. "_id" : 1,
  26. "name" : "218.30.117.195:10003",
  27. "health" : 1,
  28. "state" : 2,
  29. "stateStr" : "SECONDARY",
  30. "uptime" : 147,
  31. "optime" : {
  32. "t" : 1368553298,
  33. "i" : 1
  34. },
  35. "optimeDate" : ISODate("2013-05-14T17:41:38Z"),
  36. "lastHeartbeat" : ISODate("2013-05-14T17:44:03Z"),
  37. "lastHeartbeatRecv" : ISODate("2013-05-14T17:44:03Z"),
  38. "pingMs" : 1,
  39. "syncingTo" : "218.30.117.196:10003"
  40. },
  41. {
  42. "_id" : 2,
  43. "name" : "218.30.117.196:10003",
  44. "health" : 1,
  45. "state" : 1,
  46. "stateStr" : "PRIMARY",
  47. "uptime" : 1109,
  48. "optime" : {
  49. "t" : 1368553298,
  50. "i" : 1
  51. },
  52. "optimeDate" : ISODate("2013-05-14T17:41:38Z"),
  53. "self" : true
  54. }
  55. ],
  56. "ok" : 1
  57. }

5、向集群添加副本集分片

先通过mongo shell连接到mongos实例:

  1. # /usr/local/mongodb/bin/mongo 218.30.117.197:30001/admin
  2. MongoDB shell version: 2.4.2
  3. connecting to: 218.30.117.197:30001/admin
  4. mongos> db.runCommand({addshard:"shard1/218.30.117.193:10001", name:"ShardSet1"})
  5. { "shardAdded" : "ShardSet1", "ok" : 1 }
  6. mongos> db.runCommand({addshard:"shard2/218.30.117.195:10002", name:"ShardSet2"})
  7. { "shardAdded" : "ShardSet2", "ok" : 1 }
  8. mongos> db.runCommand({addshard:"shard3/218.30.117.195:10003", name:"ShardSet3"})
  9. { "shardAdded" : "ShardSet3", "ok" : 1 }

通过db.runCommand({listshards: 1})查看分片集群的信息:

  1. mongos> db.runCommand({listshards: 1})
  2. {
  3. "shards" : [
  4. {
  5. "_id" : "ShardSet1",
  6. "host" : "shard1/218.30.117.193:10001,218.30.117.195:10001,218.30.117.196:10001"
  7. },
  8. {
  9. "_id" : "ShardSet2",
  10. "host" : "shard2/218.30.117.193:10002,218.30.117.195:10002,218.30.117.196:10002"
  11. },
  12. {
  13. "_id" : "ShardSet3",
  14. "host" : "shard3/218.30.117.193:10003,218.30.117.195:10003,218.30.117.196:10003"
  15. }
  16. ],
  17. "ok" : 1
  18. }

6、开启数据库的分片功能

我们的数据库名为page_db,则命令如下:

  1. mongos> sh.enableSharding("page_db")
  2. { "ok" : 1 }

7、开启数据库中users集合的分片功能,并指定_id的散列值组作为片键

  1. mongos> sh.shardCollection("page_db.users", {_id : "hashed"})
  2. { "collectionsharded" : "page_db.users", "ok" : 1 }

总结:

分片功能在少数据量时没有必要,只是增加了架构的复杂性。但如果数据量大,访问量大的情况下,分片还是很有必要的。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/羊村懒王/article/detail/599467
推荐阅读
相关标签
  

闽ICP备14008679号