use config
db.settings.save( { _id:"chunksize", value: 100 } )
分片
Chunk
条目 | 说明 |
---|---|
修改 Chunk 默认大小 |
Chunk 默认大小为 64 MB,允许 Chunk 大小的范围是 1MB - 1024MB。 连接到 mongos,修改 Chunk 大小为 100 MB |
配置集合分片 |
执行如下命令,如果集合不为空,需要基于片键创建索引。
通过 admin command 也可以执行集合分片
|
jumbo chunk |
当片键是一个不变的值时,chunk 是不会分裂的,运行 jumbo.js,它会查人 1秒条文档,约 1GB,其中 accountNo 为一不变的值,运行如下命令会产生一个 jumbo chunk
|
Chunk 分裂 |
当 Chunk 的大小大于 Chunk Size 或 大于一个 Chunk 允许的最大文档数目时,Chunk 就会分裂。 执行 splits.js,它会查人 200k 条文档,约 200 MB,其中 accountNo 为片键,单调递增,构建输出提示查看 chunk 分裂情况:
|
手动 Chunk 分裂 |
本部分手动进行 Chunk 分裂 创建分片集合:
停止 Balancer 的运行
执行 splitsManually.js,进行手动 Chunk 分裂
查看 Chunk 分布情况
执行 splitsManuallyLoad.js 导入数据
启用 Balancer
查看最终数据的分布
|
Chunk 常见 Troubleshooting 步骤
mongodump --host HOST:PORT -d config -o configdump
use config
db.databases.find({},{primary: 1, partitioned: 1})
{ "_id" : "testSharding", "primary" : "repl-b", "partitioned" : true }
{ "_id" : "test", "primary" : "repl-c", "partitioned" : true }
// 各分片 chunk 总数
use config
db.chunks.aggregate([{ $group: { _id: { shard: "$shard" }, count: { $sum: 1 } } }, { $sort : { "_id.shard" : 1 } } ])
{ "_id" : { "shard" : "repl-a" }, "count" : 26 }
{ "_id" : { "shard" : "repl-b" }, "count" : 25 }
{ "_id" : { "shard" : "repl-c" }, "count" : 26 }
// 某一个数据库 chunk 在各分片的分布
db.chunks.aggregate([{$match: {"ns" : "testSharding.people"}}, { $group: { _id: { shard: "$shard" }, count: { $sum: 1 } } }, { $sort : { "_id.shard" : 1 } } ])
{ "_id" : { "shard" : "repl-a" }, "count" : 25 }
{ "_id" : { "shard" : "repl-b" }, "count" : 25 }
{ "_id" : { "shard" : "repl-c" }, "count" : 25 }
db.collections.count({dropped: false})
3
db.collections.count({key: {x: 1}, dropped: false})
2
手动迁移 chunk
chunk 的移动有两种方式:Balancer 均衡器,手动,本部分说明如何手动迁移 chunk。
TODO
将新数据引导到新的分片上
通过分片集标签可以在新集合上将数据引导到新分片上。由于每天一个集合,数量较大,使用脚本完成分配工作。
// disable balancing for all namespaces
use config
db.collections.find({dropped: false}).forEach(function(coll) {
sh.disableBalancing(coll._id);
print(coll._id + " balancing disabled");
});
sh.addTagRange("test.people", {x: MinKey}, {x: MaxKey}, "new_shards")
sh.moveChunk("test.people", {x: 1}, "repl-b")
sh.addShardTag("repl-b", "new_shards");
//re-enable balancer for all namespaces
db.collections.find({dropped: false}).forEach(function(coll) {
sh.enableBalancing(coll._id);
print(coll._id + " balancing enabled");
});
导入 100 万条数据
java -jar target/bulkLoad-jar-with-dependencies.jar -u "mongodb://localhost:27017" -d test -c people -f people.json -s -n 1000000 -k x --start 0 -m
查看chunk 分布
db.chunks.aggregate([{$match: {"ns" : "test.people"}}, { $group: { _id: { shard: "$shard" }, count: { $sum: 1 } } }, { $sort : { "_id.shard" : 1 } } ])
{ "_id" : { "shard" : "repl-a" }, "count" : 1 }
{ "_id" : { "shard" : "repl-b" }, "count" : 14 }
片键选择
片键选择三原则
Shard Key 决定如何将一个集合中的文档分发到不同的分片,Shard Key 需要是一个索引的字段或索引的复合字段。将一个集合进行分片语法如下:
sh.shardCollection( <database>.<collection>, <key> )
Note
|
如果集合不为空,那么进行分区操作前,首先必须对 Shard Key 对应的字段创建索引,如果集合为空,那么进行分区操作时会自动创建对 Shard Key 对应的字段创建索引。 |
到目前可以看出,Shard Key 在集合分区中起到决定性的作用,如何确保集合分区合理高效,就必须选择最合适的 Shard Key。那么如何选择 Shard Key?选择 Shard Key 有三个指标:
-
合理的基数(Cardinality) - 基数不能太小,如果太小,例如 Shard Key 就有三个可能的值,那么如果有超过 3 个分区,则是无意义的,基数太大也不行,虽然可以保证水平扩展,但无法高效的通过 chunk 分发
-
合理的频率(Frequency) - 如果数据集中在某一个 Shard Key 对应的值上,那么数据无法很好的分发
-
合理的变化(Monotonically) - 如果 Shard Key 对应的值在不停的增加或减小,那么靠近 maxKey 或 minKey 的 分区数据变得越来越多,不利于集群高效运行.
单调递增/递减片键易造成数据分配不均
单调递增或递减的片键(以下简称单调片键)因为新值总是位于整个值域的上限(递增)或下限(递减)处,造成无论哪个片拥有上限或下限处的数据块,新文档始终会被分配到那个分片上。一方面这样会造成该分片成为写入热点,无法分散写入压力。另外一方面持续对该片的写入会使数据分布极不均匀,需要依赖均衡器在后期将数据移动到其他分片达到均衡的目的。从另一个角度讲,如果数据一开始就能够分散写入到不同的分片上,则不需要通过重新搬迁来均衡数据,即省去了均衡对资源带来的额外消耗。因此,单调片键无论对性能或水平扩展都会造成不利影响,选择片键时应当避免。
常见的单调片键包括:
-
数字序列
-
时间
-
ObjectId
-
UUID
读写负载影响片键选择
选择分片键时,需要考虑最主要的因素是什么?明白集群的读写负载。
如果工作负载主要是写入,则不能使用单调增加/减少的片键并分发写入是至关重要的。
如果工作负载主要是读取,则需要识别最常见的查询,并确保这些查询得到分发和本地化。不使用片键的查询将被发送到所有分片。那些非目标查询不能很好地扩展,这意味着添加新的分片没有帮助,所以我们希望最小化这些。
TODO