martes, 30 de noviembre de 2010

De huevos, gallinas y propietarios con miopía

Me gustaría compartir con vosotros, una parábola muy bien redactada y enfocada por Michel Henric-Coll, creador de Fractal Teams.  (Fuente: http://blog.fractalteams.com/?p=299)  ¿A alguno le resulta familiar?

huevo
Érase una vez una granja que poseía 50 gallinas. Eran gallinas ponedoras que ponían una media regular de cuatro huevos al día, lo que permitiera que el feliz granjero vendiera en el mercado a diario 200 huevos.
Pero el propietario de la granja quería sacar más beneficios, así que emprendió una cruzada contra el encargado para que consiguiera mayor productividad de las gallinas.

Eran una gallinas bastante felices. Hasta escuchaban música por los altavoces repartidos en la granja. Pero debido a la presión que el propietario aplicaba al granjero para rebajar costes, este vendió el equipo de música y canceló la suscripción a Spotify. Ansioso, en lugar de visitar a las gallinas una vez al día, empezó a visitarlas cada dos horas y a recoger el huevo que habían puesto ya. Pero todo este movimiento perturbaba a las gallinas y como consecuencia del estrés, bajaron su producción en un 25%.

Fueron sólo 150 huevos los que pudieron venderse en el mercado. El propietario se puso furioso exigiendo mayor rendimiento con amenazas de despido. Entonces el granjero apretujado cogió una gallina, la mató y la puso en venta en el mercado junto a los 150 huevos del día. Como una gallina valía como 50 huevos, el propietario estuvo relativamente satisfecho con los ingresos. Una gallina menos representaba un ahorro en comida para animales y esto era muy bueno. Sin embargo, al día siguiente, sólo habían 146 huevos. Preocupado, el granjero mató a otra gallina y así sucesivamente durante 10 días. Al cabo de este plazo, le quedaban 40 gallinas que pusieron 120 huevos. Había conseguir mantener ingresos cercanos al precio de venta de una producción de 200 huevos matando gallinas.

¡Qué demonios! El propietario exigía su rendimiento, así que recortó el presupuesto para granos, mató a otra gallina y fue a venderla junto con los 120 huevos del día. Pero los ingresos totales correspondían a una producción de 170 huevos, nada más. El día siguiente era un martes, las 39 gallinas sólo habían puesto 117 así que aumentó la presión sobre ella, molestándolas cada hora y causando aún más estrés.

El viernes tuvo que matar a dos gallinas. Recogió 100 huevos que las supervivientes habían puesto, bajando el promedio habitual. Con todo, ingresó el equivalente a 200 €, como en los buenos tiempos.
Al finalizar del mes, le quedaba una sola gallina que puso dos huevos. Se hizo una tortilla que degustó con un vaso de tinto y un mendrugo de pan, preparó el petate y se fue por los caminos sin mirar atrás.

lunes, 29 de noviembre de 2010

MondonGO: Herramienta ODM para MongoDB y PHP

Las virtudes de MongoDB como base de datos son cada vez más apreciadas por las empresas, ofreciendo servicios Web, Web 2.0 y cloud computing, además de productos basados en las mismas tecnologías o en tecnologías tradicionales. Por otra parte, aparecen herramientas que facilitan la administración de MongoDB, backups, el desarrollo de aplicaciones con MongoDB, etc. En esta ocasión me place presentar una herramienta creada en España, llamada MondonGO, y que seguro será muy apreciada por los desarrolladores de PHP.

MondonGO es un ODM (Object Data Mapper, o un Mapeador de Objetos de Datos), lo que permite mapear la estructura de los documentos de MongoDB automáticamente a objetos en PHP, simplificando enormemente los desarrollos, ya que el acceso, la seguridad y las tareas del mapeo son realizadas automáticamente, optimizando la sencillez y la productividad a la hora de desarrollar aplicaciones PHP usando MongoDB

MondonGO es un desarrollo de código abierto, que se puede descargar libremente desde GitHub.
Enlace a MondonGO: http://mondongo.es

MongoDB: Cómo trabaja una consulta en un entorno fragmentado

Un servidor pequeño. Queremos más capacidad. ¿Qué hacer? Tradicionalmente, podríamos escalar verticalmente con una caja más grande.

Con la fragmentación, en su lugar, escalamos horizontalmente para conseguir la misma huella computacional/almacenamiento/memoria desde servidores pequeños.

He aquí la comparación gráfica de escalabilidad vertical y horizontal:
Una colección fragmentada de MongoDB tiene una clave de fragmento. La colección es particionada en un orden preservando esta clave. En este ejemplo a es nuestra clave de fragmento:

{a:..., b:..., c:... } a es declarado clave de fragmento para la colección

Los metadatos son mantenidos en trozos, los cuales están representados por rangos de claves de fragmentos. Cada trozo es asignado a un fragmento en particular.

RangoFragmento
a en [∞, 2000]2
a en [2000, 2100]8
a en [2100, 2500]3
......
a en [88700, ∞]0

Cuando un trozo se hace demasiado grande, MongoDB automáticamente lo divide, y el balanceador más tarde migrará los trozos cuando sea necesario.

find({a:{$gt:333,$lt:400})

El proceso mongos enruta una consulta a los fragmentos adecuados. Para la consulta anterior, todos los datos posiblemente relevantes están en el fragmento 2, así que la consulta es enviada solamente a aquel nodo, y allí procesada.
A veces, un rango de consulta puede abarcar más de un fragmento, pero muy pocos en total. Esto es razonablemente eficiente.
Las consultas que no involucran las claves de fragmentos serán enviadas a todos los fragmentos como una operación "esparcir/reunir". Esto a veces es correcto. Aquí, tanto en nuestra máquina tradicional como en los fragmentos, haremos una exploracion de tabla igualmente (aproximadamente) costosa en ambas.
De nuevo, una consulta con una clave de fragmento resulta en una operación esparcir/reunir. Sin embargo, en cada fragmento, podemos usar el índice {b:1} para hacer la operación eficiente para dicho fragmento. Tenemos un coste elevado sobre la configuración vertical para el esfuerzo de las comunicaciones desde los procesos mongos para cada fragmento - no demasiado si el número de fragmentos es pequeño (10), pero digamos que muy sustancial en un sistema de 1000 fragmentos.
El término a involucra la clave de fragmento y permite a los procesos mongos enrutar inteligentemente la consulta al fragmento 2. Una vez que la consulta alcanza el fragmento 2, el índice {b:1} puede ser usada para procesar eficientemente la consulta.
Cuando se especifica una ordenación, los fragmentos relevantes ordenan localmente, y los procesos mongos funde los resultados. Así, el uso de recursos de los procesos mongos no es terriblemente alto.
Cuando se usa la replicación (típicamente un conjunto de réplica), simplemente tenemos más de un nodo por fragmento.

Abajo, las flechas indican replicación en entornos tradicionales contra fragmentados.

Fuente original: http://www.mongodb.org/download/attachments/2097354/how+queries+work+with+sharding.pdf

Primeros pasos con Python y MongoDB

Una de las mejores características de MongoDB es la multitud de drivers existentes para casi cualquier lenguaje de programación (ver Sección Drivers). Para los que amamos Python, tenemos un driver llamado pymongo, y que está soportado por 10gen, la empresa creadora de MongoDB. El site oficial de este driver lo encontramos en: http://api.mongodb.org/python/1.8.1%2B/index.html

Utilizar Python junto a MongoDB es muy sencillo, pues su sintaxis es muy similar a la de la consola mongo.

Instalación
La instalación de pymongo es sencilla. En Linux, procederemos a ejecutar el siguiente comando:

$ easy_install pymongo

O bien, descargamos el código fuente y lo instalamos desde la consola de Python:

$ git clone git://github.com/mongodb/mongo-python-driver.git pymongo
$ cd pymongo/
$ python setup.py install


Abrir conexión
La conexión se realiza de la siguiente manera:

>>> from pymongo import Connection
>>> conn = Connection() # Conexion local por defecto
>>> conn = Connection('miServidor', 30500) # Conexion remota a puerto 30500


Usar base de datos
Para obtener un objeto que referencia a la base de datos:

>>> db = conn.miBBDD # metodo 1
>>> db = conn['miBBDD'] # metodo 2



Usar una colección
Para obtener la colección con la que trabajar:

>>> coll = db.miColeccion # metodo 1
>>> coll = db['miColeccion'] # metodo 2



Documentos
Los documentos se mapean en Python como un diccionario:

>>> noticia = {"autor": "Rafael Hernamperez",
...   "cuerpo": "Python y MongoDB",
...   "etiquetas": ["Tutorial","Python","MongoDB"]}


Algunos tipos de datos especiales requieren de alguna librería específica, como datetime:

>>> import datetime
>>> noticia = {"autor": "Rafael Hernamperez",
...   "cuerpo": "Python y MongoDB",
...   "etiquetas": ["Tutorial","Python","MongoDB"],
...   "fecha": datetime.datetime.utcnow()}



Insertar documentos
Para insertar un documento:

>>> coll = db.noticias # Coleccion "noticias"
>>> coll.insert(noticia)



Recuperar un documento
Para recuperar un único documento simple (el primero):

>>> coll.find_one({"autor": "Rafael Hernamperez"})


Recuperar varios documentos
Para recuperar todos los documentos de una colección:

>>> for noticia in coll.find():
...    print noticia


Para recuperar todos los documentos (noticias, en este caso) de un autor concreto:

>>> for noticia in coll.find({"autor": "Rafael Hernamperez"})
...    print noticia



Contar documentos
Para contar todos los documentos de una colección:

>>> coll.count()

Para contar todos los documentos de un autor concreto:

>>> coll.find({"autor": "Rafael Hernamperez"}).count()


Consultas de rango
Recupera un determinado rango de documentos, delimitados por condiciones. En este caso, recupera aquellas noticias anteriores a una determinada fecha. El resultado se ordenará por autor:

>>> fecha = datetime.datetime(2010, 9, 28, 0, 0) # 2010/09/38 00:00h
... for noticia in coll.find({"fecha": {"$lt":fecha}}).sort(autor):
...    print noticia



Indexación
Para indexar la colección "noticias", por fecha (de la más reciente a la más antigua) y por autor (en orden alfabético):

>>> from pymongo import ASCENDING, DESCENDING
>>> coll.create_index([("fecha", DESCENDING), ("autor", ASCENDING)])

MongoDB: Indexación

En MongoDB, la indexación permite optimizar el rendimiento de las consultas, de forma muy similar a la de una base de datos relacional. Los índices se aplican a claves (campos) de nuestros documentos, ordenando sus valores para que la búsqueda sea más eficiente, y manteniendo dicha ordenación de forma constante. Tiene mucho sentido si las consultas sobre la clave es muy frecuente, especialmente si sobre la búsqueda se aplican filtros (mayor que, menor que, etc.), o si la consulta es lenta. También tiene sentido si en las consultas se muestra en un orden determinado.

A pesar de las ventajas del uso de índices, hay que tener en cuenta que los índices toman espacio y ralentizan las escrituras.

El siguiente ejemplo, muestra cómo ordenar una colección por la clave nombre:

db.articulos.ensureIndex({"nombre":1})

El valor 1 indica que la ordenación será en orden ascendente (de menor a mayor valor, o alfabéticamente, de la A a la Z). Para indicar que el orden sea descendente, se indicaría el valor -1. El siguiente ejemplo crea un índice compuesto, ordenando por nombre y por , en orden descendente (de más reciente a más antiguo):

db.articulos.ensureIndex({"nombre":1, "fecha":-1})

Si la clave del índice corresponde a un documento embebido, se ha de indicar la ruta del mismo:

db.articulos.ensureIndex({"comentarios.autor":1})

Los índices únicos indican que los valores de la clave no pueden repetirse:

db.articulos.ensureIndex({"titulo":1}, {unique:true})

Los índices toman un tiempo para realizar su cometido. Si la colección posee muchos datos, este tiempo ralentizaría el resto de operaciones de la base de datos. Para evitar este tiempo de demora, se puede indicar que el índice se realice en background o como operación en segundo plano:

db.articulos.ensureIndex({"nombre":1, "fecha":-1}, {background: true})

Si se desea conocer los índices que posee una colección determinada:

db.articulos.getIndexes()

Para eliminar un índice de una colección:

db.articulos.dropIndex({"nombre":1})


Más información: Os recomiendo ver la presentación de Mike Dirolf, de la cual se ha extraído gran parte de la información mostrada aquí:

MongoDB: Traducir SQL a MapReduce


Fuente: http://nosql.mypopescu.com/post/392418792/translate-sql-to-mongodb-mapreduce

MongoDB: Python y MapReduce

El presente post está inspirado en el artículo "MapReduce with MongoDB and Python", de Marcel Caraciolo, y en el cual veremos cómo implementar la función Map-Reduce mediante Python y MongoDB, donde podremos apreciar su potencial en grandes conjuntos de datos en computación distribuída.

Como ejemplo sencillo e ilustrativo, contaremos la frecuencia de palabras a través de varios documentos. En primer lugar, veremos cómo funciona map-reduce,utilizando una lista de sentencias simples. Para ello, tenemos el siguiente diagrama, en donde las palabras son divididas y agrupadas por la función map, y después reducidas independientemente (agregación) por la función reduce. Nuestra consulta puede estar distribuída en cuatro ordenadores, por lo que su procesamiento es mucho más rápido en tiempo de ejecución. El ejemplo, además, muestra un árbol balanceado, pero podría estar no balanceado o incluso tener alguna redundancia.
Antes de comenzar a desarrollar las funciones map y reduce debes saber que:
1) El motor MapReduce puede invocar funciones reduce de forma iterativa, por lo que éstas deben ser idempotentes:
   for all k,vals : reduce( k, [reduce(k,vals)] ) == reduce(k,vals)
1) Actualmente, el retorno de valores de una función reduce no puede ser un array (ha de ser un objeto o un número)
3) Si necesitas realizar una operación una única vez, utiliza la funión finalize.

