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

Wie kann die Leistung des Aktualisierungsvorgangs in Mongo gesteigert werden?

Wenn Sie auf Ihre Weise aktualisieren, müssen Sie den Dokumentinhalt abrufen, um ihn zu überprüfen und solche Änderungen vorzunehmen. MongoDB hat keine atomaren Operationen, die auf vorhandene Werte so wirken, wie Sie es möchten, daher ist natürlich eine Iteration erforderlich.

Es gibt keinen wirklichen Unterschied im "Abfrage"-Teil, wie Sie den regulären Ausdruck zwischen Ihren beiden Versionen der Anweisung abgleichen. In jedem Fall wird der Inhalt in BSON konvertiert, bevor er an den Server gesendet wird. Ob Sie also einen Standard-Ausdrucksgenerator oder ein direktes BSON-Dokument verwenden, spielt keine Rolle.

Aber nun zu den Leistungsverbesserungen, die vorgenommen werden können.

Verwenden Sie Massenvorgänge zum Aktualisieren

Wie bereits erwähnt, sind Massenoperationen die Art und Weise, wie Sie bei einer solchen Listeniteration aktualisieren sollten, und Sie "sollten" auch einen Cursor verwenden, anstatt alle Ergebnisse in eine Liste umzuwandeln, da dies Speicherplatz spart.

Alle spezifischen Typdeklarationen vermeiden und nur als BsonDocument darstellen (was Ihnen wahrscheinlich das Marshalling ersparen wird, aber nicht erforderlich ist), dann wäre der grundlegende Beispielprozess:

var pattern = @"(?si)<([^\s<]*workUnit[^\s<]*)>.*?</\1>";
var filter = Builders<JobInfoRecord>.Filter.Regex(x => x.SerializedBackgroundJobInfo,
                                              new BsonRegularExpression(pattern, "i"));


var ops = new List<WriteModel<BsonDocument>>();
var writeOptions = new BulkWriteOptions() { IsOrdered = false };

using ( var cursor = await records.FindAsync<BsonDocument>(filter))
{
    while ( await cursor.MoveNextAsync())
    {
        foreach( var doc in cursor.Current )
        {
            // Replace inspected value
            var updatedJobInfo = Regex.Replace(doc.SerializedBackgroundJobInfo, pattern, "<$1></$1>");

            // Add WriteModel to list
            ops.Add(
                new UpdateOneModel<BsonDocument>(
                    Builders<BsonDocument>.Filter.Eq("JobTypeValue", doc.JobTypeValue),
                    Builders<BsonDocument>.Update.Set("SerializedBackgroundJobInfo", updatedJobInfo)
                )
            );

            // Execute once in every 1000 and clear list
            if (ops.Count == 1000)
            {
                BulkWriteResult<BsonDocument> result = await records.BulkWriteAsync(ops,writeOptions);
                ops = new List<WriteModel<BsonDocument>>();
            }
        }
    }

    // Clear any remaining
    if (ops.Count > 0 )
    {
        BulkWriteResult<BsonDocument> result = await records.BulkWriteAsync(ops,writeOptions);
    }

}

Anstatt also für jedes einzelne aus der Abfrage abgerufene Dokument eine Anfrage an die Datenbank zu stellen, erstellen Sie eine List von WriteModel Operationen statt.

Sobald diese Liste auf einen angemessenen Wert ( 1000 in diesem Beispiel ) angewachsen ist, übergeben Sie den Schreibvorgang an den Server in einer einzigen Anfrage und Antwort für alle Batch-Vorgänge. Hier verwenden wir BulkWriteAsync .

Sie können die Stapel in einer Größe von mehr als 1000 erstellen, wenn Sie möchten, aber es ist im Allgemeinen eine vernünftige Anzahl, mit der Sie umgehen können. Die einzige wirklich harte Grenze ist die BSON-Grenze von 16 MB, die immer noch gilt, da alle Anfragen tatsächlich BSON-Dokumente sind. Wie auch immer, es braucht viele Anfragen, um sich 16 MB zu nähern, aber es gibt auch eine Impedanzübereinstimmung, die berücksichtigt werden muss, wie die Anfrage verarbeitet wird, wenn sie den Server tatsächlich erreicht, wie dokumentiert:

„Jede Gruppe von Vorgängen kann höchstens 1000 Vorgänge umfassen. Wenn eine Gruppe diese Grenze überschreitet, teilt MongoDB die Gruppe in kleinere Gruppen von 1000 oder weniger auf. Wenn die Liste der Massenvorgänge beispielsweise aus 2000 Einfügevorgängen besteht, wird MongoDB erstellt 2 Gruppen mit jeweils 1000 Operationen."

Wenn Sie also die Anfragegröße auf dem gleichen Niveau halten, wie der Server sie verarbeiten wird, profitieren Sie auch vom yield wobei "mehrere Batches" tatsächlich in parallelen Verbindungen zum Server agieren können, anstatt den Server das Aufteilen und Einreihen in die Warteschlange zu überlassen.

Das zurückgegebene Ergebnis ist BulkWriteResult die Informationen über die Anzahl der "Übereinstimmungen" und "Änderungen" usw. aus dem gesendeten Operationsstapel enthält.

Da sich die Operationen natürlich in "Batches" befinden, ist es sinnvoll, am Ende der Schleifeniteration zu prüfen, ob noch weitere "Batch"-Operationen in der Liste vorhanden sind, und dann natürlich auf die gleiche Weise zu übermitteln.

Beachten Sie auch IsOrdered = false als BulkWriteOptions bedeutet, dass der Stapel von Operationen nicht tatsächlich in serieller Reihenfolge ausgeführt wird, was bedeutet, dass der Server die Aufgaben tatsächlich "parallel" ausführen kann. Dies kann "große" Geschwindigkeitsverbesserungen bewirken, wenn die Reihenfolge der Verpflichtung nicht erforderlich ist. Standardmäßig werden "bestellt" und seriell gesendet.

Dies ist nicht erforderlich, um diese Option einzustellen, aber wenn Ihre Bestellung nicht wichtig ist (was in diesem Fall nicht der Fall sein sollte, da hier keine anderen Operationsanforderungen von der vorherigen Änderung eines Dokuments abhängen), dann lohnt sich die Verbesserung, die Sie erhalten.

Es geht darum, die Anzahl der tatsächlichen Anfragen an den Server zu "reduzieren". Das Senden von Aktualisierungen und das Warten auf eine Antwort nimmt Zeit in Anspruch und ist bei großen Operationen eine sehr kostspielige Übung. Das ist es, womit Massenoperationen fertig werden sollen, indem mehrere Operationen innerhalb einer Anfrage angewendet werden.

Die Reduzierung dieses Overheads ist ein "riesiger" Leistungsgewinn. Deshalb verwenden Sie dies.