We kennen de initiële gegevensgrootte (120 GB) en we weten dat de standaard maximale chunkgrootte in MongoDB 64 MB is. Als we 64 MB in 120 GB verdelen, krijgen we 1920 - dus dat is het minimum aantal brokken waar we om moeten beginnen. Toevallig is 2048 een macht van 16 gedeeld door 2, en aangezien de GUID (onze shard-sleutel) op hex is gebaseerd, is dat een veel gemakkelijker getal om mee om te gaan dan 1920 (zie hieronder).
OPMERKING: Deze pre-splitsing moet voor worden gedaan alle gegevens worden aan de collectie toegevoegd. Als u de opdracht enableSharding() gebruikt voor een verzameling die gegevens bevat, zal MongoDB de gegevens zelf splitsen en u voert dit vervolgens uit terwijl er al chunks bestaan - dat kan leiden tot een nogal vreemde verdeling van chunks, dus pas op.
Laten we voor dit antwoord aannemen dat de database users
gaat heten en de verzameling heet userInfo
. Laten we ook aannemen dat de GUID wordt geschreven in de _id
veld. Met die parameters zouden we verbinding maken met een mongos
en voer de volgende opdrachten uit:
// 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 moeten we, volgens de bovenstaande berekening, het GUID-bereik opsplitsen in 2048 brokken. Om dat te doen hebben we minimaal 3 hexadecimale cijfers nodig (16 ^ 3 =4096) en we zullen ze in de meest significante cijfers plaatsen (d.w.z. de 3 meest linkse) voor de bereiken. Nogmaals, dit moet worden uitgevoerd vanuit een mongos
schelp
// 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 } } );
}
}
}
Zodra dat is gebeurd, gaan we de stand van zaken controleren met behulp van de sh.status()
helper:
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
We hebben onze 2048 chunks (plus één extra dankzij de min/max chunks), maar ze staan allemaal nog op de originele shard omdat de balancer uit staat. Laten we de balancer dus opnieuw inschakelen:
sh.startBalancer();
Dit begint onmiddellijk in evenwicht te komen, en het zal relatief snel zijn omdat alle brokken leeg zijn, maar het zal nog even duren (veel langzamer als het concurreert met migraties uit andere collecties). Als er enige tijd is verstreken, voer je sh.status()
. uit nogmaals en daar (zou) je het hebben - 2048 brokken allemaal mooi verdeeld over 4 scherven en klaar voor een eerste gegevensbelasting:
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" }
U bent nu klaar om gegevens te gaan laden, maar om absoluut te garanderen dat er geen splitsingen of migraties plaatsvinden totdat het laden van uw gegevens is voltooid, moet u nog één ding doen:de balancer uitschakelen en automatisch splitsen voor de duur van de import:
- Als u alle balancering wilt uitschakelen, voert u deze opdracht uit vanaf de mongo's:
sh.stopBalancer()
- Als u andere balanceringsbewerkingen wilt laten lopen, kunt u deze voor een specifieke verzameling uitschakelen. Gebruik de bovenstaande naamruimte als voorbeeld:
sh.disableBalancing("users.userInfo")
- Als u automatisch splitsen tijdens het laden wilt uitschakelen, moet u elke
mongos
opnieuw opstarten die u gaat gebruiken om de gegevens te laden met de--noAutoSplit
optie.
Zodra het importeren is voltooid, keert u de stappen indien nodig om (sh.startBalancer()
, sh.enableBalancing("users.userInfo")
, en herstart de mongos
zonder --noAutoSplit
) om alles terug te zetten naar de standaardinstellingen.
**
Update:optimaliseren voor snelheid
**
Bovenstaande aanpak is prima als je geen haast hebt. Zoals de zaken er nu voorstaan, en zoals je zult ontdekken als je dit test, is de balancer niet erg snel - zelfs niet met lege brokken. Dus als je het aantal chunks dat je maakt vergroot, hoe langer het duurt om in evenwicht te komen. Ik heb gezien dat het meer dan 30 minuten duurde om 2048 chunks in evenwicht te brengen, hoewel dit afhankelijk is van de implementatie.
Dat is misschien goed voor testen of voor een relatief stil cluster, maar als de balancer is uitgeschakeld en er geen andere updates nodig zijn, zal het veel moeilijker zijn om ervoor te zorgen op een druk cluster. Dus, hoe kunnen we dingen versnellen?
Het antwoord is om vroeg wat handmatige bewegingen te doen en de brokken vervolgens te splitsen zodra ze op hun respectievelijke scherven zijn. Houd er rekening mee dat dit alleen wenselijk is bij bepaalde shard-sleutels (zoals een willekeurig verdeelde UUID) of bepaalde datatoegangspatronen, dus pas op dat u als resultaat geen slechte datadistributie krijgt.
Als we het bovenstaande voorbeeld gebruiken, hebben we 4 scherven, dus in plaats van alle splitsingen te doen en vervolgens te balanceren, splitsen we in plaats daarvan in 4. Vervolgens plaatsen we een brok op elke scherf door ze handmatig te verplaatsen, en ten slotte splitsen we die brokjes in het vereiste aantal.
De bereiken in het bovenstaande voorbeeld zien er als volgt uit:
$min --> "40000000000000000000000000000000"
"40000000000000000000000000000000" --> "80000000000000000000000000000000"
"80000000000000000000000000000000" --> "c0000000000000000000000000000000"
"c0000000000000000000000000000000" --> $max
Het zijn maar 4 commando's om deze te maken, maar aangezien we die hebben, waarom zou je de bovenstaande lus niet opnieuw gebruiken in een vereenvoudigde/aangepaste vorm:
for ( var x=4; x < 16; x+=4){
var prefix = "" + x.toString(16) + "0000000000000000000000000000000";
db.adminCommand( { split : "users.userInfo" , middle : { _id : prefix } } );
}
Zo zien gedachten er nu uit - we hebben onze 4 chunks, allemaal op 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)
We laten de $min
brok waar het is, en verplaats de andere drie. Je kunt dit programmatisch doen, maar het hangt er wel van af waar de chunks zich aanvankelijk bevinden, hoe je je shards hebt genoemd etc. dus ik laat deze handleiding voor nu, het is niet te zwaar - slechts 3 moveChunk
commando's:
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 }
Laten we dubbel controleren en ervoor zorgen dat de brokken zijn waar we ze verwachten:
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)
Dat komt overeen met onze voorgestelde reeksen hierboven, dus alles ziet er goed uit. Voer nu de originele lus hierboven uit om ze "op hun plaats" op elke scherf te splitsen en we zouden een uitgebalanceerde distributie moeten hebben zodra de lus is voltooid. Nog een sh.status()
zou dingen moeten bevestigen:
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
En daar heb je het - niet wachten op de balancer, de verdeling is al gelijk.