[NoSQL] Replace vs Modified arrays en MongoDb

Introducción

MongoDb es una base de datos documental (Orientadas a documentos) y forma parte de la familia de las bases de datos NoSQL. MondoDb almacena la información en documentos en vez de guardar los datos en tablas relacionales como lo hacen las base de datos relacionales. Estos documentos se almacenan en un formato propio de MongoDb que es BSON (JSON binario) lo que hace que el sistema de almacenamiento sea muy dinámico y no esté ligado a un esquema como en las bases de datos relacionales (se conoce como schema free).

Para trabajar con MongoDb existen una gran variedad de drivers. Yo voy usar el driver oficial de C# que puedes añadirlo a trus proyectos desde Nuget.

Bueno, vista una pequeñisima introducción teórica a que es MongoDb. Recomendaría para seguir el artículo, leer un poco acerca de arrays en MongoDb y documentos embebidos. Pues dicho esto, vamos a ver que diferencia hay entre las operaciones Replace y Modified y cual usar en cada caso.

Caso práctico

Debemos usar Replace siempre que nuestra prioridad no sea el rendimiento y la concurrencia y si modelar un dominio de aplicación y que este no esté acoplado a MongoDb (Ya veremos más adelante porque).

Disclaimer: Quizás no sea el mejor ejemplo del mundo y el más acertado pero es sufiente para ver la diferencia entre ambos comandos.

Por ejemplo, imaginad que estamos diseñando un blog para una pequeña empresa. En este caso contamos con que el número de comentarios que se harán sobre un post será muy pequeño (Tal vez 5 o 6 comentarios) puesto que esta empresa no es muy conocida y la temática será sobre la vida en dicha empresa y solo los clientes comentarán los artículos. En este caso hemos decidido modelar un dominio de aplicación para nuestro blog y los vamos a  usar en un controlador para añadir un comentario:

[HttpPost]

public ActionResult AddComment(string message, string id)

{

    var post = GetPost(id);

    var comment = new Comment(message);

    

    post.AddComment(comment);

    _blogContext.Posts.Save(post);

 

    return RedirectToAction("Details", new {id});

}

El método Save si no existe  el documento lo inserta y si existe lo reemplaza (http://docs.mongodb.org/manual/tutorial/modify-documents/). Como dijimos anteriormente el rendimiento no es nuestra prioridad y y sí diseñar un modelo no anémico. Pero, ¿Que pasa si usamos esta aproximación en un blog cuyas visitas e interacción de los usuarios se cuentan por miles? Pues que vamos a encontrarnos con un problema de rendimiento y/o concurrencia y vamos a ir viendo por qué.

Lo primero que vamos a hacer es ver que pasa cuando ejecutamos un Replace sobre un documento en el que hemos añadido un nuevo elemento a un array de documentos.

Voy a usar un cliente visual para administrar MongoDb, en mi caso MongoVue:

image

Como podmeos observar, tenemos una colección donde almacenamos los posts y dentro de cada post tenemos un array para almacenar los comentarios (Hay que vigilar cuantos comentarios vamos a insertar y su tamaño ya que el tamaño máximo de un documento en MongoDb es de 16Mb)

Vamos a activar el profiler de MongoDb sobre la base de datos de blog para ver que pasa cuando añadimos un nuevo comentario:

image

image

o desde la consola de MongoDb con el comando db.setProfilingLevel(2)

Cuando activamos el profiler se crea una colección llamada system.profile

image

Añadimos un nuevo comment y vamos a buscar la operación de update:

image

o desde la consola de MongoDb con el comando db.system.profile.find({ “op” : “update” }).limit(50)

Y vamos a estudiar el resultado:

image

Como podemos observar el campo updateobj contiene el objeto post que había antes pero ahora la colección de comentarios tiene 2, el anterior que había y el nuevo que hemos añadido, es decir que cada vez que añadamos un nuevo comentario hará un Replace de todo el documento añadiendo el nuevo comentario al array de documentos.

¿Pero esto impacta al rendimiento?

Pues si, a medida que el número de comentarios crezca, cada operación de Replace ira creciendo exponencialmente. Como decía anteriormente, esto no está mal, es un comportamiento estandard de este tipo de operación, pero si buscas rendimiento esta no es tu mejor opción y vamos a ver porque.

Vamos a ejecutar una prueba para añadir 1000, 2500 y 5000 comentarios a un post y vamos a medir tiempos, ok?

image

Como podéis observar Replace es bastante más lento que Modify. Un crece de manera exponencial mientras que la otra crece de menera más o menos lineal.

¿Entonces que hace Modify con respecto a Replace?

Lo primero es mostrar la diferencia en el código:

[HttpPost]

public ActionResult AddComment(string message, string id)

{

    var comment = new Comment { Message = message };

    var update = Update<Post>.Push(p => p.Comments, comment);

    _blogContext.Posts.Update(Query.EQ("_id", ObjectId.Parse(id)), update);

 

    return RedirectToAction("Details", new {id});

}

Como podemos observar, en este caso hacemos uso de la clase Update, para hacer un push a la colección de comments. Si este código lo metemos dentro de la entidad Post estaremos acoplando nuestro modelo a MongoDb, de ahí que Replace encaja mejor en ese tipo de escenarios, pero como decíamos, si buscamos rendimiento y/o concurrencia, Modify es nuestra solución.

Si ejecutamos el profiler y añadimos un comentario con Modify:

image

¿Veís la diferencia? Ahora solo se añade un comentario al array de documentos pero no se reemplaza todo el post como ocurría con Replace, por lo tanto es una operación menos costosa.

Conclusión

Si lo que te importa es el rendimiento y la concurrencia, olvidate de modelar tu dominio y usa directamente Modify para operaciones sobre arrays, para todo lo demás Replace.

Un saludo.

Un comentario en “[NoSQL] Replace vs Modified arrays en MongoDb”

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *