Nous connaissons la taille initiale des données (120 Go) et nous savons que la taille de bloc maximale par défaut dans MongoDB est de 64 Mo. Si nous divisons 64 Mo en 120 Go, nous obtenons 1920 - c'est donc le nombre minimum de morceaux que nous devrions regarder pour commencer. Il se trouve que 2048 est une puissance de 16 divisé par 2, et étant donné que le GUID (notre clé de fragment) est basé sur l'hexagone, c'est un nombre beaucoup plus facile à gérer que 1920 (voir ci-dessous).
REMARQUE : Ce pré-split doit être fait avant toutes les données sont ajoutées à la collection. Si vous utilisez la commande enableSharding() sur une collection qui contient des données, MongoDB divisera les données elles-mêmes et vous exécuterez alors ceci alors que des morceaux existent déjà - cela peut conduire à une distribution de morceaux assez étrange, alors méfiez-vous.
Pour les besoins de cette réponse, supposons que la base de données s'appellera users
et la collection s'appelle userInfo
. Supposons également que le GUID sera écrit dans le _id
domaine. Avec ces paramètres, nous nous connecterions à un mongos
et exécutez les commandes suivantes :
// 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();
Maintenant, selon le calcul ci-dessus, nous devons diviser la plage GUID en 2048 morceaux. Pour ce faire, nous avons besoin d'au moins 3 chiffres hexadécimaux (16 ^ 3 =4096) et nous les mettrons dans les chiffres les plus significatifs (c'est-à-dire les 3 les plus à gauche) pour les plages. Encore une fois, cela devrait être exécuté à partir d'un mongos
coquille
// 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 } } );
}
}
}
Une fois cela fait, vérifions l'état du jeu en utilisant le sh.status()
aide :
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
Nous avons nos 2048 morceaux (plus un supplémentaire grâce aux morceaux min/max), mais ils sont tous toujours sur le fragment d'origine car l'équilibreur est désactivé. Alors, réactivons l'équilibreur :
sh.startBalancer();
Cela commencera immédiatement à s'équilibrer, et ce sera relativement rapide car tous les morceaux sont vides, mais cela prendra encore un peu de temps (beaucoup plus lent s'il est en concurrence avec des migrations d'autres collections). Une fois qu'un certain temps s'est écoulé, exécutez sh.status()
encore une fois et là vous (devriez) l'avoir - 2048 morceaux tous bien répartis sur 4 fragments et prêts pour un chargement de données initial :
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" }
Vous êtes maintenant prêt à commencer à charger des données, mais pour garantir absolument qu'aucune division ou migration ne se produise tant que le chargement de vos données n'est pas terminé, vous devez faire une dernière chose :désactiver l'équilibreur et le fractionnement automatique pendant la durée de l'importation :
- Pour désactiver tout équilibrage, exécutez cette commande depuis les mongos :
sh.stopBalancer()
- Si vous souhaitez laisser d'autres opérations d'équilibrage en cours d'exécution, vous pouvez les désactiver sur une collection spécifique. En utilisant l'espace de noms ci-dessus comme exemple :
sh.disableBalancing("users.userInfo")
- Pour désactiver le fractionnement automatique pendant le chargement, vous devrez redémarrer chaque
mongos
vous utiliserez pour charger les données avec le--noAutoSplit
option.
Une fois l'importation terminée, inversez les étapes si nécessaire (sh.startBalancer()
, sh.enableBalancing("users.userInfo")
, et redémarrez le mongos
sans --noAutoSplit
) pour tout remettre aux paramètres par défaut.
**
Mise à jour :Optimisation de la vitesse
**
L'approche ci-dessus convient si vous n'êtes pas pressé. Dans l'état actuel des choses, et comme vous le découvrirez si vous testez cela, l'équilibreur n'est pas très rapide - même avec des morceaux vides. Par conséquent, plus vous augmentez le nombre de morceaux que vous créez, plus il faudra de temps pour équilibrer. J'ai vu qu'il fallait plus de 30 minutes pour terminer l'équilibrage de 2 048 morceaux, bien que cela varie en fonction du déploiement.
Cela peut convenir pour les tests ou pour un cluster relativement silencieux, mais il sera beaucoup plus difficile de s'assurer que l'équilibreur est désactivé et qu'aucune autre mise à jour n'interfère sur un cluster occupé. Alors, comment accélérer les choses ?
La réponse est de faire quelques mouvements manuels tôt, puis de diviser les morceaux une fois qu'ils sont sur leurs fragments respectifs. Notez que cela n'est souhaitable qu'avec certaines clés de partition (comme un UUID distribué de manière aléatoire) ou certains modèles d'accès aux données. Veillez donc à ne pas vous retrouver avec une mauvaise distribution des données.
En utilisant l'exemple ci-dessus, nous avons 4 fragments, donc plutôt que de faire toutes les divisions, puis d'équilibrer, nous nous sommes divisés en 4 à la place. Nous plaçons ensuite un morceau sur chaque fragment en les déplaçant manuellement, puis nous divisons finalement ces morceaux en le nombre requis.
Les plages de l'exemple ci-dessus ressembleraient à ceci :
$min --> "40000000000000000000000000000000"
"40000000000000000000000000000000" --> "80000000000000000000000000000000"
"80000000000000000000000000000000" --> "c0000000000000000000000000000000"
"c0000000000000000000000000000000" --> $max
Il n'y a que 4 commandes pour les créer, mais puisque nous l'avons, pourquoi ne pas réutiliser la boucle ci-dessus sous une forme simplifiée/modifiée :
for ( var x=4; x < 16; x+=4){
var prefix = "" + x.toString(16) + "0000000000000000000000000000000";
db.adminCommand( { split : "users.userInfo" , middle : { _id : prefix } } );
}
Voici à quoi ressemblent les pensées maintenant - nous avons nos 4 morceaux, tous sur 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)
Nous laisserons le $min
morceau où il est, et déplacez les trois autres. Vous pouvez le faire par programmation, mais cela dépend de l'emplacement initial des morceaux, de la façon dont vous avez nommé vos fragments, etc. Je vais donc laisser ce manuel pour l'instant, ce n'est pas trop lourd - juste 3 moveChunk
commandes :
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 }
Vérifions une deuxième fois et assurons-nous que les morceaux sont là où nous nous attendons à ce qu'ils soient :
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)
Cela correspond à nos gammes proposées ci-dessus, donc tout semble bon. Exécutez maintenant la boucle d'origine ci-dessus pour les diviser "en place" sur chaque fragment et nous devrions avoir une distribution équilibrée dès que la boucle se termine. Un autre sh.status()
devrait confirmer les choses :
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
Et voilà, pas d'attente pour l'équilibreur, la distribution est déjà régulière.