sql >> Databasteknik >  >> NoSQL >> MongoDB

Hur man försplittrar en GUID-baserad Shard Key med MongoDB

Vi känner till den ursprungliga datastorleken (120 GB) och vi vet att standardstorleken för maximal chunk i MongoDB är 64 MB. Om vi ​​delar upp 64MB i 120GB får vi 1920 - så det är det minsta antalet bitar vi bör titta på till att börja med. Som det händer råkar 2048 vara en potens av 16 dividerat med 2, och med tanke på att GUID (vår skärvnyckel) är hexbaserad, är det ett mycket lättare tal att hantera än 1920 (se nedan).

OBS: Denna fördelning måste göras före all data läggs till samlingen. Om du använder kommandot enableSharding() på en samling som innehåller data, kommer MongoDB att dela upp själva data och du kommer sedan att köra detta medan det redan finns bitar - det kan leda till ganska udda distribution av bitar, så se upp.

För detta svars syfte, låt oss anta att databasen kommer att kallas users och samlingen heter userInfo . Låt oss också anta att GUID kommer att skrivas in i _id fält. Med dessa parametrar skulle vi ansluta till en mongos och kör följande kommandon:

// 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(); 

Nu, enligt beräkningen ovan, måste vi dela upp GUID-intervallet i 2048 bitar. För att göra det behöver vi minst 3 hexadecimala siffror (16 ^ 3 =4096) och vi kommer att sätta dem i de mest signifikanta siffrorna (dvs. de 3 längst till vänster) för intervallen. Återigen, detta bör köras från en mongos skal

// 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 } } );
    }
  }
}

När det är gjort, låt oss kontrollera spelläget med hjälp av sh.status() hjälpare:

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

Vi har våra 2048-bitar (plus en extra tack vare min/max-bitarna), men de är alla kvar på den ursprungliga skärpan eftersom balanseringen är avstängd. Så låt oss återaktivera balanseringen:

sh.startBalancer();

Detta kommer omedelbart att börja balansera ut, och det kommer att gå relativt snabbt eftersom alla bitar är tomma, men det kommer fortfarande att ta en liten stund (mycket långsammare om det konkurrerar med migrationer från andra samlingar). När en tid har gått, kör sh.status() igen och där (borde) du ha det - 2048 bitar är alla snyggt uppdelade över 4 skärvor och redo för en första dataladdning:

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

Du är nu redo att börja ladda data, men för att absolut garantera att inga splittringar eller migrering sker förrän din dataladdning är klar måste du göra en sak till - stänga av balanseringen och autodelningen under importens varaktighet:

  • För att inaktivera all balansering, kör det här kommandot från mongos:sh.stopBalancer()
  • Om du vill låta andra balanseringsoperationer köras kan du inaktivera på en specifik samling. Använd namnutrymmet ovan som ett exempel:sh.disableBalancing("users.userInfo")
  • För att stänga av automatisk delning under laddningen måste du starta om varje mongos du kommer att använda för att ladda data med --noAutoSplit alternativ.

När importen är klar, vänd om stegen efter behov (sh.startBalancer() , sh.enableBalancing("users.userInfo") , och starta om mongos utan --noAutoSplit ) för att återställa allt till standardinställningarna.

**

Uppdatering:Optimering för hastighet

**

Tillvägagångssättet ovan är bra om du inte har bråttom. Som det ser ut, och som du kommer att upptäcka om du testar detta, är balanseraren inte särskilt snabb - även med tomma bitar. Allteftersom du ökar antalet bitar du skapar, desto längre tid kommer det att ta att balansera. Jag har sett att det tar mer än 30 minuter att slutföra balanseringen av 2048 bitar, även om detta kommer att variera beroende på distributionen.

Det kan vara OK för att testa, eller för ett relativt tyst kluster, men att ha balanseringen avstängd och att inga andra uppdateringar stör kommer att vara mycket svårare att säkerställa på ett upptaget kluster. Så, hur snabbar vi upp saker och ting?

Svaret är att göra några manuella drag tidigt och sedan dela upp bitarna när de är på sina respektive skärvor. Observera att detta endast är önskvärt med vissa shard-nycklar (som ett slumpmässigt distribuerat UUID), eller vissa dataåtkomstmönster, så var försiktig så att du inte får dålig datadistribution som resultat.

Med exemplet ovan har vi 4 skärvor, så istället för att göra alla delningar och sedan balansera, delar vi upp oss i fyra istället. Vi lägger sedan en bit på varje skärva genom att manuellt flytta dem, och till slut delar vi upp dessa bitar i det antal som krävs.

Områdena i exemplet ovan skulle se ut så här:

$min --> "40000000000000000000000000000000"
"40000000000000000000000000000000" --> "80000000000000000000000000000000"
"80000000000000000000000000000000" --> "c0000000000000000000000000000000"
"c0000000000000000000000000000000" --> $max     

Det är bara 4 kommandon för att skapa dessa, men eftersom vi har det, varför inte återanvända slingan ovan i en förenklad/modifierad form:

for ( var x=4; x < 16; x+=4){
    var prefix = "" + x.toString(16) + "0000000000000000000000000000000";
    db.adminCommand( { split : "users.userInfo" , middle : { _id : prefix } } ); 
} 

Så här ser tankar ut nu - vi har våra fyra bitar, alla på 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)                    

Vi lämnar $min bit där den är och flytta de andra tre. Du kan göra detta programmässigt, men det beror på var bitarna bor initialt, hur du har namngett dina skärvor etc. så jag lämnar den här manualen för nu, den är inte alltför betungande - bara 3 moveChunk kommandon:

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 }          

Låt oss dubbelkolla och se till att bitarna är där vi förväntar oss att de ska vara:

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)  

Det matchar våra föreslagna intervall ovan, så allt ser bra ut. Kör nu originalslingan ovan för att dela dem "på plats" på varje skärva och vi bör ha en balanserad fördelning så snart slingan är klar. En till sh.status() bör bekräfta saker:

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    

Och där har du det - ingen väntan på balansören, fördelningen är redan jämn.




  1. Hur flyttar jag en redis-databas från en server till en annan?

  2. Redis, Node.js och Socket.io:Korsserverautentisering och förståelse för node.js

  3. ställa in utgångsdatum för Hashmap-värden i Redis?

  4. Transaktionsstöd i MongoDB