设为首页 收藏本站
查看: 1043|回复: 0

[经验分享] MongoDB中的分片

[复制链接]

尚未签到

发表于 2018-10-26 12:17:52 | 显示全部楼层 |阅读模式
  通过分片能够增加更多的机器来应对不断增加的负载和数据,还不影响应用。分片(sharding)是指将数据拆分,将其分散到不同的机器上,不需要功能强大的大型计算机就可以存储更多的数据,处理更大的负载。
  MongoDB支持自动分片,可以摆脱手动分片的管理困扰,集群自动切分数据,做负载均衡。分片的基本思想就是将集合切分成小块,这些块分散到若干片里面,每个片只负责总数据的一部分。应用程序不必知道哪片对应哪些数据,甚至不知道数据已经拆分,所以在分片之前要运行一个路由进程(mongos),这个路由知道所有数据的存放位置,所以应用可以连接它来正常发送请求。对应用来说,它仅知道连接了一个普通的mongod。路由器知道数据和片的对应关系,能够转发请求到正确的片上。如果请求有了回应,路由器将其收集起来会送给应用。
  在没有分片的时候,客户端连接mongod进程;分片时,客户端连接mongos进程,mongos对应用隐藏了分片的细节。从应用角度看,分片不分片没什么差别,所以需要扩展的时候,不必修改应用程序的代码。
  何时分片:
  (1)机器的磁盘不够用了
  (2)单个mongod已经不能满足写数据的性能需求
  (3)想将大量数据放在内存中提高性能
  一般来说,先要从不分片开始,在需要时将其转换成分片的。
  1、片键
  设置分片时,需要从集合里选一个键,作为数据拆分的依据,这个键称为片键(shard key)。
  例如,文档集合表示的是人员,如果选择名字(name)为片键,第一片可能会存放以A-F开头的文档,第二片存G-P的名字,第三片存Q-Z的名字。随着添加或者删除片,MongoDB会重新平衡数据,使每片的流量都比较均衡,数据量也在合理范围内(例如,流量大的片存放的数据或许会比流量小的书数据量要要少些)。
  1.1、将已有的集合分片
  假设有个存储日志的集合,现在要分片。我们开启分片功能,然后告诉MongoDB用"timestamp"作为片键,就是所有数据放到了一个片上。可以随意插入数据,但总会是在一个片上。然后,新增一个片。这个片建好并运行了以后,MongoDB就会把集合拆分成两半,成为块。每个块中包含片键值在一定范围内的所有文档,假设其中一块包含时间戳在2015.11.11前的文档,则另一块含有2015.11.11以后的文档。其中一块会被移动到新片上。如果新文档的时间戳在2015.11.11之前,则添加到第一块,否则添加到第二块。
  1.2、递增片键还是随机片键
  片键的选择决定了插入操作在片之间的分布。如果选择了像"timestamp"这样的键,这个值可能不断增长,而且没有太大的间断,就会将所有数据发送到一个片上(含有2015.11.11以后日期的那片)。如果又添加了新片,再拆分数据,还是会都导入到一台服务器上。添加了新片,MongoDB肯能会将2011.11.11以后的拆分成2011.11.11-2021.11.11。如果文档的时间大于2021.11.11以后,所有的文档还会以最后一片插入。这就不适合写入负载很高情况,但按照片键查询会非常高效。如果写入负载比较高,想均匀分散负载到各个片,就得选择分布均匀的片键。日志例子中时间戳的散列值,没有模式的"logMessage"都是复合这个条件的。
  不论片键随机跳跃还是稳定增加,片键的变化很重要。如,如果有个"logLevel"键的值只有3种值"DEBUG","WARN","ERROR",MongoDB无论如何也不能把它作为片键将数据分成多于3块(因为只有3个值)。如果键的变化太少,但又想让其作为片键,可以把这个键与一个变化较大的键组合起来,创建一个复合片键,如"logLevel"和"timestamp"组合。
  选择片键并创建片键很像索引,因为二者原理相似。事实上,片键也是最常用的索引。
  1.3、片键对操作的影响
  最终用户应该无法区分是否分片,但是要了解选择不同片键情况下的查询有何不同。
  假设还是那个表示人员的集合,按照"name"分片,有3个片,其名字首字母的范围是A-Z.下面以不同的方式查询:
  db.people.find({"name":"Refactor"})
  mongos会将这个查询直接发送给Q-Z片,获得响应后,直接转发给客户端
  db.people.find({"name":{"$lt":"L"}})
  mongos会将其先发送给A-F和G-P片,然后将结果转发给客户端.
  db.people.find().sort({"email":1})
  mongos会在所有片上查询,返回结果时还会做归并排序,确保结果顺序正确.
  mongos用游标从各个服务器上获取数据,所以不必等到全部数据都拿到才向客户端发送批量结果.
  db.people.find({"email":"refactor@msn.cn"})
  mongos并不追踪"email"键,所以也不知道应该将查询发给那个片.所以他就向所有片顺序发送查询.
  如果是插入文档,mongos会依据"name"键的值,将其发送到相应的片上.
  2、建立分片
  建立分片分两步:启动实际的服务器和切分数据。
  分片一般会有3个组成部分:
  (1)片
  片就是保存子集合数据的容器,片可是单个的mongod服务器(开发和测试用),也可以是副本集(生产用)。所以一片有多台服务器,也只能有一个主服务器,其他的服务器保存相同的数据。
  (2)mongos
  mongos就是MongoDB配的路由器进程。它路由所有的请求,然后将结果聚合。它本身并不存储数据或者配置信息,但会缓存配置服务器的信息。
  (3)配置服务器
  配置服务器存储了集群的配置信息:数据和片的对应关系。mongos不永久存放数据,所以需要个地方存放分片的配置。它会从配置服务器获取同步数据。
  如果已经能用了MongoDB,就有了一个整装待发的片了(当前的mongod可以变成第一个片)。
  2.1、启动服务器
  2.1.1、启动配置服务器
  配置服务器需要最先启动,因为mongos会用到其上的配置信息。配置服务器的启动就像普通mongod一样。
  [root@gflinux102 master]# mkdir -p /opt/mongo/config
  [root@gflinux102 ~]# mongod --dbpath /opt/mongo/config/ --port 20000
  配置服务器不需要很多空间和资源(200MB实际数据大约占用1KB的配置空间)
  2.1.2建立mongos进程(应用程序连接)
  这种路由服务器不需要数据目录,但是一定要指明配置服务器的位置。
  [root@gflinux102 ~]# mongos --port 30000 --configdb localhost:20000
  分片管理是通过mongos来完成的。
  2.1.3、添加片
  片就是普通的mongod实例(或者副本集):
  [root@gflinux102 master]# mkdir -p /opt/mongo/shard1
  [root@gflinux102 ~]# mongod --dbpath /opt/mongo/shard1/ --port 10000
  连接mongos,为集群添加一个片。启动shell,连接mongos:
  [root@gflinux102 ~]# mongo localhost:30000/admin
  MongoDB shell version: 2.6.6
  connecting to: localhost:30000/admin
  mongos>
  通过addshard命令添加片:
  mongos> db.runCommand({addshard:"localhost:10000",allowLocal:true})
  { "shardAdded" : "shard0000", "ok" : 1 }
  mongos>
  再增加一个片:
  [root@gflinux102 master]# mkdir -p /opt/mongo/shard2
  [root@gflinux102 ~]# mongod --dbpath /opt/mongo/shard2 --port 10001
  mongos> db.runCommand({addshard:"localhost:10001",allowLocal:true})
  { "shardAdded" : "shard0001", "ok" : 1 }
  当在localhost运行片时,得设定"allowLocal"键。MongoDB尽量避免由于错误配置,将集群配置到本地。
  想添加片的时候,就运行addshard,MongoDB会负责将片集成到集群。
  2.2、切分数据
  MongoDB不会将存储的每一条数据都直接发布,得先在数据库和集合的级别将分片功能打开。下面以"_id"为基准切分test数据库的rgf集合。
  2.2.1、开启数据库的分片功能
  分片功能的开启是在哪个服务器上哪?
  首先在配置服务器上运行:
  [root@gflinux102 master]# mongo localhost:20000
  MongoDB shell version: 2.6.6
  connecting to: localhost:20000/test
  Server has startup warnings:
  > use admin
  switched to db admin
  > db.runCommand({"enablesharding":"test"})
  {
  "ok" : 0,
  "errmsg" : "no such cmd: enablesharding",
  "code" : 59,
  "bad cmd" : {
  "enablesharding" : "test"
  }
  }
  >
  配置服务器没有该命令。
  (1)在路由服务器上对数据库启用分片功能("enablesharding")
  在路由服务器上运行:
  mongos> db.runCommand({"enablesharding":"test"})
  { "ok" : 1 }
  mongos>
  将test数据库启用分片功能,对数据库分片后,其内部的集合便会存储到不同的片上,同时这也是对这些集合分片的前置条件。
  (2)堆积和进行分片("shardcollection")
  在数据库级别启用了分片以后,就可以使用shardcollection命令堆积和进行分片:
  mongos> db.runCommand({"shardcollection":"test.rgf","key":{"name":1}})
  {
  "proposedKey" : {
  "name" : 1
  },
  "curIndexes" : [
  {
  "v" : 1,
  "key" : {
  "_id" : 1
  },
  "name" : "_id_",
  "ns" : "test.rgf"
  }
  ],
  "ok" : 0,
  "errmsg" : "please create an index that starts with the shard key before sharding."
  }
  出现这种情况,需要在片上的集合中创建文档:
  > db.rgf.ensureIndex({"name":1})
  {
  "createdCollectionAutomatically" : false,
  "numIndexesBefore" : 1,
  "numIndexesAfter" : 2,
  "ok" : 1
  }
  再次运行分片命令:
  mongos> db.runCommand({"shardcollection":"test.rgf","key":{"name":1}})
  { "collectionsharded" : "test.rgf", "ok" : 1 }
  对test数据库的rgf集合进行分片,片键name,如果对rgf集合添加数据,就会依据"name"的值自动分散到各个片上。
  注意:插入数据在mongos端插入。
  3、生产配置
  成功地构建分片需要如下条件:
  (1)多个配置服务器
  (2)多个mongos服务器
  (3)每个片都是副本集
  (4)正确设置w
  3.1、配置多个配置服务器
  建立多个配置服务器非常简单:
  [root@gflinux102 master]# mkdir -p /opt/mongo/config{1,2,3}
  [root@gflinux102 ~]# mongod --dbpath /opt/mongo/config1/ --port 20001
  [root@gflinux102 ~]# mongod --dbpath /opt/mongo/config2/ --port 20002
  [root@gflinux102 ~]# mongod --dbpath /opt/mongo/config3/ --port 20003
  启动mongos时应将其连接到这3个配置服务器中:
  [root@gflinux102 ~]# mongos --configdb localhost:20001,localhost:20002,localhost:20003
  配置服务器使用的是两步提交机制,而不是普通MongoDB的异步复制来维护集群的不同副本,这样能保证集群状态的一致性。这也意味着,某台配置服务器宕掉了以后,集群配置信息将是只读的。客户端还是能够读写,但是只有所有配置服务器备份了以后才能均衡数据。
  3.2、多个mongos
  mongos的数量不受限制。建议针对一个应用只运行一个mongos进程,这样每个应用服务器就可以与mongos进行本地会话,如果服务器不工作了,就不会有应用试图与不存在的monges通话。
  3.3、健壮的片
  生成环境中,每个片都应是副本集。这样单个服务器坏了,就不会导致整个片失效。用addshard命令可以将副本集作为片添加,添加时只要指定副本集的名字和种子就好了。
  比如要添加副本集foo,其中包含一个服务器gf.xxx.com:27017(还有别的服务器),就可以使用下列命令将其添加到集群里:
  >db.runCommand({"addshard":"foo/gf.xxx.com:27017"})
  如果gf.xxx.com:27017挂了,mongos会知道它所连接的是一个副本集,并会使用新的主节点。
  3.4、物理服务器
  可以把配置服务器、一些mongos进程和副本集的一个节点放到一台机器上。
  4、管理分片
  分片信息主要存放在config数据库上,这样就能被任何连接到mongos的进程访问到了。
  4.1、配置集合
  4.1.1、片
  可以在config数据库中shards集合中查到所有的的片
  mongos> use config
  switched to db config
  mongos> db.shards.find()
  { "_id" : "shard0000", "host" : "localhost:10000" }
  { "_id" : "shard0001", "host" : "localhost:10001" }
  mongos>
  每个片都有唯一的_id。
  4.1.2、数据库
  databases集合还有已经在片上的数据库列表和一些相关信息。
  mongos> use config
  switched to db config
  mongos> db.databases.find()
  { "_id" : "admin", "partitioned" : false, "primary" : "config" }
  { "_id" : "test", "partitioned" : true, "primary" : "shard0000" }
  mongos>
  "_id"
  表示数据库名
  "partitioned"
  表示是否启用了分片功能
  "primary"
  这个值与"_id"相对应,表名这个数据的"大本营"在哪里.不论分片与否,数据库总会有个大本营.要是分片的话,创建数据库时会随机选择一个片.也就是说,大本营是开始创建数据库文档的位置.虽然分片时数据库也会用到很多别的服务器,但会从这个片开始。
  4.1.3、
  mongos> use config
  switched to db config
  mongos> db.chunks.find()
  { "_id" : "test.rgf-name_MinKey", "lastmod" : Timestamp(2, 0), "lastmodEpoch" : ObjectId("54dc4378f975c20703d7f0a1"), "ns" : "test.rgf", "min" : { "name" : { "$minKey" : 1 } }, "max" : { "name" : "agl" }, "shard" : "shard0001" }
  { "_id" : "test.rgf-name_\"agl\"", "lastmod" : Timestamp(2, 1), "lastmodEpoch" : ObjectId("54dc4378f975c20703d7f0a1"), "ns" : "test.rgf", "min" : { "name" : "agl" }, "max" : { "name" : { "$maxKey" : 1 } }, "shard" : "shard0000" }
  mongos>
  块信息保存在chunks集合中,可以看到数据到底是怎么切分到集群的。
  单块的集合范围:MinKey到MaxKey。
  4.2、分片命令
  4.2.1、获得概要
  mongos> use config;
  switched to db config
  mongos> printShardingStatus()
  --- Sharding Status ---
  sharding version: {
  "_id" : 1,
  "version" : 4,
  "minCompatibleVersion" : 4,
  "currentVersion" : 5,
  "clusterId" : ObjectId("54dc38c8f975c20703d7eed3")
  }
  shards:
  {  "_id" : "shard0000",  "host" : "localhost:10000" }
  {  "_id" : "shard0001",  "host" : "localhost:10001" }
  databases:
  {  "_id" : "admin",  "partitioned" : false,  "primary" : "config" }
  {  "_id" : "test",  "partitioned" : true,  "primary" : "shard0000" }
  test.rgf
  shard key: { "name" : 1 }
  chunks:
  shard00011
  shard00001
  { "name" : { "$minKey" : 1 } } -->> { "name" : "agl" } on : shard0001 Timestamp(2, 0)
  { "name" : "agl" } -->> { "name" : { "$maxKey" : 1 } } on : shard0000 Timestamp(2, 1)
  mongos>
  4.2.1、删除片
  用removeshard就能从集群中删除片。removeshard会把给定片上的所有块都挪到其他片上。
  mongos> db.runCommand({"removeshard":"localhost:10001"})
  {
  "ok" : 0,
  "errmsg" : "removeShard may only be run against the admin database.",
  "code" : 13
  }
  注意一定要切换到admin数据库
  mongos> use admin;
  switched to db admin
  mongos> db.runCommand({"removeshard":"localhost:10001"})
  {
  "msg" : "draining started successfully",
  "state" : "started",
  "shard" : "shard0001",
  "ok" : 1
  }
  mongos>
  如果删除的片时数据库的基片,必须手动移动数据库(使用moveprimary命令):
  mongos> db.runCommand({"moveprimary":"test","to":"localhost:10001"})
  { "primary " : "shard0001:localhost:10001", "ok" : 1 }
  mongos>


运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-626765-1-1.html 上篇帖子: MongoDB 4个使用技巧 下篇帖子: Mongodb Replica Set 读写分离
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表