Para implementar el código debes utilizar el framework Pymongo, el cual tiene soporte para Map/Reduce. Como ejemplo, el discurso de Obama en 2009 tiene muchas palabras repetidas, como puede apreciarse en la siguiente nube de etiquetas, cuyo tamaño de fuente indica la frecuencia de cada palabra:
MongoDB permite a los clientes enviar las implementaciones JavaScript de map y reduce, las cuales se evaluarán y ejecutarán en el servidor. La función map sería la siguiente (wordMap.js):

function wordMap() {
  // Buscar palabras en el texto del documento
  var palabras = this.text.match(/\w+g);

  if (palabras == null) {
     return;
  }

  for (var i = 0; i < palabras.length; i++) {
    // Emitir cada palabra, con contador de uno
    emit(words[i], {count: 1});
  }
}

MongoDB llamará a la función map por cada documento en la colección que está consultando, y apuntará al documento donde tendrá acceso una clave como text mediante this.text. Esta función no retorna una lista, si no que llama a una función emit que espera ser definida. Los parámetros de esta función (clave, valor), serán agrupados con otros resultados intermedios de otras evaluaciones map que tengan la misma clave (clave, [valor1, valor2]) y pasada a la función reduce (wordReduce.js
function wordReduce(key, values) {

  var total = 0;
  for (var i = 0; i < values.length; i++) {
    total += values[i].count;
  }

  return {count: total};
}

La función reduce ha de reducir una lista de un tipo dado a un valor simple del mismo tipo; debe transsitivo para que no tenga problemas sobre cómo están agrupados los elementos mapeados.

A continuación, el código del cliente Pymongo que pasa las funciones map/reduce al servidor:

MongoDB: Un ejemplo de MapReduce

Conceptos de MapReduce
MapReduce es una característica importante comprendida por MongoDB. Este framework fue creado por Google para dar solución a la computación paralela sobre grandes colecciones de datos en sistemas distribuídos. El concepto se basa en dos funciones, map y reduce, que se aplican sobre datos estructurados en formato de pares clave-valor.

La función map (mapeo) toma uno de estos pares en un dominio de datos, y retorna una lista de pares en un dominio diferente. Se aplica en paralelo por cada elemento de la entrada de datos, produciendo la lista de pares por cada llamada, juntando todos los pares con la misma clave de todas las listas y los agrupa, creando un grupo por cada clave.

La función reduce (reducción) se aplica paralelamente a cada grupo, generando una colección de valores para cada dominio, produciendo un valor (o varios) en cada llamada.

Básicamente, MapReduce transforma una lista de pares clave-valor en una lista de valores.


Un ejemplo
Para comprender cómo funciona MapReduce, vamos a ver un ejemplo práctico. Para ello, voy a utilizar uno ya hecho por Kristina Chodorow, una de las principales creadoras de MongoDB.

Imaginemos que tenemos una colección posts, que almacena los artículos de nuestro blog. Un documento en esta colección podría tener esta estructura:
La clave tags es un array de etiquetas, que permiten categorizar cada artículo. Y sobre esta clave vamos a basar el ejemplo de MapReduce, como por ejemplo, conocer las etiquetas más populares, dando un resultado como éste:
La función map emitirá cada etiqueta (tag), y la contará en la función reduce.

La función map es la siguiente:
Lo primero que hace es comprobar si existe una clave llamada tags, para evitar errores. A continuación realiza un bucle sobre esta clave (por cada elemento del array), emitiendo su nombre y un contador de 1.

La función reduce inicializa un contador a 0 y le añade cada elemento del array current. Al final retorna el contador final:
Para ejecutar ésto, invocaremos al comando mapreduce:
Los parámetros facilitados son:
- mapreduce: Se facilita la colección sobre la que extraer los datos (posts).
- map: Nombre de la función que realizará el mapeo.
- reduce: Nombre de la función que realizará la reducción.
- out: Colección donde se pondrá el resultado.

Finalmente, consultamos la colección tags para mostrar los resultados:


Fuentes consultadas
Concepto de MapReduce en Wikipedia: http://es.wikipedia.org/wiki/MapReduce
Artículo "Contar etiquetas", por Kristina Chodorow: http://cookbook.mongodb.org/patterns/count_tags/

Comparativa MongoDB con otras Bases de datos NoSQL

He encontrado en myNoSQL algunas tablas comparativas de MongoDB con respecto a otras bases de datos NoSQL, creadas por Alex Popescu.

Bases de datos orientadas a documento
La primera tabla compara tres bases de datos orientadas a documento: MongoDB, CouchDB y RavenDB (echo en falta una columna para OrientDB).

  MongoDB CouchDB RavenDB
Documentos
FormatoBSONJSONJSON
MetadataNoSistemaSistema + personalizado
VersionadoNoPlugin incluído
AdjuntosGridFS
Map/ReduceJavaScript + otrosJavaScriptLINQ
Carga masivamongoimport
Consulta AdhocNoNo
Almacenamiento
Fragmentación
DurabilidadServidor simple v1.8Diseño "crash-only"Escritura previa trazabilidad y aislamiento de instantáneas para garantizar recuperación vía ESE
TransaccionesNoNo
ConcurrenciaActualización in-placeMVCC (Multi-Version Concurrency Control)Concurrencia optimista
ConsistenciaMaestro fuerte / Esclavo eventualNodo fuerte / Cluster eventualEventual
ReplicaciónMaestro-Maestro con funciones de resolución personalizadasMaestro-EsclavoPlugin incluído
Interfaz
ProtocoloPersonalizado sobre TCP/IPHTTP/RESTHTTP/REST
.NET APIProyectos de tercerosProyectos de tercerosIncluído
Otros
TriggersNoValidación de actualización, Seguridad
SeguridadBásicaBásicaNinguna
Escrito enC++ErlangC#

Lenguajes de programación
La siguiente tabla muestra el uso de lenguajes de programación en varias bases de datos NoSQL. Yo hubiera añadido también Perl, C/C++, Erlang ó Go, que son lenguajes populares que también comprende MongoDB (entre otros muchos).

  BBDD Modo durabilidad java ruby python php .net http
documento mongodb basado en réplica x x x x x x
  couchdb nodo simple x x x x x x
  ravendb nodo simple - - - - x x
clave-valor redis en memoria, serializado en disco x x x x x -
  riak basado en réplica x x x x x x
tabular cassandra basado en réplica x x x x x -
grafo neo4j nodo simple x x x x - x
  sones nodo simple - - - - x x


Fuentes
http://nosql.mypopescu.com/post/1016366403/nosql-guide-for-beginners
http://nosql.mypopescu.com/post/978742866/document-databases-compared-couchdb-mongodb-ravendb

MongoDB: Ejemplo de configuración de fragmentación

Este post es una traducción del artículo original "Return to the Mongo Mailbag", de Kristina Chodorow.


En la lista de correo mongodb-user de la semana pasada, alguien preguntó (básicamente):

"Tengo 4 servidores y quiero dos frgamentos. ¿Cómo lo configuro?"

Mucha gente está preguntando sobre cómo configurar conjuntos de réplica y fragmentación, por lo que que aquí se explicará en detalle.


The Architecture

Prerequisitos. Si no estás familiarizado con los conjuntos de réplica, echa un vistazo a mi post en mi blog. El resto de este post no tendrá mucho sentido a menos que tú sepas qué es un árbitro. Además, deberías conocer los conceptos de la fragmentación.

Cada fragmento debería ser un conjunto de réplica, por lo que necesitaremos dos conjuntos de réplica (los llamaremos "foo" y "bar"). Queremos que nuestro cluster sea correcto si una de las máquinas se cae o se separa de la manada (partición de red), así que extenderemos fuera cada conjunto entre las máquinas disponibles. Los conjuntos de réplica están codificadas por color y las máquinas son nombradas imaginativamente server1-4.

Cada conjunto de replica tiene dos hosts y un árbitro. De esta manera, si un servidor se cae, ninguna funcionalidad se pierde (y ahí no estarán dos maestros en un servidor simple).

Para configurar ésto, ejecuta:

server1

$ mkdir -p ~/dbs/foo ~/dbs/bar
$ ./mongod --dbpath ~/dbs/foo --replSet foo
$ ./mongod --dbpath ~/dbs/bar --port 27019 --replSet bar --oplogSize 1


server2

$ mkdir -p ~/dbs/foo
$ ./mongod --dbpath ~/dbs/foo --replSet foo


server3

$ mkdir -p ~/dbs/foo ~/dbs/bar
$ ./mongod --dbpath ~/dbs/foo --port 27019 --replSet foo --oplogSize 1
$ ./mongod --dbpath ~/dbs/bar --replSet bar


server4

$ mkdir -p ~/dbs/bar
$ ./mongod --dbpath ~/dbs/bar --replSet bar


Los árbitros tienen un tamaño de oplog de 1. Por defecto, el tamaño de oplog es un ~5% de tu disco duro, pero los árbitros no necesitan mantener ningún dato, por lo que esto es un gran desperdicio de espacio.

Poner juntos los conjuntos de réplica

Ahora arrancaremos los dos conjuntos de réplica. Arrancar la consola mongo y escribir:

> db = connect("server1:27017/admin")
connecting to: server1:27017
admin
> rs.initiate({"_id" : "foo", "members" : [
... {"_id" : 0, "host" : "server1:27017"},
... {"_id" : 1, "host" : "server2:27017"},
... {"_id" : 2, "host" : "server3:27019", arbiterOnly : true}]})
{
  "info" : "Config now saved locally. Should come online in about a minute.",
  "ok" : 1
}
> db = connect("server3:27017/admin")
connecting to: server3:27017
admin
> rs.initiate({"_id" : "bar", "members" : [
... {"_id" : 0, "host" : "server3:27017"},
... {"_id" : 1, "host" : "server4:27017"},
... {"_id" : 2, "host" : "server1:27019", arbiterOnly : true}]})
{
  "info" : "Config now saved locally. Should come online in about a minute.",
  "ok" : 1
}


Ahora tenemos dos conjuntos de réplica en ejecución. Creemos ahora un cluster.


Configurar la fragmentación

Una vez intentemos configurar un sistema sin puntos simples de fallo, usaremos tres servidores de configuración. Podemos tener tantos procesos mongos como queramos (se recomienda uno en cada appserver), pero empezaremos con uno.

server2

$ mkdir ~/dbs/config
$ ./mongod --dbpath ~/dbs/config --port 20000


server3

$ mkdir ~/dbs/config
$ ./mongod --dbpath ~/dbs/config --port 20000


server4

$ mkdir ~/dbs/config
$ ./mongod --dbpath ~/dbs/config --port 20000
$ ./mongos --configdb server2:20000,server3:20000,server4:20000 --port 30000


Ahora añadiremos nuestros conjuntos de réplica al cluster. Concta los mongos y ejecuta el comando addshard:

> mongos = connect("server4:30000/admin")
connecting to: server4:30000
admin
> mongos.runCommand({addshard : "foo/server1:27017"})
{ "shardAdded" : "foo", "ok" : 1 }
> mongos.runCommand({addshard : "bar/server3:27017"})
{ "shardAdded" : "bar", "ok" : 1 }


Como puedes ver, terminarás con un fragmento "foo" y un fragmento "bar". (si estás usando una versión antigua de MongoDB, tus fragmentos tendrán nombres como “shard0000″ o “shard0001″)

Ahora puedes conectar a “server4:30000″ en tu aplicación y utilizarlo con un simple y “normal” mongod. Si quieres añadir más procesos mongos, simplemente arráncalos con los mismos parámetros configdb utilizados anteriormente.

MongoDB: Conjuntos de réplica. Parte 3

Este post asume que sabes qué son los conjuntos de réplica y algo de la sintáxis básica.

En la parte 1, configuramos un conjunto de réplica desde cero, pero la vida real es más desordenado: puedes querer migrar servidores de desarrollo a producción, añadir nuevos esclavos, priorizar servidores, cambiar cosas en el momento (on the fly)... y ésto es lo que este post cubre.


Antes de empezar…

A los conjuntos de réplica no les gusta localhost. Son serviciales para secundarlos... más o menos, bastante... pero a menudo provoca discrepancias. Puedes evitar estas discrepancias usando en su lugar el nombre del servidor (hostname). En Linux, puedes buscar tu nombre de servidor ejecutando el comando:

$ hostname
wooster


De aquí en adelante, usaremos mi nombre de servidor en lugar de localhost.


Arrancar con datos

Esto es más o menos que arrancar sin datos, escepto que deberías hacer una copia de seguridad de tus datos antes de arrancar (deberías siempre hacer un backup siempre de tus datos antes de hacer travesuras con tu servidor de configuración).

Si en el pre-conjunto-réplica, arrancaste tu servidor con algo como:

$ ./mongod

...conviértelo en el primer miembro de tu conjunto de réplica, por lo que has de parar y arrancar de nuevo con la opción –replset:

$ ./mongod --replSet unicomplex/wooster:27017

Ahora, inicializa el conjunto con el único servidor:

> rs.initiate()
{
"info" : "Config now saved locally. Should come online in about a minute.",
"ok" : 1
}



Agregar esclavos

Deberías arrancar MongoDB siempre con esclavos, así que agreguemos alguno.

Arranca tu servidor con las opciones usuales, así como también como –replSet. Así, por ejemplo, podríamos hacer:

$ ./mongod --dbpath ~/dbs/slave1 --port 27018 --replSet unicomplex/wooster:27017

Ahora, agregamos este esclavo al conjunto de réplica. Asegurarse de que db está conectado a wooster:27017 (el servidor primario) y arrancar:

> rs.add("wooster:27018")
{"ok" : 1}


Repetir como sea necesario para agregar más esclavos.


Agregar un árbitro

Esto es muy similar a agregar un esclavo. Cuando arranques el árbitro, deberías darle la opción –oplogSize 1. De esta manera el árbitro no desperdiciará espacio.

$ ./mongod --dbpath ~/dbs/arbiter --port 27019 --replSet unicomplex/wooster:27017 --oplogSize 1

Ahora agregarlo al conjunto. Puedes especificar que este servidor es un árbitro pasando en un objeto un campo arbiterOnly a rs.add:

> rs.add({"host" : "wooster:27019", "arbiterOnly" : true})
{"ok" : 1}



Degradar un primario

Supongamos que nuestra compañía tiene disponibles los siguientes servidores:

1. Super máquina Gazillion dollar
2. Instancia EC2
3. iMac que encontramos en la calle

A través de un accidente del destino, el iMac se convierte en primario. Podemos forzarle a que se convierta en esclavo ejecutando el comando:

> imac = connect("imac.example.com/admin")
connecting to: imac.example.com/admin
admin
> imac.runCommand({"replSetStepDown" : 1})
{"ok" : 1}


Ahora el iMac será un esclavo.


Establecer prioridades

Podemos indicar que el iMac nunca sea un maestro (queremos usarlo sólo como backup). Puedes forzar ésto estableciendo su prioridad a cero. La más alta prioridad de un servidor es la que hará que se convierta en maestro si el actual maestro falla. Las únicas opciones son 0 (no puede ser maestro) o 1 (puede ser maestro), pero en el futuro serás capaz de tener una buena graduación de prioridades..

Así, cambiaremos la prioridad del iMac a 0. Para cambiar la configuración, conectemos al maestro y editemos su configuración:

> config = rs.conf()
{
"_id" : "unicomplex",
"version" : 1,
"members" : [
{
"_id" : 0,
"host" : "prod.example.com:27017"
},
{
"_id" : 1,
"host" : "ec2.example.com:27017"
},
{
"_id" : 2,
"host" : "imac.example.com:27017"
}
]
}


Ahora, tenemos que hacer dos cosas: 1) establecer la prioridad del iMac a 0, y 2) actualizar la versión de la configuración. El nuevo número de versión es siempre el anterior más uno

