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.