Статьи

Управление распространением коллекции в MongoDB

Теги Shard — это новая функция в MongoDB версии 2.2.0. Предполагается, что принудительная запись поступает в локальный центр обработки данных, но его также можно использовать для закрепления коллекции на осколке или наборе осколков.

Примечание: чтобы попробовать это, вам придется использовать 2.2.0-rc0 или выше.

Чтобы поиграть с этой функцией, сначала вам нужно раскрутить кластер:

> sharding = new ShardingTest({shards:3,chunksize:1})

Эта команда запустит 3 шарда, сервер конфигурации и монгос . Он также начнет выпускать журналы со всех серверов в стандартный вывод, поэтому я рекомендую отложить эту оболочку и использовать другую, начиная с этого момента.

Запустите новую оболочку и подключитесь к mongos (по умолчанию это порт 30999) и создайте несколько закрытых коллекций и данных для игры:

> // remember, different shell
> conn = new Mongo("localhost:30999")
> db = conn.getDB("villains")
>
> // shard db
> sh.enableSharding("villains")
>
> // shard collections
> sh.shardCollection("villains.joker", {jokes:1});
> sh.shardCollection("villains.two-face", {luck:1});
> sh.shardCollection("villains.poison ivy", {flora:1});
> 
> // add data
> 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()}); }

Теперь у нас есть 3 осколка и 3 злодея. Если вы посмотрите, где находятся куски, вы увидите, что они довольно равномерно распределены среди осколков:

> 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" }

Или, как сказал бы Харли, «пудин».

Однако злодеи, как правило, не очень хорошо играют с другими, поэтому мы хотели бы разделить коллекции: 1 злодей на осколок. Наша цель:

осколок Пространство имен
shard0000 «Villains.joker»
shard0001 «Villains.two-лицо»
shard0002 «Злодеи. Ядовитый плющ»

Для этого мы будем использовать теги . Тег описывает свойство шарда, любое свойство (они очень гибкие). Таким образом, вы можете пометить осколок как «быстрый», «медленный», «восточное побережье» или «место в стойке».

В этом примере мы хотим пометить осколок как принадлежащий определенному злодею, поэтому мы добавим псевдонимы злодеев в качестве тегов.

> sh.addShardTag("shard0000", "mr. j")
> sh.addShardTag("shard0001", "harv")
> sh.addShardTag("shard0002", "ivy")

Это говорит: «Поместите любые куски с пометкой г-н. J ‘на Shard0000. «

Второе, что мы должны сделать, — это создать правило: «Для всех кусков, созданных в коллекции villains.joker, присвойте им тег« mr. у»«. Для этого мы можем использовать помощник addTagRange :

> sh.addTagRange("villains.joker", {jokes:MinKey}, {jokes:MaxKey}, "mr. j")

Это говорит: «Отметьте каждый кусок в villains.joker с помощью« г-н. j ‘tag ”(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" }

«Плохие головы, вы получаете EBS.»

Однако, это все еще не совсем правильно для Харви, он хотел бы, чтобы один осколок был хорошим, а другой — плохим. Допустим, мы пользуемся преимуществами нового предложения Amazon и заменяем shard0002 на твердотельные накопители. Затем мы делим трафик: отправляем 50% записей Харви на осколок SSD и 50% на осколок вращающегося диска. Сначала мы добавим теги в шарды, описывая их:

> sh.addShardTag("shard0001", "spinning")
> sh.addShardTag("shard0002", "ssd")

Значение поля «luck» находится в диапазоне от 0 до 1, поэтому мы хотим сказать: «Если luck <.5, отправьте это на вращающийся диск. Если удача> = .5, отправьте на SSD ».

> sh.addTagRange("villains.two-face", {luck:MinKey}, {luck:.5}, "spinning")
> sh.addTagRange("villains.two-face", {luck:.5}, {luck:MaxKey}, "ssd")

Теперь документы «неудачи» будут записываться на медленный диск, а документы «удачи» записываться на SSD.

Добавляя новые серверы, мы можем контролировать, какую нагрузку они получают. Маркировка дает операторам тонну контроля над тем, какие коллекции идут куда.

Наконец, я написал небольшой скрипт, который добавляет «домашний» метод в коллекции, чтобы прикрепить их к одному тегу. Пример использования:

> // 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")