> config.members[2].priority = 0
0
> config.version += 1
2


Finalmente, decimos al conjunto de réplica que tenemos una nueva configuración.

> use admin
switched to db admin
> db.runCommand({"replSetReconfig" : config})
{"ok" : 1}


Todos los cambios de configuración deben ocurrir en el maestro. Éstos se propagarán de éste a los esclavos. Ahora puedes "matar" cualquier servidor y el iMac nunca se convertirá en maestro.

Esta configuración es un poco melindrosa para hacer desde la consola. En el futuro, la gente probablemente utilice una GUI para configurar sus conjuntos y trastear con la configuración del servidor.


Artículo original: http://www.snailinaturtleneck.com/blog/2010/08/03/part-3-replica-sets-in-the-wild/

MongoDB: Conjuntos de réplica. Parte 2

Los conjuntos de réplica son básicamente maestro-esclavo con failover automático.

La idea es: tienes un esclavo y uno o más esclavos. Si el maestro se cae, uno de los esclavos automáticamente se convertirá en el nuevo maestro. Los drivers de base de datos, siempre encontrarán al maestro, si el maestro que se está usando se cae, ellos (drivers) automáticamente entenderán quién es el nuevo maestro y envía las escrituras a dichos servidor. Esto es mucho más fácil de manejar (y más rápido) que manejar manualmente el fallo sobre un esclavo.

