分片标记(Shard tagging)是MongoDB 2.2.0版中的一项新功能。通过对集合进行标记使其被强制写入到本地数据中心,也可以用来将某个集合固定到一个分片或一系列分片中。
注意:尝试分片标记功能,必须使用2.2.0-rc0或更高版本。
使用此功能,首先需要启动一个分片群集:
> sharding = new ShardingTest({shards:3,chunksize:1})
这个命令将启动3个分片,一个配置服务器和一个mongos。并将所有的服务器日志输出到标准输出,所以我建议把这个shell先放在一边,然后使用另一个shell进行以下操作。
启动一个新的shell并连接到mongos(默认为端口30999),并创建一些分片的集合和数据来进行操作:
> // 记住,使用不同的shell
> conn = new Mongo("localhost:30999")
> db = conn.getDB("villains")
>
> // 启用分片
> sh.enableSharding("villains")
>
> // 将集合加入分片
> sh.shardCollection("villains.joker", {jokes:1});
> sh.shardCollection("villains.two-face", {luck:1});
> sh.shardCollection("villains.poison ivy", {flora:1});
>
> // 添加数据
> for (var i=0; i<100000; i++) { db.joker.insert({jokes: Math.random(), count: i, time: new Date()}); }
> for (var i=0; i<100000; i++) { db["two-face"].insert({luck: Math.random(), count: i, time: new Date()}); }
> for (var i=0; i<100000; i++) { db["poison ivy"].insert({flora: Math.random(), count: i, time: new Date()}); }
现在我们有三个分片和三个集合。如果你查看一下主要的数据块在哪里,你应该看到数据在分片之间相当均衡:
> use config
> db.chunks.find({ns:“villains.joker”},{shard:1,_id:0})。sort({shard:1})
{“shard”:“shard0000”}
{“shard”:“shard0000”}
{“shard”:“shard0000”}
{“shard”:“shard0001”}
{“shard”:“shard0001”}
{“shard”:“shard0001”}
{“shard”:“shard0002”}
{“shard”:“shard0002”}
{“shard”:“shard0002”}
> db.chunks.find({ns:“villains.two-face”},{shard:1,_id:0})。sort({shard:1})
{“shard”:“shard0000”}
{“shard”:“shard0000”}
{“shard”:“shard0000”}
{“shard”:“shard0001”}
{“shard”:“shard0001”}
{“shard”:“shard0001”}
{“shard”:“shard0002”}
{“shard”:“shard0002”}
{“shard”:“shard0002”}
> db.chunks.find({ns:“villains.poison ivy”},{shard:1,_id:0})。sort({shard:1})
{“shard”:“shard0000”}
{“shard”:“shard0000”}
{“shard”:“shard0001”}
{“shard”:“shard0001”}
{“shard”:“shard0002”}
{“shard”:“shard0002”}
然而,我们的每个villains可不想和其他人混到一起,所以我们要分开储存这些集合:每个villain一个分片。我们的目标如下:
分片 | 命名空间 |
---|---|
shard0000 | “villains.joker” |
shard0001 | “villains.two-face” |
shard0002 | “villains.poison ivy” |
要做到这一点,我们将使用标记。标记描述了一个分片的属性,可以是任何属性(标签非常灵活)。所以,你可以把一个分片标记为“快”,“慢”,“东海岸”,“机架空间”或者随便什么东西。
在这个例子中,我们要标记一个碎片属于某个villain,所以我们将villain的昵称作为标签。
> sh.addShardTag(“shard0000”,“mr.j”)
> sh.addShardTag(“shard0001”,“harv”)
> sh.addShardTag(“shard0002”,“ivy”)
这就是说,“把任何标记为 ‘mr. j’的数据块放在shard0000分片上。“
我们要做的第二件事就是制定一个规则:“对于在villains.joker系列中创建的所有数据块,给他们标记‘mr. j’。“要做到这一点,我们可以使用addTagRange:
> sh.addTagRange("villains.joker", {jokes:MinKey}, {jokes:MaxKey}, "mr. j")
> sh.addTagRange("villains.two-face", {luck:MinKey}, {luck:MaxKey}, "harv")
> sh.addTagRange("villains.poison ivy", {flora:MinKey}, {flora:MaxKey}, "ivy")
这条命令的作用是用 ‘mr. j’来标记villains.joker中的每一个数据块。(MinKey是负无穷大,MaxKey是正无穷大,因此所有的块落在这个范围内)。
现在让我们为其他两个集合做同样的事情:
> sh.addTagRange("villains.two-face", {luck:MinKey}, {luck:MaxKey}, "harv")
> sh.addTagRange("villains.poison ivy", {flora:MinKey}, {flora:MaxKey}, "ivy")
操作之后需要等几分钟(数据块的平衡需要一点时间),然后再来看这些集合当中的数据块。
> use config
> db.chunks.find({ns: "villains.joker"}, {shard:1, _id:0}).sort({shard:1})
{ "shard" : "shard0000" }
{ "shard" : "shard0000" }
{ "shard" : "shard0000" }
{ "shard" : "shard0000" }
{ "shard" : "shard0000" }
{ "shard" : "shard0000" }
{ "shard" : "shard0000" }
{ "shard" : "shard0000" }
{ "shard" : "shard0000" }
{ "shard" : "shard0000" }
> db.chunks.find({ns: "villains.two-face"}, {shard:1, _id:0}).sort({shard:1})
{ "shard" : "shard0001" }
{ "shard" : "shard0001" }
{ "shard" : "shard0001" }
{ "shard" : "shard0001" }
{ "shard" : "shard0001" }
{ "shard" : "shard0001" }
{ "shard" : "shard0001" }
{ "shard" : "shard0001" }
{ "shard" : "shard0001" }
{ "shard" : "shard0001" }
> db.chunks.find({ns: "villains.poison ivy"}, {shard:1, _id:0}).sort({shard:1})
{ "shard" : "shard0002" }
{ "shard" : "shard0002" }
{ "shard" : "shard0002" }
{ "shard" : "shard0002" }
{ "shard" : "shard0002" }
{ "shard" : "shard0002" }
{ "shard" : "shard0002" }
{ "shard" : "shard0002" }
假设Two-Face对这种安排并不满意,并立即要求两台服务器提供数据(即要求拥有两个分片)。我们可以通过操纵标签将Joker 和Poison Ivy的集合移动到同一个分片,并将Harvey的集合扩展到两个分片:
> // move Poison Ivy to shard0000
> sh.addShardTag("shard0000", "ivy")
> sh.removeShardTag("shard0002", "ivy")
>
> // expand Two-Face to shard0002
> sh.addShardTag("shard0002", "harv")
现在,如果您等待几分钟并查看数据块,将看到Two-Face的集合分布在2个分片上,另外两个集合分布在shard0000分片上。
> db.chunks.find({ns: "villains.poison ivy"}, {shard:1, _id:0}).sort({shard:1})
{ "shard" : "shard0000" }
{ "shard" : "shard0000" }
{ "shard" : "shard0000" }
{ "shard" : "shard0000" }
{ "shard" : "shard0000" }
{ "shard" : "shard0000" }
{ "shard" : "shard0000" }
{ "shard" : "shard0000" }
{ "shard" : "shard0000" }
> db.chunks.find({ns: "villains.two-face"}, {shard:1, _id:0}).sort({shard:1})
{ "shard" : "shard0001" }
{ "shard" : "shard0001" }
{ "shard" : "shard0001" }
{ "shard" : "shard0001" }
{ "shard" : "shard0001" }
{ "shard" : "shard0002" }
{ "shard" : "shard0002" }
{ "shard" : "shard0002" }
{ "shard" : "shard0002" }
{ "shard" : "shard0002" }
然而,对于 Harvey来说,这仍然不太合适,他希望一块分片性能好,另一块分片性能差些。比方说,我们利用亚马逊的新服务,用SSD代替shard0002分片。然后我们划分流量:将 Harvey的50%的写入发送到SSD分片,50%发送到HDD分片。首先,我们将标签添加到分片,并为它们添加描述:
> sh.addShardTag("shard0001", "spinning")
> sh.addShardTag("shard0002", "ssd")
设定一个“运气”字段,运气值在0和1之间,我们设定如果运气<0.5,发送到HDD。如果运气> = 0.5,发送到SSD。
> sh.addTagRange("villains.two-face", {luck:MinKey}, {luck:.5}, "spinning")
> sh.addTagRange("villains.two-face", {luck:.5}, {luck:MaxKey}, "ssd")
现在,“倒霉”文档将被写入慢速磁盘,“好运”文档将写入SSD。
通过这种方式添加新的服务器,我们可以控制他们的负载情况。标记为DBA提供了对集合存放位置的控制权。
最后,我写了一个小脚本,为集合添加一个“home”方法,将它们固定在一个标签上。用法示例:
> // load the script
> load("batman.js")
> // put foo on bar
> db.foo.home("bar")
> // put baz on bar
> db.baz.home("bar")
> // move foo to bat
> db.foo.home("bat")