sql >> Database teknologi >  >> NoSQL >> MongoDB

Sådan forudspalter du en GUID-baseret Shard-nøgle med MongoDB

Vi kender den oprindelige datastørrelse (120 GB), og vi ved, at den maksimale standardstørrelse i MongoDB er 64 MB. Hvis vi deler 64MB i 120GB, får vi 1920 - så det er minimumsantallet af chunks, vi skal se til at starte med. Som det sker, er 2048 tilfældigvis en potens af 16 divideret med 2, og givet at GUID (vores shard-nøgle) er hex-baseret, er det et meget nemmere tal at håndtere end 1920 (se nedenfor).

BEMÆRK: Denne forhåndsopdeling skal udføres før eventuelle data tilføjes til samlingen. Hvis du bruger kommandoen enableSharding() på en samling, der indeholder data, vil MongoDB opdele selve dataene, og du vil derefter køre dette, mens der allerede findes bidder - det kan føre til en ret mærkelig fordeling af stykker, så pas på.

Med henblik på dette svar, lad os antage, at databasen kommer til at hedde users og samlingen hedder userInfo . Lad os også antage, at GUID'et vil blive skrevet ind i _id Mark. Med disse parametre ville vi forbinde til en mongos og kør følgende kommandoer:

// 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 skal vi ifølge beregningen ovenfor opdele GUID-området i 2048 bidder. For at gøre det har vi brug for mindst 3 hex-cifre (16 ^ 3 =4096), og vi vil sætte dem i de mest signifikante cifre (dvs. de 3 længst til venstre) for områderne. Igen skal dette køres fra 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 er gjort, lad os tjekke spilletilstanden ved hjælp af sh.status() hjælper:

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 vores 2048 chunks (plus en ekstra takket være min/max chunks), men de er alle stadig på den originale shard, fordi balanceren er slukket. Så lad os genaktivere balanceren:

sh.startBalancer();

Dette vil straks begynde at balancere, og det vil være relativt hurtigt, fordi alle bidder er tomme, men det vil stadig tage lidt tid (meget langsommere, hvis det konkurrerer med migreringer fra andre samlinger). Når der er gået noget tid, skal du køre sh.status() igen og der (skal) du have det - 2048 bidder er alle pænt fordelt på 4 shards og klar til en indledende dataindlæsning:

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 er nu klar til at begynde at indlæse data, men for absolut at garantere, at ingen opdelinger eller migrering sker, før din dataindlæsning er fuldført, skal du gøre en ting mere - slå balanceren og autoopdelingen fra under importens varighed:

  • For at deaktivere al balancering, kør denne kommando fra mongoerne:sh.stopBalancer()
  • Hvis du vil lade andre balanceringsoperationer køre, kan du deaktivere på en bestemt samling. Brug af navnerummet ovenfor som eksempel:sh.disableBalancing("users.userInfo")
  • For at slå automatisk opdeling fra under indlæsningen skal du genstarte hver mongos du vil bruge til at indlæse dataene med --noAutoSplit mulighed.

Når importen er fuldført, skal du vende trinene om efter behov (sh.startBalancer() , sh.enableBalancing("users.userInfo") , og genstart mongos uden --noAutoSplit ) for at returnere alt til standardindstillingerne.

**

Opdatering:Optimering af hastighed

**

Fremgangsmåden ovenfor er fin, hvis du ikke har travlt. Som tingene ser ud, og som du vil opdage, hvis du tester dette, er balanceren ikke særlig hurtig - selv med tomme bidder. Derfor når du øger antallet af bidder, du opretter, jo længere tid vil det tage at balancere. Jeg har set det tage mere end 30 minutter at afslutte balanceringen af ​​2048 bidder, selvom dette vil variere afhængigt af implementeringen.

Det kan være OK til test eller for en relativt stille klynge, men at have balanceren slukket og ikke kræver andre opdateringer, vil være meget sværere at sikre på en travl klynge. Så hvordan fremskynder vi tingene?

Svaret er at lave nogle manuelle træk tidligt og derefter dele bidderne, når de er på deres respektive skår. Bemærk, at dette kun er ønskeligt med visse shard-nøgler (som et tilfældigt distribueret UUID) eller visse dataadgangsmønstre, så pas på, at du ikke ender med dårlig datadistribution som følge heraf.

Ved at bruge eksemplet ovenfor har vi 4 skår, så i stedet for at lave alle opdelingerne og derefter balancere, deler vi i 4 i stedet. Derefter lægger vi et stykke på hvert skår ved manuelt at flytte dem, og til sidst opdeler vi disse bidder i det nødvendige antal.

Områderne i eksemplet ovenfor ville se sådan ud:

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

Det er kun 4 kommandoer til at oprette disse, men da vi har det, hvorfor så ikke genbruge løkken ovenfor i en forenklet/modificeret form:

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

Sådan ser tanker ud nu - vi har vores 4 bidder, alle 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 forlader $min stykke, hvor det er, og flyt de tre andre. Du kan gøre dette programmæssigt, men det afhænger af, hvor bidderne befinder sig i starten, hvordan du har navngivet dine skår osv., så jeg lader denne manual ligge indtil videre, den er ikke for besværlig - kun 3 moveChunk kommandoer:

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 }          

Lad os dobbelttjekke og sikre os, at bidderne er, hvor vi forventer, at de er:

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 matcher vores foreslåede intervaller ovenfor, så alt ser godt ud. Kør nu den originale løkke ovenfor for at dele dem "på plads" på hvert skår, og vi bør have en afbalanceret fordeling, så snart løkken er færdig. En mere sh.status() skal bekræfte ting:

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    

Og der har du det - ingen ventetid på balanceren, fordelingen er allerede lige.




  1. Hvordan opdaterer man værdier ved hjælp af pymongo?

  2. Tæl i Spring Data MongoDB-lageret

  3. Forskellen mellem dokumentbaserede og nøgle/værdibaserede databaser?

  4. MongoDB Aggregation Framework - Dynamic Field Rename