MongoDB
 sql >> база данни >  >> NoSQL >> MongoDB

Как програмно предварително да разделите ключ на шард, базиран на GUID, с MongoDB

Знаем първоначалния размер на данните (120GB) и знаем, че максималният размер на парчето по подразбиране в MongoDB е 64MB. Ако разделим 64MB на 120GB, получаваме 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 парчета), но всички те все още са на оригиналния шард, защото балансьорът е изключен. И така, нека да активираме отново балансира:

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 ), за да върнете всичко към настройките по подразбиране.

**

Актуализация:Оптимизиране за скорост

**

Подходът по-горе е добър, ако не бързате. Както стоят нещата и както ще откриете, ако тествате това, балансьорът не е много бърз - дори и с празни парчета. Следователно, когато увеличавате броя на парчетата, които създавате, толкова повече време ще отнеме за балансиране. Виждал съм, че отнема повече от 30 минути, за да завърши балансирането на 2048 парчета, въпреки че това ще варира в зависимост от внедряването.

Това може да е добре за тестване или за относително тих клъстер, но изключването на балансира и липсата на други актуализации ще бъде много по-трудно да се гарантира в натоварен клъстер. И така, как да ускорим нещата?

Отговорът е да направите някои ръчни движения рано, след което да разделите парчетата, след като са върху съответните им парчета. Имайте предвид, че това е желателно само с определени ключове на сегменти (като произволно разпределен 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    

И ето го - няма чакане на балансира, разпределението вече е равномерно.




  1. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. MongoDB $slice

  2. MongoDB $substrBytes

  3. Как да свържете отдалечен mongodb с pymongo

  4. MongoDB:как да анализирам дата във версия 3.6 mongoDb?

  5. Уникалният индекс на Mongoose не работи!