Así, tu tienes un pool de servidores con un primario (el maestro) y N secundarios (esclavos). Si el primario tiene un accidente y desaparece, los otros servidores mantendrán una elección para elegir un nuevo primario.


Elecciones

Un servidor tiene que obtener una mayoría del total de votos para ser elegido, no sólo una mayoría. Esto significa que, si tenemos 50 servidores y cada servidor tiene un voto (por defecto, los últimos post mostrará cómo cambiar el número de votos que un servidor obtiene), un servidor necesita al menos 26 votos para convertirse en primario. Si ninguno obtiene 26 votos, ninguno se convierte en primario. El conjunto puede todavía manejar lecturas, pero no escrituras (ya que no hay maestro).

Si un servidor obtiene 26 o más votos, éste se convertirá en primario. Todas las futuras escrituras serán direccionadas a éste, hasta que pierda una elección, estalle, etc.

El primario original es todavía parte del cojunto. Si lo levantas, éste se convertirá en un servidor secundario (hasta que obtenga nuevamente la mayoría de votos).

Una multitud de tres (de buena manera)

Una complicación con este sistema de votación es que tú no puedes tener sólo un maestro y un esclavo.

Si estableces sólo un maestro y un esclavo, el sistema tiene un total de 2 votos, así que un servidor necesita ambos botos para ser elegido maestro (1 no es mayoría). Si uno de los servidores se cae, el otro servidor sólo tiene un voto de 2, así que ésto lo convierte (o lo conserva) en un esclavo. Si la red está particionada, y repentinamente el maestro no tiene una mayoría de los votosos (sólo tiene su único voto), será degradado a un esclavo. El esclavo además no tiene una mayoría de los votos, po lo que permanecerá siendo esclavo (así que terminarás con dos esclavos hasta que los servidores se puedan alcanzarse el uno al otro de nuevo).

Esto es un desperdicio, aunque, tener dos servidores y ningún maestro levantado, los conjuntos de réplica tienen varias formas de evitar esta situación. Una de las más simples y más versátiles es usar un árbitro, un servidor especial que existe para resolver disputas. Éste no sirve ningún dato al mundo exterior, si no que es simplemente un votante (puede incluso estar en la misma máquina como otro servidor, siendo muy ligero). En la parte 1, el árbitro era localhost:27019.

