sql >> Base de Datos >  >> NoSQL >> MongoDB

¿Cómo aumentar el rendimiento de la operación de actualización en Mongo?

Al actualizar de la forma en que lo está, debe recuperar el contenido del documento para inspeccionarlo y realizar dichas modificaciones. MongoDB no tiene operaciones atómicas que actúen sobre los valores existentes de la manera que desea hacerlo, por lo que, por supuesto, se requiere iteración.

No hay una diferencia real en la parte de "consulta" de cómo está haciendo coincidir la expresión regular entre sus dos versiones de la declaración. Pase lo que pase, el contenido se convierte a BSON antes de enviarlo al servidor de todos modos, por lo que si usa un generador de expresiones estándar o un documento BSON directo tiene poca importancia.

Pero pasemos a las mejoras de rendimiento que se pueden realizar.

Usar operaciones masivas para actualizar

Como se indicó, las operaciones masivas son la forma en que debe actualizar dicha iteración de lista, y también "debería" usar un cursor en lugar de convertir todos los resultados en una lista, ya que se guardará en la memoria.

Evitando todas las declaraciones de tipos específicos y simplemente representándolos como BsonDocument (lo que probablemente le ahorrará la clasificación, pero no es necesario), entonces el proceso de ejemplo básico sería:

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

}

Entonces, en lugar de realizar una solicitud a la base de datos para cada documento individual recuperado de la consulta, crea una List de WriteModel operaciones en su lugar.

Una vez que esta lista ha crecido a un valor razonable (1000 en este ejemplo), envía la operación de escritura al servidor en una sola solicitud y respuesta para todas las operaciones por lotes. Aquí usamos BulkWriteAsync .

Puede crear los lotes en un tamaño superior a 1000 si lo desea, pero generalmente es un número razonable para manejar. El único límite estricto real es el límite BSON de 16 MB, que, dado que todas las solicitudes siguen siendo documentos BSON, esto aún se aplica. De todos modos, se necesitan muchas solicitudes para acercarse a los 16 MB, pero también hay una coincidencia de impedancia a considerar en cómo se procesará la solicitud cuando realmente llegue al servidor, como se documenta:

"Cada grupo de operaciones puede tener como máximo 1000 operaciones. Si un grupo supera este límite, MongoDB dividirá el grupo en grupos más pequeños de 1000 o menos. Por ejemplo, si la lista de operaciones masivas consta de 2000 operaciones de inserción, MongoDB crea 2 grupos, cada uno con 1000 operaciones."

Por lo tanto, al mantener el tamaño de la solicitud en el mismo nivel en que el servidor la procesará, también obtendrá el beneficio del yield donde "múltiples lotes" pueden actuar de hecho en conexiones paralelas con el servidor, en lugar de dejar que el servidor haga la división y la cola.

El resultado devuelto es de BulkWriteResult que contendrá información sobre el número de "coincidencias", "modificaciones", etc. del lote de operaciones enviadas.

Naturalmente, dado que las operaciones están en "lotes", tiene sentido verificar al final de la iteración del bucle para ver si existen más operaciones "en lotes" en la lista y luego, por supuesto, enviarlas de la misma manera.

También notando el IsOrdered = false como BulkWriteOptions significa que el lote de operaciones en realidad no se ejecuta en orden de serie, lo que significa que el servidor puede ejecutar las tareas en "paralelo". Esto puede generar mejoras de velocidad "enormes" donde no se requiere el orden de compromiso. El valor predeterminado es enviar "pedido" y en serie.

Esto no es necesario para establecer esta opción, pero si su pedido no es importante (que no debería serlo en este caso, ya que ninguna otra solicitud de operación aquí depende de la modificación anterior de un documento), entonces la mejora que obtiene vale la pena.

De lo que se trata es de "reducir" la cantidad de solicitudes reales realizadas al servidor. Enviar actualizaciones y esperar una respuesta lleva tiempo, y en operaciones grandes es un ejercicio muy costoso. Eso es lo que se supone que deben tratar las operaciones masivas, mediante la aplicación de varias operaciones dentro de una sola solicitud.

Reducir esa sobrecarga es una ganancia de rendimiento "enorme". Por eso usas esto.