sql >> Databáze >  >> NoSQL >> MongoDB

Jak zvýšit výkon operace aktualizace v Mongo?

Při aktualizaci způsobem, jakým jste, musíte načíst obsah dokumentu, abyste jej mohli zkontrolovat a provést takové úpravy. MongoDB nemá žádné atomické operace, které působí na existující hodnoty tak, jak chcete, takže je samozřejmě vyžadována iterace.

Mezi vašimi dvěma verzemi příkazu není žádný skutečný rozdíl v části „dotaz“ v tom, jak odpovídáte regulárnímu výrazu. Bez ohledu na to je obsah před odesláním na server stejně převeden na BSON, takže pokud použijete standardní nástroj pro tvorbu výrazů nebo přímý dokument BSON, nemá to žádný význam.

Ale k vylepšením výkonu, která lze provést.

K aktualizaci použijte hromadné operace

Jak již bylo řečeno, hromadné operace jsou způsob, jakým byste měli při takové iteraci seznamu aktualizovat, a také byste „měli“ používat kurzor místo převodu všech výsledků na seznam, protože to šetří paměť.

Vyhýbejte se všem konkrétním deklaracím typu a zastupujte je pouze jako BsonDocument (což vám pravděpodobně ušetří seřazování, ale není to nutné), pak by základní příklad procesu byl:

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

}

Takže místo toho, abyste požadovali do databáze pro každý jednotlivý dokument získaný z dotazu, vytvoříte List z WriteModel operace.

Jakmile tento seznam naroste na rozumnou hodnotu (v tomto příkladu 1000), odešlete operaci zápisu na server v jediném požadavku a odpovědi pro všechny dávkové operace. Zde používáme BulkWriteAsync .

Pokud chcete, můžete vytvořit dávky o velikosti větší než 1000, ale obecně je to rozumný počet. Jediným skutečným pevným limitem je limit BSON 16 MB, který, protože všechny požadavky jsou stále ve skutečnosti dokumenty BSON, stále platí. Každopádně je potřeba hodně požadavků, aby se přiblížily 16 MB, ale je zde také potřeba vzít v úvahu impedanci, jak bude požadavek zpracován, až se skutečně dostane na server, jak je zdokumentováno:

"Každá skupina operací může mít maximálně 1000 operací. Pokud skupina překročí tento limit, MongoDB rozdělí skupinu na menší skupiny po 1000 nebo méně. Pokud například seznam hromadných operací obsahuje 2000 operací vložení, MongoDB vytvoří 2 skupiny, každou s 1000 operacemi."

Pokud tedy velikost požadavku udržíte na stejné úrovni, jak jej server zpracuje, získáte také výhodu z yield kde „více dávek“ může ve skutečnosti fungovat v paralelních připojeních k serveru, spíše než nechat server, aby rozděloval a zařazoval do fronty.

Vrácený výsledek je BulkWriteResult který bude obsahovat informace o počtu "shod" a "modifikace" atd. z dávky odeslaných operací.

Přirozeně, protože operace jsou v „dávkách“, má smysl na konci opakování smyčky zkontrolovat, zda v seznamu neexistují další „dávkové“ operace, a pak samozřejmě stejným způsobem odeslat.

Všimněte si také IsOrdered = false jako BulkWriteOptions znamená, že dávka operací se ve skutečnosti neprovádí v sériovém pořadí, což znamená, že server může ve skutečnosti spouštět taks "paralelně". To může vést k „obrovskému“ zlepšení rychlosti tam, kde není vyžadováno pořadí závazků. Výchozí nastavení je odeslat „objednané“ a sériově.

Toto není vyžadováno pro nastavení této možnosti, ale pokud vaše objednávka není důležitá (což by v tomto případě nemělo být, protože žádné další požadavky na operaci zde nezávisí na předchozí úpravě dokumentu), pak se vylepšení, které získáte, vyplatí.

Jde o „snížení“ počtu skutečných požadavků na server. Odesílání aktualizací a čekání na odpověď zabere čas a ve velkých provozech je to velmi nákladné cvičení. To je to, s čím se mají vypořádat hromadné operace, a to aplikací několika operací v rámci jednoho požadavku.

Snížení této režie je „obrovský“ nárůst výkonu. To je důvod, proč to používáte.




  1. Příkaz MongoDB dropIndexes

  2. MongoDB - Vytvořte sbírku

  3. Aktualizace změny licence MongoDB SSPL

  4. Srovnávací načítání z redis vs paměti v pythonu (pomocí timeit)