Así, digamos que establecemos un maestro, un esclavo y un árbitro, cada un con un voto (total 3 votos). Entonces, si tenemos un maestro y un árbitro en un centro de datos y el esclavo en otro, si se produce una partición de red, el maestro todavía tendrá una mayoría de votos (maestro+árbitro). El esclavo tiene sólo 1 voto. Si el maestro falle y la red no se particiona, el árbitro puede votar por el esclavo, promocionándole como maestro.

Con esta configuración de tres servidores, obtenemos un failover sensible y robusto.

Lo próximo: configuración dinámica de tu conjunto de réplica. En la parte 1, confiramos todo completamente al empezar. En el mundo real querremos ser capaces de añadir servidores dinámicamente y cambiar la configuración.


Artículo original: http://www.snailinaturtleneck.com/blog/2010/08/02/replica-sets-part-2-what-are-replica-sets/

MongoDB: Conjuntos de réplica. Parte 1

Los próximos tres posts están dedicados a los conjuntos de réplica, una de las mejores características de MongoDB para asegurar la alta disponibilidad en un sistema de base de datos. Estos posts los escribió Kristina Chodorow, una de las desarrolladoras más importantes de MongoDB.

Este post muestra cómo hacer el "Hola, mundo" de los conjuntos de réplica. Voy a empezar con un post explicand qué es, pero la codificar es mucho más divertido que leer. Por ahora, todo lo que tienes que saber es que hay maestro-esclavo con failover (a prueba de fallos) automático.

Asegúrate de que tienes la versión 1.5.7 o superior de la base de datos (MongoDB) antes de probar el código que viene a continuación.

Paso 1: Elegir un nombre para tu conjunto

Esto es sólo organizacional, así que puedes elegir el que sea. Yo usaré "unicomplex" para mi ejemplo.

Nota: Kristina hace uso de su buen humor para la elección de un nombre. El nombre "que sea" es un artículo para dar un nombre a un bebé. Unicomplex se refiere a la saga de Startrek.


Paso 2: Crear los directorios de datos

Necesitamos un directorio de datos para cada servidor que vayamos a arrancar:

$ mkdir -p ~/dbs/borg1 ~/dbs/borg2 ~/dbs/arbiter


Paso 3: Arrancar los servidores

Arrancaremos nuestros tres servidores:

$ ./mongod --dbpath ~/dbs/borg1 --port 27017 --replSet unicomplex/
$ ./mongod --dbpath ~/dbs/borg2 --port 27018 --replSet unicomplex/
$ ./mongod --dbpath ~/dbs/arbiter --port 27019 --replSet unicomplex/



Paso 4: Inicializar el conjunto

Ahora tenemos que decirle al conjunto, "¡hey, existes!". Arranca la consola mongo y ejecuta:

MongoDB shell version: 1.5.7
connecting to: test
> rs.initiate({"_id" : "unicomplex", "members" : [
... {"_id" : 0, "host" : "localhost:27017"},
... {"_id" : 1, "host" : "localhost:27018"},
... {"_id" : 2, "host" : "localhost:27019", "arbiterOnly" : true}]})
{
"info" : "Config now saved locally. Should come online in about a minute.",
"ok" : 1
}


rs es una variable global que mantiene un racimo de funciones útiles para conjuntos de réplica.

El mensaje dice que estará online en aproximadamente un minuto, pero siempre han sido unos ~5 segundos para mi. Una vez que veas la siguiente línea en uno de los logs (trazas):

replSet PRIMARY

¡…tu conjunto de réplica está listo para funcionar!

Jugar con el conjunto

Uno de los servidores será el maestro (master), el otro es un esclavo (slave). Puedes entender cuál es cuál ejecutando el comando isMaster en la consola:

> db.isMaster()
{
"ismaster" : true,
"secondary" : false,
"hosts" : [
"localhost:27017",
"localhost:27018",
],
"arbiters" : [
"localhost:27019"
],
"ok" : 1
}


Si db no es primario, el servidor que está será listado en el campo "primary" (primario):

> db.isMaster()
{
"ismaster" : false,
"secondary" : true,
"hosts" : [
"localhost:27017",
"localhost:27018",
],
"arbiters" : [
"localhost:27019"
],
"primary" : "localhost:27018",
"ok" : 1
}


Ahora, prueba a "matar" el servidor primario. Espera un par de segundos y verás que el otro servidor (no-árbitro) será elegido primario.

Una vez que haya un nuevo primeraio, rearranca el mongod que acabas de "matar". Verás que se une a la refriega, aunque no se convierte en maestro (ya hay un maestro, así que no mecerá el barco). Después de unos pocos segundos, mata el maestro actual. Ahora, el antiguo maestro se convertirá en maestro de nuevo.

Es bastante divertido jugar con ésto, levantando y cayendo y mirando cómo el maestrazgo va atrás y adelante (o quizá yo me entretenga fácilmente).


Insertar y consultar datos

Por defecto, los esclavos están sólo como respaldo o backup, por lo que puedes usarlo para consultas (lecturas) si estableces el flag "slave ok". Conecta a cada uno de los servidores y establece este flag:

> db.getMongo().setSlaveOk()
> borg2 = connect("localhost:27018/test")
connecting to: localhost:27018/test
test
> borg2.getMongo().setSlaveOk()


Ahora puedes insertar, actualizar y eliminar datos en el maestro y leer los cambios en el esclavo.


Artículo original: http://www.snailinaturtleneck.com/blog/2010/07/30/replica-sets-part-1-master-slave-is-so-2009

MongoDB: Fragmentación y conjuntos de réplica ilustrados

Este post asume que conoces qué son los conjuntos de réplica y la fragmentación.

Paso 1: No usar fragmentación

En serio. Casi nadie lo necesita. Si estabas en el punto donde necesitabas particionar tu base de datos MySQL, probablemente tengas otras vías de hacerlo antes de que necesites particionar MongoDB (nos burlamos de los billones de filas).

Ejecuta MongoDB como un conjunto de réplica. Cuando necesites realmente capacidad extra, entonce, y sólo entonces, comienza la fragmentación. ¿Por qué?

1. Tienes que elegir una clave de fragmentación. Si conoces las características de tu sistema antes de elegir una clave de fragmentación, puedes ahorrarte a tí mismo un mundo de dolor.
2. La fragmentación añade complejidad.: tienes que mantener trazabilidad de más máquinas y procesos.
3. La optimización prematura es la raíz de todo mal. Si tu aplicación no se ejecuta rápido, ¿está tu CPI o tu red limitadas? ¿Tienes demasiados índices? ¿Demasiados pocos? ¿Están siendo golpeados por tus consultas? Verifica (al menos) todas estas causas primero.


Usar la fragmentación

Un fragmento está definido como uno o más servidores con un maestro. Asi, un fragmento podría ser un simple mongod (mala idea), una configuración maestro-esclavo (mejor idea), o un conjunto de ráplica (mejor idea).

Digamos que renemos tres fragmentos y cada uno de ellos es un conjunto de réplica. Para tres fragmentos, querrás un mínimo de 3 servidores (la regla general es: mínimo de N servidores para N fragmentos). Haremos también el mínimo en los conjuntos de réplica: un maestro, primario y árbitro para cada conjunto.

Las tazas son procesos MongoDB. Así, tenemos tres conjuntos de réplica:

“M” representa al “maestro” (master), “S” representa al “esclavo” (slave), y “A” representa el "árbitro" (arbiter). Tenemos también los servidores de configuración:

y procesos mongos:

Ahora, soporta estos procesos en servidores (las bandejas son servidores). Cada maestro necesita hacer mucho, así que cada primario toma su propio servidor.

Ahora ponemos también un esclavo y un árbitro en cada caja.

Observar cómo mezclamos las cosas: sin conjunto de réplica está alojado en un servidor simple, así que si un servidor se cae, el conjunto puede recuperarse en un servidor diferente y seguir funcionando.

Ahora podemos añadir tres servidores de configuración y dos procesos mongos. Los procesos mongos son puestos normalmente en el appserver, pero son muy ligeros, así que soportaremos un par de ellos aquí.

Un poco cargado, pero posible

En caso de emergencia...

Digamos que caemos una bandeja. CRASH! Con esta configuración, tus datos están a salvo (mientras estés usando w) y el cluster no pierde funcionalidad (en términos de lecturas y escrituras).

Los trozos no estarán disponibles para migrar (debido a que uno de los servidores de configuración está caído), así que un fragmento puede estar "hinchado" si el servidor de configuración está caído por mucho tiempo.

Las particiones de red y perder dos servidores son problemas grandes, así que deberías tener más de tres servidores si quieres gran disponibilidad.


