Ответ 1
Мы знаем размер встроенных данных (120 ГБ), и мы знаем, что размер максимального размера по умолчанию в MongoDB составляет 64 МБ. Если мы разделим 64 МБ на 120 ГБ, мы получим 1920 - так что это минимальное количество кусков, с которыми мы должны начать работать. Как это бывает, 2048 случается с силой 16, разделенной на 2, и учитывая, что GUID (наш ключ осколка) имеет шестую основу, что намного проще иметь дело с чем 1920 (см. Ниже).
ПРИМЕЧАНИЕ. Это предварительное разбиение должно быть выполнено до того, как любые данные будут добавлены в коллекцию. Если вы используете команду enableSharding() в коллекции, содержащей данные, MongoDB будет разбивать сами данные, и тогда вы будете запускать это, а фрагменты уже существуют, что может привести к довольно нечетному распределению пакетов, поэтому будьте осторожны.
В целях этого ответа предположим, что база данных будет называться users
, а коллекция называется userInfo
. Пусть также предположим, что GUID будет записан в поле _id
. С этими параметрами мы подключаемся к mongos
и запускаем следующие команды:
// first switch to the users DB
use users;
// now enable sharding for the users DB
sh.enableSharding("users");
// enable sharding on the relevant collection
sh.shardCollection("users.userInfo", {"_id" : 1});
// finally, disable the balancer (see below for options on a per-collection basis)
// this prevents migrations from kicking off and interfering with the splits by competing for meta data locks
sh.stopBalancer();
Теперь, для расчета выше, нам нужно разбить диапазон GUID на 2048 кусков. Для этого нам нужно как минимум 3 шестнадцатеричных разряда (16 ^ 3 = 4096), и мы поставим их в самые значащие цифры (т.е. 3 левых) для диапазонов. Опять же, это должно запускаться из оболочки mongos
// Simply use a for loop for each digit
for ( var x=0; x < 16; x++ ){
for( var y=0; y<16; y++ ) {
// for the innermost loop we will increment by 2 to get 2048 total iterations
// make this z++ for 4096 - that would give ~30MB chunks based on the original figures
for ( var z=0; z<16; z+=2 ) {
// now construct the GUID with zeroes for padding - handily the toString method takes an argument to specify the base
var prefix = "" + x.toString(16) + y.toString(16) + z.toString(16) + "00000000000000000000000000000";
// finally, use the split command to create the appropriate chunk
db.adminCommand( { split : "users.userInfo" , middle : { _id : prefix } } );
}
}
}
Как только это будет сделано, проверьте состояние игры, используя sh.status()
помощник:
mongos> sh.status()
--- Sharding Status ---
sharding version: {
"_id" : 1,
"version" : 3,
"minCompatibleVersion" : 3,
"currentVersion" : 4,
"clusterId" : ObjectId("527056b8f6985e1bcce4c4cb")
}
shards:
{ "_id" : "shard0000", "host" : "localhost:30000" }
{ "_id" : "shard0001", "host" : "localhost:30001" }
{ "_id" : "shard0002", "host" : "localhost:30002" }
{ "_id" : "shard0003", "host" : "localhost:30003" }
databases:
{ "_id" : "admin", "partitioned" : false, "primary" : "config" }
{ "_id" : "users", "partitioned" : true, "primary" : "shard0001" }
users.userInfo
shard key: { "_id" : 1 }
chunks:
shard0001 2049
too many chunks to print, use verbose if you want to force print
У нас есть 2048 кусков (плюс один дополнительный благодаря min/max chunks), но они все еще находятся на первоначальном осколке, потому что балансир выключен. Итак, позвольте снова включить балансировку:
sh.startBalancer();
Это немедленно начнет балансировать, и оно будет относительно быстрым, потому что все куски пусты, но все равно потребуется немного времени (гораздо медленнее, если он конкурирует с миграциями из других коллекций). По прошествии некоторого времени снова запустите sh.status()
, и вы должны (должны) иметь его - 2048 кусков, которые хорошо расщепляются на 4 осколка и готовы к начальной загрузке данных:
mongos> sh.status()
--- Sharding Status ---
sharding version: {
"_id" : 1,
"version" : 3,
"minCompatibleVersion" : 3,
"currentVersion" : 4,
"clusterId" : ObjectId("527056b8f6985e1bcce4c4cb")
}
shards:
{ "_id" : "shard0000", "host" : "localhost:30000" }
{ "_id" : "shard0001", "host" : "localhost:30001" }
{ "_id" : "shard0002", "host" : "localhost:30002" }
{ "_id" : "shard0003", "host" : "localhost:30003" }
databases:
{ "_id" : "admin", "partitioned" : false, "primary" : "config" }
{ "_id" : "users", "partitioned" : true, "primary" : "shard0001" }
users.userInfo
shard key: { "_id" : 1 }
chunks:
shard0000 512
shard0002 512
shard0003 512
shard0001 513
too many chunks to print, use verbose if you want to force print
{ "_id" : "test", "partitioned" : false, "primary" : "shard0002" }
Теперь вы готовы начать загрузку данных, но чтобы абсолютно гарантировать, что никакие расколы или миграции не произойдут до тех пор, пока ваша загрузка данных не будет завершена, вам нужно сделать еще одну вещь: выключите балансировщик и авторасшивку на время импорта:
- Чтобы отключить все балансировки, запустите эту команду из mongos:
sh.stopBalancer()
- Если вы хотите оставить другие операции балансировки, вы можете отключить определенную коллекцию. Используя приведенное выше пространство имен:
sh.disableBalancing("users.userInfo")
- Чтобы отключить автоматическое разделение во время загрузки, вам необходимо перезапустить каждый
mongos
, который вы будете использовать для загрузки данных с помощью параметра--noAutoSplit
.
После завершения импорта измените необходимые шаги (sh.startBalancer()
, sh.enableBalancing("users.userInfo")
и перезапустите mongos
без --noAutoSplit
), чтобы вернуть все значения по умолчанию.
**
Обновление: оптимизация скорости
**
Подход, описанный выше, хорош, если вы не спешите. Когда все будет стоять, и, как вы обнаружите, если вы проверите это, балансир не очень быстрый - даже с пустыми кусками. Следовательно, поскольку вы увеличиваете количество кусков, которые вы создаете, тем дольше он будет балансировать. Я видел, что для завершения балансировки 2048 кусков требуется более 30 минут, хотя это будет зависеть от развертывания.
Это может быть нормально для тестирования или для относительно спокойного кластера, но с выключением балансира и не требуя каких-либо других обновлений, будет намного сложнее обеспечить работу в загруженном кластере. Итак, как мы ускоряем работу?
Ответ заключается в том, чтобы сделать некоторые ручные шаги раньше, а затем разделить куски, когда они находятся на их соответствующих осколках. Обратите внимание, что это желательно только с определенными ключами осколков (например, случайно распределенным UUID) или некоторыми шаблонами доступа к данным, поэтому будьте осторожны, чтобы в результате вы не получили плохого распределения данных.
Используя пример выше, у нас есть 4 осколка, поэтому вместо того, чтобы делать все расщепления, а затем балансируя, мы разделим на 4. Затем мы помещаем один кусок на каждый осколок, вручную перемещая их, а затем, наконец, разделим эти куски на требуемое число.
Диапазоны в приведенном выше примере выглядят следующим образом:
$min --> "40000000000000000000000000000000"
"40000000000000000000000000000000" --> "80000000000000000000000000000000"
"80000000000000000000000000000000" --> "c0000000000000000000000000000000"
"c0000000000000000000000000000000" --> $max
Это всего лишь 4 команды для их создания, но поскольку у нас есть это, почему бы не повторить цикл выше в упрощенной/модифицированной форме:
for ( var x=4; x < 16; x+=4){
var prefix = "" + x.toString(16) + "0000000000000000000000000000000";
db.adminCommand( { split : "users.userInfo" , middle : { _id : prefix } } );
}
Вот как думает сейчас посмотреть - у нас есть 4 куска, все на shard0001:
mongos> sh.status()
--- Sharding Status ---
sharding version: {
"_id" : 1,
"version" : 4,
"minCompatibleVersion" : 4,
"currentVersion" : 5,
"clusterId" : ObjectId("53467e59aea36af7b82a75c1")
}
shards:
{ "_id" : "shard0000", "host" : "localhost:30000" }
{ "_id" : "shard0001", "host" : "localhost:30001" }
{ "_id" : "shard0002", "host" : "localhost:30002" }
{ "_id" : "shard0003", "host" : "localhost:30003" }
databases:
{ "_id" : "admin", "partitioned" : false, "primary" : "config" }
{ "_id" : "test", "partitioned" : false, "primary" : "shard0001" }
{ "_id" : "users", "partitioned" : true, "primary" : "shard0001" }
users.userInfo
shard key: { "_id" : 1 }
chunks:
shard0001 4
{ "_id" : { "$minKey" : 1 } } -->> { "_id" : "40000000000000000000000000000000" } on : shard0001 Timestamp(1, 1)
{ "_id" : "40000000000000000000000000000000" } -->> { "_id" : "80000000000000000000000000000000" } on : shard0001 Timestamp(1, 3)
{ "_id" : "80000000000000000000000000000000" } -->> { "_id" : "c0000000000000000000000000000000" } on : shard0001 Timestamp(1, 5)
{ "_id" : "c0000000000000000000000000000000" } -->> { "_id" : { "$maxKey" : 1 } } on : shard0001 Timestamp(1, 6)
Мы оставим кусок $min
там, где он есть, и переместите остальные три. Вы можете сделать это программно, но это зависит от того, где расположены фрагменты, как вы назвали ваши осколки и т.д., Поэтому я оставлю это руководство на данный момент, это не слишком обременительно - всего 3 moveChunk
:
mongos> sh.moveChunk("users.userInfo", {"_id" : "40000000000000000000000000000000"}, "shard0000")
{ "millis" : 1091, "ok" : 1 }
mongos> sh.moveChunk("users.userInfo", {"_id" : "80000000000000000000000000000000"}, "shard0002")
{ "millis" : 1078, "ok" : 1 }
mongos> sh.moveChunk("users.userInfo", {"_id" : "c0000000000000000000000000000000"}, "shard0003")
{ "millis" : 1083, "ok" : 1 }
Дайте двойной проверке и убедитесь, что куски - это то место, где мы ожидаем, что они будут:
mongos> sh.status()
--- Sharding Status ---
sharding version: {
"_id" : 1,
"version" : 4,
"minCompatibleVersion" : 4,
"currentVersion" : 5,
"clusterId" : ObjectId("53467e59aea36af7b82a75c1")
}
shards:
{ "_id" : "shard0000", "host" : "localhost:30000" }
{ "_id" : "shard0001", "host" : "localhost:30001" }
{ "_id" : "shard0002", "host" : "localhost:30002" }
{ "_id" : "shard0003", "host" : "localhost:30003" }
databases:
{ "_id" : "admin", "partitioned" : false, "primary" : "config" }
{ "_id" : "test", "partitioned" : false, "primary" : "shard0001" }
{ "_id" : "users", "partitioned" : true, "primary" : "shard0001" }
users.userInfo
shard key: { "_id" : 1 }
chunks:
shard0001 1
shard0000 1
shard0002 1
shard0003 1
{ "_id" : { "$minKey" : 1 } } -->> { "_id" : "40000000000000000000000000000000" } on : shard0001 Timestamp(4, 1)
{ "_id" : "40000000000000000000000000000000" } -->> { "_id" : "80000000000000000000000000000000" } on : shard0000 Timestamp(2, 0)
{ "_id" : "80000000000000000000000000000000" } -->> { "_id" : "c0000000000000000000000000000000" } on : shard0002 Timestamp(3, 0)
{ "_id" : "c0000000000000000000000000000000" } -->> { "_id" : { "$maxKey" : 1 } } on : shard0003 Timestamp(4, 0)
Это соответствует нашим предлагаемым диапазонам выше, поэтому все выглядит хорошо. Теперь запустите исходный цикл выше, чтобы разделить их "на месте" на каждом осколке, и мы должны иметь сбалансированное распределение, как только цикл закончится. Еще один sh.status()
должен подтвердить:
mongos> for ( var x=0; x < 16; x++ ){
... for( var y=0; y<16; y++ ) {
... // for the innermost loop we will increment by 2 to get 2048 total iterations
... // make this z++ for 4096 - that would give ~30MB chunks based on the original figures
... for ( var z=0; z<16; z+=2 ) {
... // now construct the GUID with zeroes for padding - handily the toString method takes an argument to specify the base
... var prefix = "" + x.toString(16) + y.toString(16) + z.toString(16) + "00000000000000000000000000000";
... // finally, use the split command to create the appropriate chunk
... db.adminCommand( { split : "users.userInfo" , middle : { _id : prefix } } );
... }
... }
... }
{ "ok" : 1 }
mongos> sh.status()
--- Sharding Status ---
sharding version: {
"_id" : 1,
"version" : 4,
"minCompatibleVersion" : 4,
"currentVersion" : 5,
"clusterId" : ObjectId("53467e59aea36af7b82a75c1")
}
shards:
{ "_id" : "shard0000", "host" : "localhost:30000" }
{ "_id" : "shard0001", "host" : "localhost:30001" }
{ "_id" : "shard0002", "host" : "localhost:30002" }
{ "_id" : "shard0003", "host" : "localhost:30003" }
databases:
{ "_id" : "admin", "partitioned" : false, "primary" : "config" }
{ "_id" : "test", "partitioned" : false, "primary" : "shard0001" }
{ "_id" : "users", "partitioned" : true, "primary" : "shard0001" }
users.userInfo
shard key: { "_id" : 1 }
chunks:
shard0001 513
shard0000 512
shard0002 512
shard0003 512
too many chunks to print, use verbose if you want to force print
И там у вас есть - нет ожидания балансировки, распределение уже равно.