Tag Archives: MongoDB

Utilizar MongoDB como sistema de caché

Prácticamente cualquier servicio online con un mínimo de tráfico y de complejidad requiere de un sistema de cache para aligerar su carga y reducir el tiempo de respuesta de ciertas peticiones, que deben ser aquellas peticiones más comunes y aquellas más costosas en cuanto a recursos que consumen.

En este punto siempre surge la incógnita sobre que sistema de caché utilizar, y factores como el tamaño y la estructura de los datos, o la velocidad de lectura y escritura son variables que debemos tener en cuenta a la hora de elegir uno.

Una muy buena opción desde mi punto de vista es utilizar Mongo para cachear datos. La idea es que tienes una colección por cada tipo de información que quieres cachear:

db.micache = {  
   _id:String,
   datos: { 
      ... todo lo que queremos cachear ...
   },
   fecha:Date
}

Donde:

  • El _id es de tipo String y es una cadena de caracteres en cuya generación participan todas las variables que influyen en el resultado a cachear. Es decir, si por ejemplo queremos cachear los contactos de un usuario, el _id debe ser generado con el identificador del usuario, algo del estilo “contacts_007”. Otro ejemplo sería si quisiéramos cachear los contactos compartidos por dos usuarios diferentes cualesquiera, en este caso generaríamos algo como “shared_007_x_199”, que almacenaría datos diferentes a otra entrada que podría ser “shared_007_x_203”.
  • En datos guardaremos lo que queramos cachear, sea del tipo que sea, aprovechando la flexibilidad de Mongo que nos va a permitir almacenar cualquier cosa sin tener que definir ningún esquema previo. Además otra de las ventajas de Mongo es que los datos guardados pueden ser del tamaño que queramos, sin importar lo grandes que sean.
  • Por último en fecha se almacenará la fecha de creación del objeto cacheado, y además crearemos un índice con tiempo de expiración, para que sea el propio Mongo automáticamente el que borre de caché los objetos que hayan cumplido el tiempo de expiración. En la siguiente línea decimos que cualquier documento guardado expirará a los 14.400 segundos, es decir, estamos creando una cache de 4 horas.
db.micache.createIndex( { "fecha": 1 }, { expireAfterSeconds: 14400 } )

 

Las ventajas que aporta esta solución son:

  • Es extremadamente flexible en cuanto a la estructura de datos a guardar. Texto, números, mapas, listas, cualquier tipo de estructura puede guardarse directamente en Mongo, y será aquel que lo guarde y recupere el que le sepa como manejarlo.
  • Disponemos de una capacidad de almacenamiento muy alto, no limitado a la memoria RAM que tengamos disponible. El disco es más barato que la memoria, y nos va a permitir cachear sin tener que preocuparnos prácticamente por lo que consumamos.
  • Si estamos utilizando un Replica Set de Mongo, tendremos además un sistema de cache con alta disponibilidad y escalable, ya que podemos distribuir las lecturas entre los nodos secundarios. Además si añadimos un nuevo servidor de aplicaciones, o tenemos que reiniciar alguno, tendrá inmediatamente toda la información cacheada a su disposición, lo que no ocurriría si cada servidor de aplicaciones guarda su cache en su memoria local.

El inconveniente que le podemos atribuir es:

  • Al ser acceso a disco no será tan rápido como es el acceso a memoria, pero como siempre vamos a acceder por _id será suficientemente rápido para la mayoría de los casos requeridos.

Yo lo estoy utilizando en sistemas en producción con un excelente resultado.

Como optimizar procesos de inserción masiva en MongoDB

Mongo DB es un document storage NoSql que tiene una capacidad de escritura muy alta, y realmente la tiene, por mi experiencia mucho más que por ejemplo MySql. Pero cuando empiezas a cargarlo de verdad, con procesos de inserción masiva, del orden de millones de documentos, la cosa ya no va sola, y necesitas empezar a desarrollar de una forma determinada para conseguir que tus procesos sigan yendo a toda pastilla. Aquí te cuento algunos de los que he descubierto a base de leer en algunos casos, y de tortas en otros.

No gestionar las transacciones

Ni se te ocurra gestionar las transacciones en este tipo de procesos. Mongo tiene la virtud de poder “insertar y olvidar”, es decir, de enviar los datos a insertar al servidor, y no esperar ningún tipo de respuesta, ni de confirmación por su parte. Esto lo consigues especificando un WriteConcern.UNACKNOWLEDGED, que quiere decir eso, que no te interesa si la inserción ha ido bien o mal. Eso si, para utilizar esto, ya te tienes que haber preocupado previamente de asegurarte que tus inserciones no van a fallar, sino, mal lo tendrás para poder verificar lo que ha ido bien o mal.

Ejecuta un único save para cada documento, nunca update

El método save es mucho más rápido que el método update, ya que directamente escribe, sin verificar si previamente existe el registro o no. Nunca, repito, nunca desarrolles procesos de inserción masiva que quieras que vayan rápido y que no se degraden al crecer las colecciones en los que hagas uno o varios updates. Nuevamente, en este proceso, tendrás que preocuparte de tener a tu alcance toda la información que quieres guardar para cada documento, júntala durante N procesos e inserta una única vez.

Contra colecciones vacías

El tiempo de inserción en colecciones vacías en Mongo es mas bajo que cuando tiene datos. Plantéate en tu proceso si es posible al inicio del mismo borrar completamente la colección y volver a generarla de nuevo con los datos que vas a insertar. Aunque parezca mentira, en muchos casos puede ser más rápido hacer de nuevo una inserción completa que N parciales.

Contra colecciones sin índices

Si tus colecciones tienen índices, bórralos con dropIndex antes de empezar a insertar, ejecutas, y al finalizar vuelves a crear el indice con ensureIndex. El tiempo de creación del índice global es mucho menor que el tiempo que se tiene que invertir en actualizar el mismo índice ya creado en cada inserción de cada documento.

En colecciones más pequeñas

Trocea tus colecciones, tanto la inserción como la recuperación de datos van a ir más rápidas contra diez colecciones de 2 millones de documentos, que contra una única colección de 20 millones. Además, esto te permitirá paralelizar tus procesos y reducir la duración total.

Utiliza diferentes databases

Es incluso preferible utilizar diferentes databases para datos temporales o que vayas a regenerar por completo en ese ciclo, que diferentes colecciones en la misma base de datos. Esto te permitirá incluso hacer dropDatabase que liberará el espacio en disco que Mongo reserva para la db, cosa que no hace si eliminas la colección.

Y hasta aquí mis pequeños trucos para optimizar la inserción masiva en Mongo. Si tienes algún truco que no haya dicho, no dudes en comentármelo.