Fuente original: http://www.snailinaturtleneck.com/blog/2010/08/09/sharding-and-replica-sets-illustrated/

MongoDB: Tres consejos para la consola

La consola MongoDB es una herramienta administrativa poderosa. Algunas de sus características más útiles no son ampliamente conocidas.

1. Puedes ejecutar ficheros JavaScript

Si tienes una serie de comandos que debes mantener ejecutando, considera ponerlos en un fichero. Por ejemplo, si quieres escribir tu propia versión de mongostat con una única operación de información, puedes crear un fichero llamado myMongoStat.js:

// Impresión de la cabecera
print("inserts\tqueries\ tupdates\tdeletes");

// Impresión constante de las estadísticas
while (true) {
var stats = db.serverStatus();

print(stats.opcounters. insert+"\t"+
stats.opcounters.query+"\t"+
stats.opcounters.update+"\t"+
stats.opcounters['delete']);

sleep(1000);
}


Cosas a anotar:

* No puedes usar asistentes de la consola: cosas como "use foo" ó "show dbs". Para hacer ésto, utiliza los comandos equivalentes de la base de datos (db.getSisterDB("foo") ó admin = db.getSisterDB("admin"); admin.runCommand({ listDatabases:1}) , respectivamente.
* "delete" es una palabra reservada en JavaScript, así que no puedes escribir stats.opcounters.delete, si no que tienes que usar un obtenedor de estilo array asociativo (stats.opcounters["delete"]).

Para eje utar este script, abrir un termina y escribir:

$ ./mongo myMongoStat.js

Puedes hacer ésto con cualquier serie de comandos que utilices regularmente.

2. Puedes ver (casi) el código fuente de cualquier función

¿Curioso con lo que find() está haciendo? ¿No puedes recordar los parámetros de update()? Simplemente ejecuta el comando sin los paréntesis y la consola mostrará su código fuenrte:

> db.foo.update
function (query, obj, upsert, multi) {
assert(query, "need a query");
assert(obj, "need an object");
this._validateObject(obj);
this._mongo.update(this._ fullName, query, obj, upsert ? true : false, multi ? true : false);
}


Por instancia, si intentas imprimir el código de la función nativa Date.parse(), obtendrás lo siguiente:

> Date.parse
function parse() {
[native code]
}


3. Puedes ejecutar cualquier programa

Una característica de la consola poco conocida e indocumentada es que puedes ejecutar cualquier ejecutable desde la consola.

> runProgram("ls", "-l")
sh7056| -rwxr-xr-x 1 root root 41224968 2010-06-01 18:21 mongo
sh7056| -rwxr-xr-x 1 root root 41224968 2010-06-01 18:21 mongod
sh7056| -rwxr-xr-x 1 root root 41224968 2010-06-01 18:21 mongodump
sh7056| -rwxr-xr-x 1 root root 41195810 2010-06-01 18:21 mongoexport
sh7056| -rwxr-xr-x 1 root root 41204267 2010-06-01 18:22 mongofiles
sh7056| -rwxr-xr-x 1 root root 41237850 2010-06-01 18:22 mongoimport
sh7056| -rwxr-xr-x 1 root root 41275461 2010-06-01 18:23 mongorestore

MongoDB: Consejos sobre diseño de esquemas

El diseño de esquemas es una de las tareas más exigentes para nuevos usuarios de MongoDB. A menudo hay más de un modelo de datos viable, dependiendo de las necesidades de la aplicación, y los sacrificios han de ser medidos. Obtener un buen modelo de datos viene con la experiencia, y desde luego algo de experiencia con MongoDB es siempre útil. Pero si estás empezando, he aquí algunas ideas útiles a tener en mente.

Entender las características de MongoDB
Entender el conjunto de características de MongoDB es el primer paso para decidir qué patrones de modelo de datos tienen sentido. Por instancia, la habilidad de indexar claves de array y documentos internos, hacen viable el modelo de riqueza de documento. Si estás teniendo problemas modelando datos, primero asegúrate que entiendes las características de las bases de datos. E incluso si eres un veternao con MongoDB, asegúrate de mantener las últimas actualizaciones. Los operadores $slice y $or, recientemente añadidos, por ejemplo, te permiten mucha más flexibilidad en algunos casos.

Uso de documentos ricos
Parte de la belleza de MongoDB es su capacidad para almacenar estructuras de documentos ricos. Esto permite a los desarrolladores respresentar sus entidades de modelo de datos más holísticamente que lo que podrían hacer con una base de datos relacional. Mientras que en una base de datos relacional, una entidad producto puede ser dividida en docenas de tablas, el mismo producto puede guardarse en un documento simple en MongoDB. Que MongoDB pueda actuar en documentos ricos, usando la notación punto del lenguaje de consulta y una lista creciente de operadores de consulta y actualización, es la razón más convincente para considerar las estructuras ricas de documentos en tus esquemas.

Normalizar cuando sea necesario
Con todo lo dicho, no hay nada malo al migrar hacia una representación más normalizada. Algunos desrrolladores sienten que MongoDB no debería ser usado para almacenar relaciones entre colecciones, pero esto no podría estar más lejos de la verdad. Es una práctica común usar múltiples colecciones cuando las entidades involucradas necesitan la máxima cantidad de flexibilidad. Por instancia, si se representan productos y reseñas, recomendamos normalmente tener una colección separada para cada una. Las reseñas necesitan ser actualizadas y consultadas en una variedad de formas, y usualmente tienen en sí mismas una estructura de documento rica, conteniendo votos y otras bondades, información de perfil de usuario, etc. Podría preocuparte que normalizar algo como esto sin joins es ineficiente, así que será necesaria más de una consulta para visualizar una página de productos y sus reseña. Pero MongoDB es muy rápido; un pequeño benchmarking muestra que múltiples consultas no serán un problema para mayor parte de los casos.

Hay mucho más sobre el diseño de esquemas. La siguiente presentación aporta más información:

MongoDB y Ruby

Estupenda presentación de Kyle Banker en la Mongo France celebrada el pasado día 21 de Junio en París, donde explica la sinergia de MongoDB y el lenguaje de programación Ruby, con multitud de ejemplos y casos. También expone casos de normalización y desnormalización, y cuándo embeber y anidar documentos. Muy interesante.

MongoDB: Presentación sobre la fragmentación

Interesante presentación de Eliot Horowitz que esboza en líneas generales cómo será la característica de fragmentación en MongoDB.

Minitutorial: Vistazo rápido a MongoDB

En las próximas líneas se presenta un pequeño y rápido tutorial que abarca las nociones más básicas y elementales de MongoDB y la consola mongo. Este minitutorial está extraído del Tiny MongoDB Browser Shell (http://try.mongodb.org), el cual recomiendo utilizar para una toma de contacto y entender los conceptos.

1. Consola JavaScript
La primera cosa a destacar es que la consola MongoDB está basada en JavaScript.
De esta manera, puedes hacer cosas como éstas:

a = 5;
a * 10;
for(i=0; i<10; i++) { print('hello'); };



2. Documentos
MongoDB es una base de datos basada en documentos. Esto significa que almacenamos los datos como documento, lo cual es similar a los objetos JavaScript. A continuación hay algunos objetos JS de muestra:

var a = {age: 25};
var n = {name: 'Ed', languages: ['c', 'ruby', 'js']};
var student = {name: 'Jim', scores: [75, 99, 87.2]};



3. Guardar
He aquí cómo se guarda un documento en MongoDB:

db.scores.save({a: 99});

Esto dice, "guarda el documento '{a: 99}' en la colección 'scores'."

Para ver si el documento fue guardado:

db.scores.find();


4. Guardar y consultar
Lo siguiente añade algunos documentos a la colección scores:

for(i=0; i<10; i++) { db.scores.save({a: i, exam: 5}) };

Al escribir

db.scores.find();

Verás si se guardaron correctamente. Desde la consola sólo se visualizan 10 resultados al mismo tiempo, por lo que neceistarás el comando 'it' para iterar sobre el resto.


5. Consultas básicas
Vamos a realizar consultas más específicas, por ejemplo, consultando todos los documentos donde a == 2:

db.scores.find({a: 2});

o todos los documentos donde a > 15:

db.scores.find({a: {'$gt': 15}});


6. Operadores de consulta
$gt es uno de los operadores especiales de consulta. He aquí algunos otros:

$lt - '<', $lte - '<=',
$gte - '>=', $ne - '!='
$in - 'está en un array', $nin - '! (no) en array'

db.scores.find({a: {'$in': [2, 3, 4]}});
db.scores.find({a: {'$gte': 2, '$lte': 4}});



7. Actualizaciones
Vamos a crear un par de documentos para actualizarlos después:

db.users.save({name: 'Johnny', languages: ['ruby', 'c']});
db.users.save({name: 'Sue', languages: ['scala', 'lisp']});


Asegúrate de que se han guardado llamando a db.users.find()

Actualizaremos el primer documento mediante:

db.users.update({name: 'Johnny'}, {name: 'Cash', languages: ['english']});


8. Operadores de actualización
La anterior actualización reemplazó el documento entero, pero MongoDB también soporta actualizaciones parciales en los documentos. Por ejemplo, para establecer un valor:

db.users.update({name: 'Cash'}, {'$set': {'age': 50} });

También puedes poner (push) y extraer (pull) elementos de los arrays:

db.users.update({name: 'Sue'}, {'$pull': {'languages': 'scala'} });
db.users.update({name: 'Sue'}, {'$push': {'languages': 'ruby'} });



9. Eliminar datos
Para eliminar todo en una colección:

db.scores.remove();

Para eliminar sólo los documentos coincidentes, agrega un selector de consulta al método remove:

db.users.remove({name: 'Sue'});


10. ¿Lo siguiente?
Hay mucho más que lo mostrado en este minitutorial. Si te ha interesado, te animo a que te descargues MongoDB (http://www.mongodb.org/display/DOCSES/Descarga) y lo pruebes. También puedes consultar la documentación (http://www.mongodb.org/display/DOCSES/Inicio) para más información.

MongoDB: Consistencia distribuida. Parte 6

El siguiente diagrama (click para verlo en grande) muestra varios modelos de consistencia que han sido discutidos en esta serie. Los modos de consistencia fuerte generalmente encuentran los requerimientos de los modos débiles, y así de este modo se muestran como subconjuntos en este diagrama tipo Venn.

Ten en mente que para muchos productos, la consistencia es afinable: un producto no pertenece necesariamente a un rectángulo en particular, pero una opración dada ciertamente lo es.



* Consistencia eventual - la consistencia eventual definica por Amazon en el papel dynamo.
* Consistencia de lectura monotónica - Una forma estricta de consistencia eventual.
* Consistencia lee-tus-propias-escrituras - una forma estricta de consistencia eventual.
* MRC + RYOW - un sistema con propiedades lectura monotónica más lee-tus-propias-escrituras. Un sistema de replicación maestro-maestro, donde un cliente dado siempre interactúa con un maestro simple, podría tener estas propiedades.
* Consistencia inmediata - un sistema que es inmediatamente consistente pero que no soporta operaciones atómicas. Los sitemas de quorum stricto, donde R+W>N, conocen este criterio (y teóricamente podrían hacer más, depende del diseño).
* Consistencia fuerte - un sistema que soporta operaciones lectura/escritura atómicas en entidades simples de datos. Este es el modo por defecto de MongoDB.
* Transacciones completas - Oracle

Fuente: http://blog.mongodb.org/post/523516007/on-distributed-consistency-part-6-consistency-chart

MongoDB: Consistencia distribuida. Parte 5

En la parte 2 hablamos someramente sobre consistencia eventual de "escritor simple". Aquí discutiremos sobre muchos escritores, y definiremos este término con más precisión.

Por "muchos escritores" nos referimos a un sistema donde diferentes servidores de datos pueden recibir concurrentemente escrituras (y asíncronas). Algunos ejemplos de sistemas que reciben escrituras eventualmente consistentes:

* Amazon Dynamo
* Replicación CouchDB maestro-maestro

Con la consistencia eventual multi-escritora, necesitamos direccionar el fenómeno de conflicto de escrituras. Las escrituras a dos servidores al mismo tiempo pueden actualizar el mismo objeto. Hemos de solucionar el conflicto de una manera que sea aceptable para el caso en cuestión. Algunas soluciones podrían ser:

* Última escritura gana
* Fusión programática
* Operaciones conmutativas


Última escritura gana

Última escritura gana es un popular método por defecto en muchos sistemas. Si recibimos una operación que es antigua, simplemente la ignoramos. En un sistema distribuido, la definición de "último" es tan duro que los relojes no pueden ser sincronizados perfectamente. Así que muchos sistemas utilizan un vector de relojes.

Inserciones

Sorprendentemente, una operación tradicional de inserción es delicada con muchos escritores. Considera estas operaciones realizadas al mismo tiempo en diferentes servidores:

op1: insert( { _id : 'joe', age : 30 } )
op2: insert( { _id : 'joe', age : 33 } )


Si aplicamos nativamente estas dos operaciones en cualquier orden, obtendremos un resultado inconsistente. Insertar significa típicamente:

if( !already_exists(x._id) ) then set( x );
si( !ya_existe(x._id) ) entonces establece( x );


Sin embargo, con la consistencia eventual no tenemos un estado global en tiempo real. Verificar already_exists() es delicado.

La mejor solución no es soportar la inserción, si no más bien set() (establece()) - por ejemplo, "establece un nuevo valor". A veces, a ésto se le llama un upsert. Entonces, si tenemos semánticas última-escritura-gana, todo va bien.

Eliminaciones

La eliminaciones requieren de una manipulación especial en caso de objetos renacidos. Considera esta secuencia:

op1: set( { _id : 'joe', age : 40 } }
op2: delete( { _id : 'joe' } )
op3: set( { _id : 'joe', age : 33 } )


Si op2 y op3 invierten el orden de ejecución, podríamos tener un problema. Así que necesitamos recordar el borrado por un momento, y aplicar semánticas de última-operación-gana. Algunos productos llaman al recuerdo del borrado una lápida.

Actualizaciones

Las actualizaciones tienen un asunto similar a las inserciones, así que para las actualizaciones, usamos la operación set() tal y como se describió anteriormente.

Nótese que las actualizaciones parciales de objeto pueden ser delicadas para replicar de forma eficiente. Considera una operación set() donde deseamos actualizar un simple campo:

update users set age=40 where _id=’joe’

Esto no es problema con la consistencia eventual si replicamos una copia completa del objeto. Sin embargo, ¿qué ocurre si el objeto del usuario tenía 1MB de tamaño? Esto debería ir bien enviando el campo de la nueva edad y el _id, en lugar del objeto completo. Sin embargo, esto es difícil. Considera:

op1: update users set age=40 where _id='joe'
op2: update users set state='ca' where _id='joe'


No podemos replicar simplemente la actualización parcial y usar el método última-escritura-gana; la base de datos neceitará más sofisticación para manipular ésto de manera eficiente.


Fusión programática

El método última-escritura-gana es buena, pero no siempre es suficiente. Tener que resolver mediante la aplicación cliente el conflico mediante una fusión es una buena alternativa. Consideremos un ejemplo mencionado en el Papel de Amazon Dynamo: manipulaciones de carritos de la compra. Con una consistencia eventual podría no ser seguro hacer algo como:

update cart set this[our_sku].qty=1 where _id='joe'

Si hay múltiples manipulaciones del carrito, algunas podrían perderse utilizando el método última-escritura-gana. En su lugar, el papel de Dynamo habla sobre almacenar las operaciones en el objeto carrito, en lugar del estado actual de los datos. Podríamos almacenar algo como:

update cart append { time : now(), op : 'addToCart', sku : our_sku, qty : 1 }
where _id='joe'


Cuando ocurra un conflicto, los objetos carrito pueden ser fusionados. No perdemos ninguna operación. Cuando llega la hora de comprar (check out), reproducimos todas las operaciones, lo cual podría incluir ajuste de cantidades y eliminaciones desde el carrito. Después de la reproducción tenemos el estado final del carrito.

Los ejemplos anteriores utilizan un campo timestamp - en un sistema real se puede usar un vector para ordenar las operaciones en el carrito.

Es interesante notar que no sólo hemos evitado conflictos, si no que también somos capaces de realizar operaciones donde la atomicidad sería requerida.


Operaciones conmutativas

Si todas las operaciones son conmutativas (más precisamente, plegables), nunca tendremos ningún conflicto. Las operaciones pueden ser aplicadas simplemente en cualquier orden, y el resultado sería el mismo. Por ejemplo:

// x starts as { }
x.increment('a', 1);
x.increment('a', 3);
x.addToSet('b', 'foo');
x.addToSet('b', 'bar');
result: { a : 4, b : {bar,foo} }

// x starts as { }
x.addToSet('b', 'bar');
x.increment('a', 3);
x.increment('a', 1);
x.addToSet('b', 'foo');
result: { a : 4, b : {bar,foo} }


Sin embargo la composición de addToSet e increment podría no ser plegable; de este modo, tenemos que usar sólo uno o el otro para un campo particular del objeto.

Fuente: http://blog.mongodb.org/post/520888030/on-distributed-consistency-part-5-many-writer

MongoDB: Consistencia distribuida. Parte 4

La consistencia eventual hace más fácil el almacenamiento de datos en un centro multi-datos. Hay razones por las que la consistencia eventual es útil para centros multi-datos que no están relatados para la disponibilidad y CAP. Como se mencionó en la parte 3, algunos tipos comunes de particiones de red, tales como la pérdida de un centro de datos entero, son actualmente particiones de red triviales y pueden incluso no tener efecto de disponibilidad de todos modos.

Hay algunas arquitecturas para el almacenamiento de datos en un centro multi-datos:

* DR
* Región simple
* Lecturas locales, escrituras remotas
* Búsqueda inteligente
* Consistencia eventual

DR

Por DR nos referimos a una arquitectura tradicional de continuidad desastre recuperación / negocio. Es bastante simple: servimos cualquier cosa desde un centro de datos, con replicación a una facilidad secundaria que está offline. En un fallo transferimos todo de forma sincronizada.

La disponibilidad puede ser muy alta en este modelo, cuando cualquier asunto sobre el primer centro de datos, incluyendo las particiones de red internas, transferimos sincronizadamente, y con todo el primer centro de datos desactivado, la partición es trivial.

Este modelo funciona bien con consistencia fuerte.

Centro multi datos, Región simple

Esta opción es análoga a usar múltiples centros de datos dentro de una región simple. Amazon y DoubleClick han usado este esquema en el pasado. Tenemos múltiples centros de datos, separados físicamente, pero todo dentro de una región (por ejemplo, el Noroeste). La latencia entre centros de datos es entonces razonable: si permanecemos dentro de un radio de 150 millas, podemos tener transmisiones de cerca de 5 milisegundos. Podríamos tener un anillo de fibra entre digamos, 3 o 4 centros de datos. Como la latencia es razonable, para muchos problemas, una operación WAN aquí está bien. Con una topología de anillo, una partición de red no-trivial es poco probable.

La región simple es útil tanto para arquitecturas de consistencia fuerte como para consistencia eventual. Con un producto del estilo Dynamo, cuando N=W ó N=R, esta es una buena opción, por lo demás cuando se usan múltiples centros de datos tendremos un tiempo de espera grande para confirmar escrituras remotas.

Lecturas locales, Escrituras remotas

Para casos de lectura pesada, esta es una buena opción. Aquí leemos datos eventualmente consistentes (fácil con la mayor parte de productos de base de datos, incluyendo sistemas RDBMS), pero haciendo que todas las escrituras vuelvan a la facilidad maestro sobre la WAN. Un sistema del estilo dynamo en un cento de datos múltiple con un muy alto valor W y un bajo valor R puede ser también considerado de esta manera.

Este patrón debería funciona muy bien para gestión de contenidos tradicionales: publicar no es frecuente, y leer es muy frecuente.

Usar una Red de Entrega de Contenidos (Content Delivery Network (CDN)), con un sitio web origen centralizado sirviendo contenidos dinámicos, es otro ejemplo.

Búsqueda inteligente

Discutimos un poco sobre "Búsqueda Inteligente" (“Intelligent Homing”) en la parte 3. La idea es almacenar la copia maestra de una entidad de datos dada cerca de su usuario.

Esto funciona funciona muy bien si los datos se correlacionan con el usuario, como el perfil del usuario, la bandeja de entrada, etc

Tenemos rápidas escrituras confirmadas localmente. Si un centro de datos se cae completamente, podríamos estar aún a prueba de fallos sobre el estado maestro a cualquier lugar donde haya una réplica.

Consistencia eventual

La consistencia eventual de muchos-escritores nos brinda dos beneficios con centros de datos múltiples:

* altísima disponibilidad en el caso de apagones de red;
* rápidas escrituras confirmadas localmente

En el diagrama de debajo, un cliente de un sistema del estilo dynamo escribe los datos a cuatro servidores (N=4). Sin emabargo, únicamente espera confirmación de las escrituras de dos servidores en su centro de datos local, para mantener la latencia baja en la confirmación de escritura.



Nótese sin embargo que si R+W > N, no podemos tener rápidas lecturas y escrituras locales al mismo tiempo si todos los centros de datos son pares iguales.

Combinaciones

Las combinaciones a menudo tienen sentido. Por ejemplo, es común mezclar DR y Lectura Local / Escritura Remota.

Fuente: http://blog.mongodb.org/post/516567520/on-distributed-consistency-part-4-multi-data-center

MongoDB: Consistencia distribuida. Parte 3

Es fascinante que la sentencia formal del teorema para CAP, en la primera prueba no usa la palabra partición.

Teorema 1. Es imposible en el modelo asíncrono de red para implementar un objeto de datos lectura/escritura que garantice las siguientes propiedades:
• Disponibilidad
• Consistencia atómica en todas las ejecuciones aceptables (incluyendo aquellas en las que los mensajes están perdidos).


Lo dicho, hablemos sobre las particiones, como "mensajes perdidos...en el modelo de red asíncrono" es directamente análogo.

Echemos un vistazo a un ejemplo:



En el diagrama anterior, la red está particionada. Las mitades izquierda y derecha (quizá éstas correspondan a dos continentes) no se pueden comunicar del todo. Cuatro clientes y cuatro nodos de servidor de datos son mostrados en el diagrama. Así que ¿cuáles son nuestras opciones?

1. Denegar todas las escrituras. Si denegamos todas las escrituras entonces la red está particionada, podemos aún escribir datos completamente consistentes en ambos lados. Así que ésta es una opción. Obtenemos disponibilidad para escribir, y consistencia sostenida.
2. Permitir escrituras en un lado. Mediante algún tipo de mecanismo de consenso, podríamos dejar que un lado de la partición "gane" y tener un maestro (mostrado en el diagrama con "M"). En este caso, las lecturas y escrituras podrían ocurrir en ese lado. En otras particiones no-maestras, podríamos tanto (a) ser estrictos y no permitir operaciones, o (b) permitir lecturas eventualmente consistentes, pero no escrituras. Así, en esta situación tenemos consistencia completa en una partición, y operación parcial en el resto.
3. Permitir lecturas y escrituras en todas las particiones. Aquí, mantenemos disponibilidad, pero debemos sacrificar consistencia fuerte. Una partición no verá las operaciones y el estado de la otra hasta que la red sea restaurada. Una vez restaurada, necesitaremos un método para fusionar las operaciones que ocurrieron mientras estaba desconectado.

Una técnica de mitigación también viene a la mente. Supón que un cliente particular C tenga mucha más probabilidad de necesitar una entidad X que otros clientes. Si almacenamos la copia maestra de X en un servidor dedicado a C, incrementamos la probabilidad de que C pueda leer y escribir X en la anterior opción (2). Llamemos a ésto "buscador inteligente". Un ejemplo del mundo real de ésto podría ser "almacenar copias maestras de datos para los usuarios de la costa este en la costa este". La búsqueda inteligente no soluciona nuestros problemas, pero podría probablemente reducir su frecuencia.

Afortunadamente lo anterior es una buena "prueba" informal de CAP. Es realmente bastante simple.

Particiones de red triviales

Muchas particiones de red comunes son lo que nosotros podemos denominar triviales. Considerémoslo desde la perspectiva de la anterior opción (2). Definimos que una partición de red trivial es una tal que en todas las particiones no-maestras, no hay

* clientes vivos en todos, o
* servidores en todos

Por ejemplo si tenemos muchos centros de datos y nuestros clientes son navegadores web de Internet, y uno de nuestros centros de datos se hace completamente oscuro (y nos quedan más), es una partición de red trivial (asumimos aquí que podemos fallar sobre el estado maestro en tal situación). Asimimismo, perder un estante simple en su integridad es a menudo una partición de red trivial.



En estas situaciones, puede aún ser consistente y disponible. (Bien, para los clientes particionados, estamos no disponibles, pero por supuesto es una certeza si no puede alcanzar ningún servidor en ningún sitio)

Fuente: http://blog.mongodb.org/post/505822180/on-distributed-consistency-part-3-network