Aprendiendo MongoDB-21-31
Aprendiendo MongoDB-21-31
Aprendiendo MongoDB-21-31
En el lado positivo, los drivers existen para un gran número de idiomas, el protocolo es moderno y simple, y el desarrollo
está sucediendo a una velocidad rapida. MongoDB esta en producción en suficientes compañías que, preocupaciones
por madurez, aunque validas, están convirtiendo rapidamente en algo del pasado.
En este Capitulo
El mensaje de este capítulo es que MongoDB, en la mayoría de los casos, puede sustituir a una base de datos relacional.
Es mucho más simple y directo, es más rápido y por lo general impone menos restricciones a los desarrolladores de
aplicaciones. La falta de transacciones puede ser una preocupación legítima y seria. Sin embargo, cuando la gente
pregunta * ¿dónde esta MongoDB con respecto al nuevo paisaje de almacenamiento de datos? * la respuesta es simple:
**justo en el centro **.
20
Capitulo 6 - MapReduce
MapReduce es un enfoque al procesamiento de datos que tiene dos grandes beneficios sobre los modelos tradicionales.
La primera y principal razón es que fue desarrollado enfocado en el rendimiento. En teoria MapReduce puede correr
en paralelo permitiendonos así procesar largos conjuntos de datos utilizando muchos procesadores o maquinas. Como
ya mencionamos, esto no es algo que MongoDB puede hacer actualmente. El segundo beneficio de MapReduce es que
uno escribe código para procesar la información. Comparado con lo que se hiciera en SQL, MapReduce es mucho mas
rico y te deja aprovechar las ventajas del lenguaje de programación que estes usando.
MapReduce es un patron que ha crecido en popularidad y que puedes usar en casi todos los lenguajes como: C#, Ruby,
Java, Phyton, etc. Quiero advertirte que esto sonara muy complicado y diferente la primera vez que lo uses pero no te
preocupes. Toma tu tiempo y juega con MapReduce. Vale la pena aprenderlo así estes usando MongoDB o no.
MapReduce se divide en dos pasos. El primero lo llamamos “MAP” o Mapa en español y el segundo “Reduce” o reducir
en español. El proceso de crear un mapa transforma los documentos que le enviamos y emite un par de una clave y
un valor o (key => value pair). El paso de reducir toma las claves y el arreglo de valores emitidos para esas claves y
produce el resultado final. Expliquemos un poco mas:
El ejemplo que usaremos sera generar un reporte del numero de hits por dia que tenemos en una pagina. Esto es el
“Hola Mundo” del MapReduce. Para esto vamos a usar una colección de ‘hits’ con dos campos: recursos y fecha. El
resultado que esperamos esta dividido por ‘recurso’, ‘año’, ‘mes’, ‘dia’, ‘veces’
recurso fecha
index Jan 20 2010 4:30
index Jan 20 2010 5:30
about Jan 20 2010 6:00
index Jan 20 2010 7:00
about Jan 21 2010 8:00
about Jan 21 2010 8:30
index Jan 21 2010 8:30
about Jan 21 2010 9:00
index Jan 21 2010 9:30
index Jan 22 2010 5:00
21
(Lo bueno acerca este tipo de enfoque es que al ordenar el resultado, los reportes son rápidos de generar y el crecimiento
de la información esta controlado ya que solo usamos un documento de mongo por cada recurso que queramos rastrear)
Por ahora solo nos enfocaremos en entender los conceptos. Al final de este capitulo te proporcionaremos toda la data
que usamos y el código para que juegues por ti mismo.
Lo primero que haremos es ver la funciona ‘Map’ o Mapa. El objetivo de esta función es que retorne un valor que
pueda ser reducido. Es posible que emita 0 o mas veces. En nuestro caso, siempre emitira una sola vez (muy común).
Imagina que Map es un bucle a través de todos los hits. Por cada documento queremos generar, una clave con un
recurso, año, mes, dia y valor.
function () {
var key = {
resource : this . resource ,
year : this . date . getFullYear () ,
month : this . date . getMonth () ,
day : this . date . getDate ()
};
emit ( key , { count : 1}) ;
}
this se refiere al documento que estamos procesando actualmente. Vamos a ver el resultado de esta función para
entender un poco mas esto. Estamos usando la información que tenemos dos párrafos mas arriba. El resultado de la
función Map seria:
{ resource : ’ index ’ , year : 2010 , month : 0 , day : 20} => [{ count : 1} , { count : 1} , {
count :1}]
{ resource : ’ about ’ , year : 2010 , month : 0 , day : 20} => [{ count : 1}]
{ resource : ’ about ’ , year : 2010 , month : 0 , day : 21} => [{ count : 1} , { count : 1} , {
count :1}]
{ resource : ’ index ’ , year : 2010 , month : 0 , day : 21} => [{ count : 1} , { count : 1}]
{ resource : ’ index ’ , year : 2010 , month : 0 , day : 22} => [{ count : 1}]
El entendimiento de este paso intermediario es esencial para el entendimiento de MapReduce. Los valores que emite es-
tán agrupados como arreglos por clave. Los desarrollares de .NET y Java pueden pensar de esto como un: IDictionary
<object, IList<object>> (.NET) o HashMap<Object, ArrayList> (Java).
function () {
var key = { resource : this . resource , year : this . date . getFullYear () , month :
this . date . getMonth () , day : this . date . getDate () };
if ( this . resource == ’ index ’ && this . date . getHours () == 4) {
emit ( key , { count : 5}) ;
} else {
emit ( key , { count : 1}) ;
}
22
}
{ resource : ’ index ’ , year : 2010 , month : 0 , day : 20} => [{ count : 5} , { count : 1} , {
count :1}]
Observa como cada emit genera un nuevo valor agrupado por nueva clave.
La función reduce o Reducir en español toma cada uno de estos valores intermedios y emite un resultado final. Nuestra
función para reducir es:
_id : { resource : ’ home ’ , year : 2010 , month : 0 , day : 20} , value : { count : 3}
Si has estado prestando atención te preguntaras: ¿Por que no simplemente usamos sum = values.lenght? Esto
parece un enfoque eficiente cuando estas sumando arreglos de 1. La verdad es que en pocas ocasiones llamamos a la
función Reduce con un set perfecto. Por ejemplo en vez de ser llamado con:
{ resource : ’ home ’ , year : 2010 , month : 0 , day : 20} => [{ count : 1} , { count : 1} , {
count :1}]
{ resource : ’ home ’ , year : 2010 , month : 0 , day : 20} => [{ count : 1} , { count : 1}]
{ resource : ’ home ’ , year : 2010 , month : 0 , day : 20} => [{ count : 2} , { count : 1}]
El resultado final es el mismo (3) pero el camino tomado para llegar a el es diferente. Por lo tanto la función reduce
siempre debe ser indempotente. Esto es que la función debería dar el mismo resultado así se llame una sola vez o
varias veces.
Es muy común que encadenemos varias funciones Reduce cuando hacemos análisis complejos.
23
Pura Practica
Con MongoDB usamos el comando mapReduce en una colección. mapReduce toma una función para realizar el mapeo,
una funciona para realizar la reducción y una directiva para el resultado. En nuestro terminal podemos pasarle una
función de Javascript. En muchas librerías le daremos un string con nuestras funciones. Primero vamos a crear
nuestro set de data para analizar:
Ahora podemos crear nuestras funciones Map y Reduce. El terminal de MongoDB acepta comandos multi linea y veras
.. después de pulsar enter para indicar que esperamos mas texto.
Corriendo este comando podremos ver el resultado deseado. Cambiando out a inline significa que el resultado de
mapReduce es inmediatamente transmitido a nosotros. Esto esta actualmente limitado a resultados que son menores de
16 megabytes. Pudiéramos especificar {out: ’hit_stats’} y tener el resultado guardado en la colección hit_stats
24
Cuando realizamos esta acción perdemos toda la información almacenada en hit_stats. Si hiciéramos {out: {merge:
’hi_stats’}} las claves existentes serán remplazadas cuando los nuevos valores y lass nuevas llaves sera insertadas
como documentos. Finalmente podemos out usando una función reduce para manejar casos mas avanzados.
El tercer parametro toma algunas opciones adicionales. Pudiéramos filtrar, ordenar y limitar los documentos que
queremos analizar. Tambien podemos especificar una función finalize que sera ejecutada contra el resultado después
de la función reduce
En Este Capitulo
Este es el primer capitulo donde cubrimos algo diferente. Si te hizo sentir incomodo, recuerda que siempre puedes usar
otras funciones de MongoDB como la agregación, para escenarios mas sencillos. Al final ten en cuanta que MapReduce
es una de las mejores funcionalidad de MongoDB. La clave para entender como escribir las funciones de Map y Reduce
es visualizar y entender la manera como la data intermedia lucirá al salir del map y entrando al reduce
25
Capitulo 7 - Rendimiento y Herramientas
En este capitulo, veremos algunos tópicos sobre rendimiento así como algunas de las herramientas disponibles para
los desarrolladores de MongoDB. No nos adentraremos mucho en estos tópicos, pero examinaremos los aspectos mas
importantes de cada uno.
Indices
Al principio vimos los indices especiales de colecciones system.indexes que contienen información de todos los indices
de nuestra base de datos. Los indices en MongoDB trabajan muy parecido a los indices en una base de datos relacional:
ayudan a mejorar el rendimiento de las queries y ordenado. Los indices son creados via ensureIndex
Un indice unico puede ser creado pasandole un segundo parametro y configurando unique a true:
Los indices pueden ser creados en campos embebidos (de nuevo, usando la notación de punto) y en campos de arreglos.
Tambien podemos crear indices compuestos:
El orden de los indices (1 para ascendiente, -1 para descendiente) no importa para un unico indice, pero puede tener
un impacto por composición de indices cuando estas ordenando o usando condiciones de rango.
Explain
Para ver si tus queries están usando indices o no, puedes usar el metodo explain en un cursor:
La salida nos dice que un BasicCursor fue usado (lo que implica no indexado), 12 objetos fueron escaneados, cuanto
tiempo tomo, que indice, si alguno fue usado así como algunas otras piezas utiles de información.
Si cambiamos nuestro query para que use un indice, veremos que BtreeCursor fue usado, así como el index usado
para realizar el request:
26
Escrituras Asíncronas
Anteriormente mencionamos que, por defecto, las escrituras en MongoDB son asíncronas. Esto puede resultar en
ganancias de rendimiento al costo de perder datos durante un crash. Un efecto secundario de las escrituras asíncronas
es que un error no es retornado cuando una inserción o una actualización viola una restricción unica. Para ser notificado
acerca de una escritura fallida, debes llamar db.getLastError() después del ultimo error. Muchos drivers abstraen
este detalle y proveen una manera de hacer una escritura segura - usualmente via un parametro extra.
Desafortunadamente, la terminal hace automaticamente Escrituras seguras, para que veamos este comportamiento
facilmente en acción.
Sharding
MongoDB soporta auto-sharding. Sharding es un acercamiento hacia la escalabilidad, que separa tu datos a traves
de multiples servidores. Una implementación nativa podría colocar todos los datos para usuarios con un nombre que
comienza con A-M en el servidor 1 y el resto en el servidor 2. Las capacidades de MongoDB de sharding exceden tan
simple algoritmo. el sharding es un topico que esta fuera del alcance de este libro, pero debes saber que existe y que
debes considerarlo si tus necesidades se aplian hasta usar mas de un servidor.
Replicaicones
La replicaron en MongoDB trabajaba similarmente a como o hacen las bases de datos relacionales. Las escrituras son
enviadas a un solo servidor, el servidor master, el cual luego sincroniza con sigo o con los demás servers, los esclavos.
Puedes controlar si las lecturas suceden en el server esclavo o no, lo que puede ayudar a distribuir la carga al momento
de leer los datos. Si el servidor master deja de responder, un servidor esclavo puede ser promovido para actuar como
el nuevo master. De nuevo, la replicacion en MongoDB esta fuera del alcance de este libro.
Mientras la replicacion puede mejorar el desempeño (distribuyendo escrituras), su proposito principal es incrementar
la confiabilidad. Combinando la replicacion con el sharding es un proposito comun. Por ejemplo, cada shard puede
estar hecho de un servidor master y un esclavo. (Tecnicamente tambien necesitaras un arbitro en el caso de que dos
servidores quieran ser masters. Pero un arbitro requiere solo pocos recursos y puede ser usado y puede ser usado por
multiples shards)
Estadísticas
Puedes obtener estadísticas en una base de datos escribiendo db.stats(). La mayoría de la información trata con el
tamaño de tu base de datos. Tambien puedes obtener estadísticas de una colección, digamos unicorns, escribiendo
db.unicorns.stats(). De nuevo, la mayoría de esta información se relaciona al tamaño de la colección.
Interfaz Web
Incluida en la información mostrada al iniciarse MongoDB estaba un link a una herramienta de administración web
(podrás verla si haces scroll en tu terminal hasta el punto donde iniciaste mongodb). Puedes acceder a esto, abriendo
27
tu navegador en http://localhost:28017/. Para obtener el mayor resultado de el, deberás agregar rest=true a tu
configuración y reiniciar el proceso mongod. La interfaz web te da una gran cantidad de conocimiento acerca del estado
actual del server.
Profiler
db . setProfilingLevel (2) ;
La salida nos dice que fue lo que se ejecuto y cuando, cuantos documentos fueron escaneados, y cuantos fueron
retornados.
Puedes desabilitar el profiler LLAMANDO setProfileLevel de nuevo per cambiando el argumento a 0. Otra opción es
especificar 1 que solo le hará profile a los queries que tomen mas de 100 milisegundos. O, puedes especificar el tiempo
minimo, en milisegundos, con un segundo parametro:
Respaldos y Restauraciones
En la carpeta bin de MongoDB se encuentra un ejecutable mongodump. Simplemente ejecutando mongodump se conectara
a la base de datos y hará un respaldo de todas tus bases de datos a una sub carpeta dump. Puedes ejecutar mongodump
--help para ver opciones adicionales. Las opciones comunes son --db DBNAME para respaldar una base de datos
especifica y --collection COLLECTIONAME para respaldar una colección especifica.. Luego puedes usar el ejecutable
mongorestore, localizado en la carpeta bin, para restaurar un respaldo hecho previamente. De nuevo, los tags --db
y --collection pueden ser especificados para restaurar una base de datos o una colección especifica.
Por ejemplo, para respaldar nuestra colección learn a una carpeta backup, ejecutamos (esto es su mismo ejecutable
que puedes correr en una ventana de tu terminal, no dentro de la consola misma de mongo):
Vale resaltar que mongoexport y mongoimport son otros dos ejecutables que pueden ser usados para exportar e importar
datos desde JSON o CSV. Por ejemplo, podemos obtener una salida en JSON haciendo:
28
mongoexport -- db learn - collection unicorns
Nota que mongoexport y mongoimport no siempre podrá representar tus datos. Solo mongodump y mongorestore debería
ser usado para respaldos en si.
En este capitulo
En este capitulo vimos varios comandos, herramientas y detalles de rendimiento al usar MongoDB. No hemos tocado
todo, pero hemos visto los mas comunes. El indexado en MongoDB es similar al indexado en las bases de datos
relacionales, así como muchas de las herramientas. Sin embargo, con MongoDB, muchas de estas son justas y simples
de usar.
29
Conclusion
Debes tener suficiente información para comenzar a usar MongoDB en un proyecto real. Hay mas sobre MongoDB de
lo que se ha cubierto, pero tu próxima prioridad debe ser juntar lo que has aprendido, y familiarizarte con el driver que
usaras. El sitio de MongoDB tiene mucha información útil. El grupo oficial de MongoDB es un buen lugar para aclarar
tus dudas.
NoSQL nació no solo como una necesidad, si no tambien como un interes de probar nuevas formas de acercamiento.
Es de conocimiento que nuestro campo esta avanzando y que si no probamos cosas nuevas, y fallamos, nunca triun-
faremos. Esto es, a mi parecer, una buena forma de llevar nuestras vidas profesionales.
30