MongoDB
 sql >> Datenbank >  >> NoSQL >> MongoDB

So teilen Sie einen GUID-basierten Shard-Schlüssel mit MongoDB programmgesteuert vorab auf

Wir kennen die anfängliche Datengröße (120 GB) und wir wissen, dass die standardmäßige maximale Chunk-Größe in MongoDB 64 MB beträgt. Wenn wir 64 MB in 120 GB teilen, erhalten wir 1920 – das ist also die Mindestanzahl von Chunks, mit der wir beginnen sollten. Zufällig ist 2048 eine Potenz von 16 dividiert durch 2, und da die GUID (unser Shard-Schlüssel) hexadezimal basiert, ist das eine viel einfachere Zahl als 1920 (siehe unten).

HINWEIS: Dieses Pre-Splitting muss vorher erfolgen Alle Daten werden der Sammlung hinzugefügt. Wenn Sie den enableSharding()-Befehl für eine Sammlung verwenden, die Daten enthält, wird MongoDB die Daten selbst aufteilen und Sie werden dies dann ausführen, während Chunks bereits vorhanden sind – das kann zu einer ziemlich seltsamen Chunk-Verteilung führen, seien Sie also vorsichtig.

Nehmen wir für diese Antwort an, dass die Datenbank users heißen wird und die Sammlung heißt userInfo . Nehmen wir außerdem an, dass die GUID in die _id geschrieben wird Feld. Mit diesen Parametern würden wir uns mit einem mongos verbinden und führen Sie die folgenden Befehle aus:

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

Nun müssen wir gemäß der obigen Berechnung den GUID-Bereich in 2048 Blöcke aufteilen. Dazu benötigen wir mindestens 3 Hex-Ziffern (16 ^ 3 =4096) und wir werden sie in die höchstwertigen Ziffern (d. h. die 3 ganz links) für die Bereiche einfügen. Auch dies sollte von einem mongos ausgeführt werden Schale

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

Sobald dies erledigt ist, prüfen wir den aktuellen Stand mit sh.status() Helfer:

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

Wir haben unsere 2048 Chunks (plus einen zusätzlichen dank der Min/Max-Chunks), aber sie sind alle noch auf dem ursprünglichen Shard, weil der Balancer ausgeschaltet ist. Lassen Sie uns also den Balancer wieder aktivieren:

sh.startBalancer();

Dies wird sofort beginnen, sich auszugleichen, und es wird relativ schnell gehen, da alle Chunks leer sind, aber es wird noch eine Weile dauern (viel langsamer, wenn es mit Migrationen aus anderen Sammlungen konkurriert). Führen Sie nach einiger Zeit sh.status() aus wieder und da (sollten) Sie es haben - 2048 Chunks, alle schön auf 4 Shards verteilt und bereit für eine anfängliche Datenladung:

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

Sie können jetzt mit dem Laden von Daten beginnen, aber um absolut zu garantieren, dass keine Aufteilungen oder Migrationen stattfinden, bis das Laden der Daten abgeschlossen ist, müssen Sie noch etwas tun – schalten Sie den Balancer und die automatische Aufteilung für die Dauer des Imports aus:

  • Um das gesamte Balancing zu deaktivieren, führen Sie diesen Befehl aus dem Mongos aus:sh.stopBalancer()
  • Wenn Sie andere Ausgleichsvorgänge laufen lassen möchten, können Sie eine bestimmte Sammlung deaktivieren. Verwenden Sie den obigen Namespace als Beispiel:sh.disableBalancing("users.userInfo")
  • Um die automatische Aufteilung während des Ladevorgangs auszuschalten, müssen Sie jeden mongos neu starten Sie verwenden, um die Daten mit --noAutoSplit zu laden Möglichkeit.

Sobald der Import abgeschlossen ist, kehren Sie die Schritte nach Bedarf um (sh.startBalancer() , sh.enableBalancing("users.userInfo") , und starten Sie mongos neu ohne --noAutoSplit ), um alles auf die Standardeinstellungen zurückzusetzen.

**

Update:Optimierung für Geschwindigkeit

**

Der obige Ansatz ist in Ordnung, wenn Sie es nicht eilig haben. So wie die Dinge stehen, und wie Sie feststellen werden, wenn Sie dies testen, ist der Balancer nicht sehr schnell - selbst mit leeren Chunks. Wenn Sie also die Anzahl der von Ihnen erstellten Chunks erhöhen, dauert es umso länger, bis das Gleichgewicht hergestellt ist. Ich habe gesehen, dass es mehr als 30 Minuten dauert, bis 2048 Chunks ausgeglichen sind, obwohl dies je nach Bereitstellung variieren kann.

Das mag zum Testen oder für einen relativ ruhigen Cluster in Ordnung sein, aber wenn der Balancer ausgeschaltet ist und keine anderen Updates stören, ist es viel schwieriger, auf einem ausgelasteten Cluster sicherzustellen. Also, wie beschleunigen wir die Dinge?

Die Antwort ist, einige manuelle Bewegungen frühzeitig durchzuführen und die Chunks dann aufzuteilen, sobald sie sich auf ihren jeweiligen Shards befinden. Beachten Sie, dass dies nur bei bestimmten Shard-Schlüsseln (wie einer zufällig verteilten UUID) oder bestimmten Datenzugriffsmustern wünschenswert ist, also achten Sie darauf, dass Sie am Ende nicht mit einer schlechten Datenverteilung enden.

Im obigen Beispiel haben wir 4 Shards, also anstatt alle Splits durchzuführen und dann auszugleichen, teilen wir uns stattdessen in 4 auf. Dann fügen wir jedem Shard einen Chunk hinzu, indem wir sie manuell verschieben, und teilen diese Chunks schließlich in die erforderliche Anzahl auf.

Die Bereiche im obigen Beispiel würden wie folgt aussehen:

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

Es sind nur 4 Befehle, um diese zu erstellen, aber da wir sie haben, warum nicht die obige Schleife in einer vereinfachten/modifizierten Form wiederverwenden:

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

So sehen die Gedanken jetzt aus – wir haben unsere 4 Chunks, alle auf 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)                    

Wir verlassen die $min Stück, wo es ist, und verschieben Sie die anderen drei. Sie können dies programmgesteuert tun, aber es hängt davon ab, wo sich die Chunks ursprünglich befinden, wie Sie Ihre Shards benannt haben usw., also lasse ich dieses Handbuch vorerst stehen, es ist nicht zu umständlich – nur 3 moveChunk Befehle:

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 }          

Lassen Sie uns noch einmal nachsehen und sicherstellen, dass die Chunks dort sind, wo wir sie erwarten:

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)  

Das passt zu unseren oben vorgeschlagenen Bereichen, also sieht alles gut aus. Führen Sie nun die ursprüngliche Schleife oben aus, um sie auf jedem Shard „an Ort und Stelle“ aufzuteilen, und wir sollten eine ausgewogene Verteilung haben, sobald die Schleife beendet ist. Noch ein sh.status() sollte Dinge bestätigen:

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    

Und da haben Sie es - kein Warten auf den Balancer, die Verteilung ist bereits gleichmäßig.