Bij het bijwerken zoals u nu bent, moet u de inhoud van het document ophalen om het te inspecteren en dergelijke wijzigingen aan te brengen. MongoDB heeft geen atomaire bewerkingen die op bestaande waarden inwerken op de manier die u wilt, dus iteratie is natuurlijk vereist.
Er is geen echt verschil in het "query"-gedeelte van hoe u overeenkomt met de reguliere expressie tussen uw twee versies van de verklaring. Wat er ook gebeurt, de inhoud wordt sowieso naar BSON geconverteerd voordat het naar de server wordt verzonden, dus als je een standaard expressiebuilder of een direct BSON-document gebruikt, heeft dat weinig zin.
Maar nu over naar de prestatieverbeteringen die kunnen worden aangebracht.
Gebruik bulkbewerkingen om te updaten
Zoals vermeld, zijn bulkbewerkingen de manier waarop u een dergelijke lijstiteratie moet bijwerken, en u "zou" ook een cursor moeten gebruiken in plaats van alle resultaten naar een lijst te converteren, omdat dit geheugen bespaart.
Alle specifieke typedeclaraties vermijden en gewoon weergeven als BsonDocument
(wat u waarschijnlijk op rangeren zal besparen, maar niet nodig is) dan zou het basisvoorbeeldproces zijn:
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);
}
}
Dus in plaats van een verzoek aan de database te doen voor elk afzonderlijk document dat uit de query wordt opgehaald, maakt u een List
van WriteModel
operaties in plaats daarvan.
Zodra deze lijst is uitgegroeid tot een redelijke waarde (1000 in dit voorbeeld), voert u de schrijfbewerking door naar de server in een enkel verzoek en antwoord voor alle batchbewerkingen. Hier gebruiken we BulkWriteAsync
.
U kunt de batches in een grootte van meer dan 1000 maken als u wilt, maar over het algemeen is dit een redelijk aantal om mee om te gaan. De enige echte harde limiet is de BSON-limiet van 16 MB, die aangezien alle verzoeken nog steeds daadwerkelijk BSON-documenten zijn, dit nog steeds van toepassing is. Hoe dan ook, er zijn veel verzoeken nodig om 16 MB te benaderen, maar er is ook een impedantie-overeenkomst om te overwegen in hoe het verzoek zal worden verwerkt wanneer het de server daadwerkelijk bereikt, zoals gedocumenteerd:
"Elke groep bewerkingen kan maximaal 1000 bewerkingen hebben. Als een groep deze limiet overschrijdt, verdeelt MongoDB de groep in kleinere groepen van 1000 of minder. Als de lijst met bulkbewerkingen bijvoorbeeld uit 2000 invoegbewerkingen bestaat, zal MongoDB creëert 2 groepen, elk met 1000 bewerkingen."
Door de verzoekgrootte op hetzelfde niveau te houden als hoe de server het zal verwerken, profiteert u dus ook van de yield
waar "meerdere batches" in feite kunnen werken in parallelle verbindingen met de server, in plaats van de server het splitsen en in de wachtrij plaatsen te laten doen.
Het geretourneerde resultaat is van BulkWriteResult
die informatie zal bevatten over het aantal "overeenkomsten" en "wijzigingen" enz. van de reeks verzonden bewerkingen.
Aangezien de bewerkingen zich in "batches" bevinden, is het natuurlijk logisch om aan het einde van de lus-iteratie te controleren of er nog meer "batch"-bewerkingen in de lijst staan, en deze vervolgens op dezelfde manier in te dienen.
Let ook op de IsOrdered = false
als BulkWriteOptions
betekent dat de batch bewerkingen niet daadwerkelijk in seriële volgorde wordt uitgevoerd, wat betekent dat de server de taken in feite "parallel" kan uitvoeren. Dit kan "enorme" snelheidsverbeteringen opleveren waar de volgorde van inzet niet vereist is. De standaard is om "besteld" en serieel in te dienen.
Dit is niet vereist om deze optie in te stellen, maar als uw bestelling niet belangrijk is (wat in dit geval niet het geval zou moeten zijn omdat geen andere bewerkingsverzoeken hier afhankelijk zijn van de vorige wijziging van een document), dan is de verbetering die u krijgt de moeite waard.
Waar het hier allemaal om draait, is het "verminderen" van het aantal daadwerkelijke verzoeken aan de server. Het verzenden van updates en het wachten op een reactie kost tijd, en bij grote operaties is dit een zeer kostbare aangelegenheid. Dat is waar Bulkbewerkingen voor bedoeld zijn, door meerdere bewerkingen binnen één verzoek toe te passen.
Het verminderen van die overhead is een "enorme" prestatiewinst. Daarom gebruik je dit.