共计 7361 个字符,预计需要花费 19 分钟才能阅读完成。
在 MongoDB(版本 3.2.9)中,分片集群(sharded cluster)是一种水平扩展数据库系统性能的方法,能够将数据集分布式存储在不同的分片(shard)上,每个分片只保存数据集的一部分,MongoDB 保证各个分片之间不会有重复的数据,所有分片保存的数据之和就是完整的数据集。分片集群将数据集分布式存储,能够将负载分摊到多个分片上,每个分片只负责读写一部分数据,充分利用了各个 shard 的系统资源,提高数据库系统的吞吐量。
数据集被拆分成数据块(chunk),每个数据块包含多个 doc,数据块分布式存储在分片集群中。MongoDB 负责追踪数据块在 shard 上的分布信息,每个分片存储哪些数据块,叫做分片的元数据,保存在 config server 上的数据库 config 中,一般使用 3 台 config server,所有 config server 中的 config 数据库必须完全相同。通过 mongos 能够直接访问数据库 config,查看分片的元数据;mongo shell 提供 sh 辅助函数,能够安全地查看分片集群的元数据信息。
对任何一个 shard 进行查询,只会获取 collection 在当前分片上的数据子集,不是整个数据集。Application 只需要连接到 mongos,对其进行的读写操作,mongos 自动将读写请求路由到相应的 shard。MongoDB 通过 mongos 将分片的底层实现对 Application 透明,在 Application 看来,访问的是整个数据集。
一,主分片
在分片集群中,不是每个集合都会分布式存储,只有使用 sh.shardCollection()显式将 collection 分片后,该集合才会分布式存储在不同的 shard 中。对于非分片集合(un-sharded collection),其数据只会存储在主分片(Primary shard)中,默认情况下,主分片是指数据库最初创建的 shard,用于存储该数据库中非分片集合的数据。每个数据库都有一个主分片。
Each database in a sharded cluster has a primary shard that holds all the un-sharded collections for that database. Each database has its own primary shard.
例如,一个分片集群有三个分片:shard1,shard2,shard3,在分片 shard1 创建一个数据库 blog。如果将数据库 bolg 分片,那么 MongoDB 会自动在 shard2,shard3 上创建一个结构相同的数据库 blog,数据库 blog 的 Primary Shard 是 Shard1。
图示,Collection2 的主分片是 ShardA。
使用 movePrimary 命令变更数据库默认的 Primary shard,非分片集合将会从当前 shard 移动到新的主分片。
db.runCommand({ movePrimary : "test", to : "shard0001"} )
在使用 movePrimary 命令变更数据库的主分片之后,config server 中的配置信息是最新的,mongos 缓存的配置信息变得过时了。MongoDB 提供命令:flushRouterConfig 强制 mongos 从 config server 获取最新的配置信息,刷新 mongos 的缓存。
db.adminCommand({"flushRouterConfig":1})
二,分片的元数据
不要直接到 config server 上查看分片集群的元数据信息,这些数据非常重要,安全的方式是通过 mongos 连接到 config 数据查看,或者使用 sh 辅助函数查看。
使用 sh 辅助函数查看
sh.status()
连接到 mongos 查看 config 数据库中的集合
mongos> use config
1,shards 集合保存分片信息
db.shards.find()
shard 的数据存储在 host 指定的 replica set 或 standalone mongod 中。
{"_id" : "shard_name",
"host" : "replica_set_name/host:port",
"tag":[shard_tag1,shard_tag2]
}
2,databases 集合保存分片集群中所有数据库的信息,不管数据库是否分片
db.databases.find()
如果在数据库上执行 sh.enableSharding(“db_name”),那么字段 partitioned 字段值就是 true;primary 字段指定数据库的主分片(primary shard)。
{"_id" : "test",
"primary" : "rs0",
"partitioned" : true
}
3,collections 集合保存所有已分片集合的信息,不包括非分片集合(un-sharded collections)
key 是:分片的片键
db.collections.find()
{"_id" : "test.foo",
"lastmodEpoch" : ObjectId("57dcd4899bd7f7111ec15f16"),
"lastmod" : ISODate("1970-02-19T17:02:47.296Z"),
"dropped" : false,
"key" : {"_id" : 1
},
"unique" : true
}
4,chunks 集合保存数据块信息,
ns:分片的集合,结构是:db_name.collection_name
min 和 max:片键的最小值和最大值
shard:块所在的分片
db.chunks.find()
{"_id" : "test.foo-_id_MinKey",
"lastmod" : Timestamp(1, 1),
"lastmodEpoch" : ObjectId("57dcd4899bd7f7111ec15f16"),
"ns" : "test.foo",
"min" : {"_id" : 1
},
"max" : {"_id" : 3087
},
"shard" : "rs0"
}
5,changelog 集合记录分片集群的操作,包括 chunk 的拆分和迁移操作,Shard 的增加或删除操作
what 字段:表示操作的类型,例如:multi-split 表示 chunk 的拆分,
"what" : "addShard",
"what" : "shardCollection.start",
"what" : "shardCollection.end",
"what" : "multi-split",
6,tags 记录 shard 的 tag 和对应的片键范围
{"_id" : {"ns" : "records.users", "min" : { "zipcode" : "10001" } },
"ns" : "records.users",
"min" : {"zipcode" : "10001" },
"max" : {"zipcode" : "10281" },
"tag" : "NYC"
}
7,settings 集合记录均衡器状态和 chunk 的大小,默认的 chunk size 是 64MB。
{"_id" : "chunksize", "value" : 64 }
{"_id" : "balancer", "stopped" : false }
8,locks 集合记录分布锁(distributed lock),保证只有一个 mongos 实例能够在分片集群中执行管理任务。
mongos 在担任 balancer 时,会获取一个分布锁,并向 config.locks 中插入一条 doc。
The locks collection stores a distributed lock. This ensures that only one mongos instance can perform administrative tasks on the cluster at once. The mongos acting as balancer takes a lock by inserting a document resembling the following into the locks collection.
{"_id" : "balancer",
"process" : "example.net:40000:1350402818:16807",
"state" : 2,
"ts" : ObjectId("507daeedf40e1879df62e5f3"),
"when" : ISODate("2012-10-16T19:01:01.593Z"),
"who" : "example.net:40000:1350402818:16807:Balancer:282475249",
"why" : "doing balance round"
}
三,删除分片
删除分片时,必须确保该分片上的数据被移动到其他分片中,对于以分片的集合,使用均衡器来迁移数据块,对于非分片的集合,必须修改集合的主分片。
1,删除已分片的集合数据
step1,保证均衡器是开启的
sh.setBalancerState(true)
;
step2,将已分片的集合全部迁移到其他分片
use admin
db.adminCommand({"removeShard":"shard_name"})
removeShard 命令会将数据块从当前分片上迁移到其他分片上去,如果分片上的数据块比较多,迁移过程可能耗时很长。
step3,检查数据块迁移的状态
use admin
db.runCommand({ removeShard: "shard_name" } )
使用 removeShard 命令能够查看数据块迁移的状态,remaining 字段表示剩余数据块的数量
{"msg" : "draining ongoing",
"state" : "ongoing",
"remaining" : {"chunks" : 42,
"dbs" : 1
},
"ok" : 1
}
step4,数据块完成迁移
use admin
db.runCommand({ removeShard: "shard_name" } )
{"msg" : "removeshard completed successfully",
"state" : "completed",
"shard" : "shard_name",
"ok" : 1
}
2,删除未分片的数据库
step1,查看未分片的数据库
未分片的数据库,包括两部分:
- 数据库未被分片,该数据没有使用 sh.enableSharding(“db_name”),在数据库 config 中,该数据库的 partitioned 字段是 false
- 数据库中存在 collection 未被分片,即当前的分片是该集合的主分片
use config
db.databases.find({$or:[{"partitioned":false},{"primary":"shard_name"}]})
对于 partitioned=false 的数据库,其数据全部保存在当前 shard 中;对于 partitioned=true,primary=”shard_name“的数据库,表示存在未分片(un-sharded collection)存储在该数据库中,必须变更这些集合的主分片。
step2,修改数据库的主分片
db.runCommand({ movePrimary: "db_name", to: "new_shard"})
四,增加分片
由于分片存储的是数据集的一部分,为了保证数据的高可用性,推荐使用 Replica Set 作为 shard,即使 Replica Set 中只包含一个成员。连接到 mongos,使用 sh 辅助函数增加分片。
sh.addShard("replica_set_name/host:port")
不推荐将 standalone mongod 作为 shard
sh.addShard("host:port")
五,特大块
在有些情况下,chunk 会持续增长,超出 chunk size 的限制,成为特大块(jumbo chunk),出现特大块的原因是 chunk 中的所有 doc 使用同一个片键(shard key),导致 MongoDB 无法拆分该 chunk,如果该 chunk 持续增长,将会导致 chunk 的分布不均匀,成为性能瓶颈。
在 chunk 迁移时,存在限制:每个 chunk 的大小不能超过 2.5 万条 doc,或者 1.3 倍于配置值。chunk size 默认的配置值是 64MB,超过限制的 chunk 会被 MongoDB 标记为特大块(jumbo chunk),MongoDB 不能将特大块迁移到其他 shard 上。
MongoDB cannot move a chunk if the number of documents in the chunk exceeds either 250000 documents or 1.3 times the result of dividing the configured chunk size by the average document size.
1,查看特大块
使用sh.status()
,能够发现特大块,特大块的后面存在 jumbo 标志
{"x" : 2} -->> {"x" : 3} on : shard-a Timestamp(2, 2) jumbo
2,分发特大块
特大块不能拆分,不能通过均衡器自动分发,必须手动分发。
step1,关闭均衡器
sh.setBalancerState(false)
step2,增大 Chunk Size 的配置值
由于 MongoDB 不允许移动大小超出限制的特大块,因此,必须临时增加 chunk size 的配置值,再将特大块均衡地分发到分片集群中。
use config
db.settings.save({"_id":"chunksize","value":"1024"})
step3,移动特大块
sh.moveChunk("db_name.collection_name",{sharded_filed:"value_in_chunk"},"new_shard_name")
step4,启用均衡器
sh.setBalancerState(true)
step5,刷新 mongos 的配置缓存
强制 mongos 从 config server 同步配置信息,并刷新缓存。
use admin
db.adminCommand({flushRouterConfig: 1} )
六,均衡器
均衡器是由 mongos 转变的,就是说,mongos 不仅负责将查询路由到相应的 shard 上,还要负责数据块的均衡。一般情况下,MongoDB 会自动处理数据均衡,通过 config.settings 能够查看 balancer 的状态,或通过 sh 辅助函数查看
sh.getBalancerState()
返回 true,表示均衡器在正运行,系统自动处理数据均衡,使用 sh 辅助函数能够关闭 balancer
sh.setBalancerState(false)
balancer 不能立即终止正在运行的块迁移操作,在 mongos 转变为 balancer 时,会申请一个 balancer lock,查看 config.locks 集合,
use config
db.locks.find({"_id":"balancer"})
--or
sh.isBalancerRunning()
如果 state=2,表示 balancer 正处于活跃状态,如果 state=0,表示 balancer 已被关闭。
均衡过程实际上是将数据块从一个 shard 迁移到其他 shard,或者先将一个大的 chunk 拆分小的 chunk,再将小块迁移到其他 shard 上,块的迁移和拆分都会增加系统的 IO 负载,最好将均衡器的活跃时间限制在系统空闲时进行,可以设置 balancer 的活跃时间窗口,限制 balancer 在指定的时间区间内进行数据块的拆分和迁移操作。
use config
db.settings.update(
{"_id":"balancer"},
"$set":{"activeWindow":{"start":"23:00","stop":"04:00"}}),
true
)
均衡器拆分和移动的对象是 chunk,均衡器只保证 chunk 数量在各个 shard 上是均衡的,至于每个 chunk 包含的 doc 数量,并不一定是均衡的。可能存在一些 chunk 包含的 doc 数量很多,而有些 chunk 包含的 doc 数量很少,甚至不包含任何 doc。因此,应该慎重选择分片的索引键,即片键,如果一个字段既能满足绝大多数查询的需求,又能使 doc 数量均匀分布,那么该字段是片键的最佳选择。
更多 MongoDB 相关教程见以下内容:
CentOS 编译安装 MongoDB 与 mongoDB 的 php 扩展 http://www.linuxidc.com/Linux/2012-02/53833.htm
CentOS 6 使用 yum 安装 MongoDB 及服务器端配置 http://www.linuxidc.com/Linux/2012-08/68196.htm
Ubuntu 13.04 下安装 MongoDB2.4.3 http://www.linuxidc.com/Linux/2013-05/84227.htm
MongoDB 入门必读(概念与实战并重) http://www.linuxidc.com/Linux/2013-07/87105.htm
Ubunu 14.04 下 MongoDB 的安装指南 http://www.linuxidc.com/Linux/2014-08/105364.htm
《MongoDB 权威指南》(MongoDB: The Definitive Guide)英文文字版[PDF] http://www.linuxidc.com/Linux/2012-07/66735.htm
Nagios 监控 MongoDB 分片集群服务实战 http://www.linuxidc.com/Linux/2014-10/107826.htm
基于 CentOS 6.5 操作系统搭建 MongoDB 服务 http://www.linuxidc.com/Linux/2014-11/108900.htm
MongoDB 的详细介绍:请点这里
MongoDB 的下载地址:请点这里
本文永久更新链接地址:http://www.linuxidc.com/Linux/2016-09/135274.htm