Mostrando entradas con la etiqueta Tutorial. Mostrar todas las entradas
Mostrando entradas con la etiqueta Tutorial. Mostrar todas las entradas

lunes, 15 de febrero de 2021

Cómo crear un productor de streaming de eventos para Kafka en Java



Aviso: Este pequeño tutorial no va a realizar una introducción a Kafka. Se asume que el lector ya tiene unas nociones sobre Kafka y quiere comenzar a desarrollar código en Java para enviar eventos a un topic de Kafka. Esto es habitual realizarlo, principalmente, desde microservicios o desde scripts batch.

Nota: El concepto de estado utilizado en este artículo está contextualizado en una EDA (Arquitectura Orientada a Eventos), y representa un estado originado por un evento. Dentro de Kafka se asume este concepto como record o registro, y se refiere al valor que almacenará Kafka en el bus de eventos.

Requisitos

Se asume que ya se dispone de un entorno de Kafka funcionando, y que en dicho entorno existe, al menos, un topic sobre el cual escribir eventos. Dicho entorno puede estar en la propia máquina de desarrollo (localhost), en un servidor dedicado o en un servidor en cloud.

Más adelante (en otro artículo), veremos cómo desarrollar código para suscribirnos a ese topic y poder responder, en consecuencia, a dichos eventos. Ese código corresponderá a la parte del consumidor o suscriptor. En este artículo nos centraremos exclusivamente en la parte del productor o publicador.

En el entorno de desarrollo se recomienda tener lo siguiente:
  • Java JDK versión 11 (he utilizado la versión 15)
  • Gestión de dependencias con Maven
  • El IDE que utilizado ha sido el de Spring Tools, pero con Eclipse o IntelliJ IDEA no debería haber problemas.
  • Dependencias de Kafka en Maven versión 2.7.0

Configuración de Maven


En el archivo pom.xml del proyecto Java, añadir la dependencia encargada de importar las librerías necesarias para trabajar con streams de eventos en Kafka:

<dependencies>
    <dependency>
        <groupId>org.apache.kafka</groupId>
        <artifactId>kafka-clients</artifactId>
        <version>2.7.0</version>
    </dependency>
</dependencies>

Configuración de las propiedades

El primer paso a realizar en el código, será definir las propiedades para poder configurar la conexión a Kafka. Las más importantes son las siguientes:

// Propiedades del producer
Properties props = new Properties();

// Lista de servidores Kafka a los que conectarse
props.put("bootstrap.servers", "localhost:9092");

// Serializacion de los datos de la clave (key)
props.put("key.serializer", StringSerializer.class.getName());

// Serializacion de los datos del valor (value)
props.put("value.serializer", StringSerializer.class.getName());

Para el ejemplo, usaremos el tipo String, que es el que viene por defecto. Kafka permite definir estructuras de datos mediante JSON y Avro (este es el preferido), que se definen con el objeto Serdes (SERialize/DESerialize).

A continuación se exponen algunas propiedades no tan relevantes ahora (son opcionales), pero que se podrán usar en un futuro para tunear la configuración de la conexión a Kafka:

props.put("acks", "all");
props.put("retries", 0);
props.put("batch.size", 16384);
props.put("linger.ms", 1);
props.put("buffer.memory", 33554432);


Objeto KafkaProducer

El objeto KafkaProducer permite crear un cliente para una conexión a un topic de Kafka, a partir de la información proporcionada en las propiedades descritas anteriormente.

KafkaProducer<String, String> kp = new KafkaProducer<String, String>(props);

Este objeto permite definir el tipo de datos de la clave (primer parámetro) y del valor o estado (segundo parámetro). En nuestro ejemplo utilizaremos el tipo String (si se desea trabajar con tipos customizados, ver los tipos Serdes, y cómo definir tipos en JSON o Avro).

Nota: Este objeto crea un cliente genérico a un servidor de Kafka, por lo que se puede utilizar posteriormente para enviar estados a streams de eventos a diferentes topics.

Preparación del estado (registro) a enviar

Para enviar un estado o registro a Kafka desde el productor, es necesario preparar éste mediante un objeto de tipo ProducerRecord:

ProducerRecord<String, String> pr = new Producer<String, String>(topic, key, estado);

Este objeto permite definir el tipo de datos de la clave (primer parámetro) y del valor o estado (segundo parámetro). En nuestro ejemplo utilizaremos el tipo String (si se desea trabajar con tipos customizados, ver los tipos Serdes, y cómo definir tipos en JSON o Avro).

En la construcción (entre paréntesis) se pasarán los valores correspondientes a:
  • topic: Valor del nombre del topic a usar en Kafka, donde se enviará el evento.
  • key: Valor de la clave (key) del evento o registro.
  • estado: Valor del estado o registro a enviar.
El valor de la clave puede ser opcional en otros contextos de Kafka (se almacenaría como null). Por ello, también permitiría la siguiente sintaxis:

ProducerRecord<String, String> pr = new Producer<String, String>(topic, estado);


Envío del estado a Kafka


Una vez preparado el estado, éste se envía a Kafka a través del objeto KafkaProducer definido al principio, pasándole el objeto ProducerRecord con la información del estado:

kp.send(pr);

El método send() envía el estado o registro al topic especificado.


Cerrar la conexión a Kafka


Cuando nuestro código no va a enviar más estados a Kafka, debemos cerrar el objeto KafkaProducer, para liberar recursos del stream, así como la conexión. Para ello, utilizaremos el método close():

kp.close();


Aplicación de ejemplo de productor Kafka


A continuación os dejo una aplicación completa que hace de productor Kafka.

En este ejemplo, se ejecuta desde la consola de comandos como un script, pero la base os servirá también para microservicios u otro tipo de aplicaciones.

Lo primero que hará será preguntar por el topic al cual queremos enviar los estados. Después, en un bucle, solicitará el valor del estado a enviar. Dicho estado es un texto libre, por lo que se puede introducir cualquier valor, incluso un JSON en formato String.

Este bucle se repetirá hasta que el usuario introduzca el valor 'quit' (sin comillas). En ese momento se cerrará la conexión y terminará la ejecución.


package com.rhernamperez.kafkastreamsdemo;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Date;
import java.util.Properties;

import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.apache.kafka.common.serialization.StringSerializer;
//import org.apache.kafka.streams.StreamsConfig;

/**
* Demostracion de un productor Kafka que envia eventos a un stream
* @author rafinguer
*
*/
public class KafkaProducerDemo {

    public static void main(String[] args) {

        // Propiedades del producer
        Properties props = new Properties();

        props.put("bootstrap.servers", "localhost:9092");
        props.put("key.serializer", StringSerializer.class.getName());
        props.put("value.serializer", StringSerializer.class.getName());
        props.put("acks", "all");
        props.put("retries", 0);
        props.put("batch.size", 16384);
        props.put("linger.ms", 1);
        props.put("buffer.memory", 33554432);

        // Creacion del objeto productor
        KafkaProducer<String, String> kp = new KafkaProducer<String, String>(props);
        String estado="", topic="", key = "";

        // Mensaje de bienvenida
        System.out.println("Demostracion de Kafka producer. Introduce datos para cada evento. 'quit' para salir\n");

        // Introduccion del topic por teclado
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        System.out.println("Introduce el nombre del topic: ");

        try {
            topic = br.readLine();
        } catch (Exception e) {
            System.out.println("ERROR > " + e.getMessage());
        }

        // Bucle para introducir estados hasta que se escriba 'quit'
        while(!estado.equals("quit")) {

            try {
                // Lectura de estados por teclado
                System.out.print(">>> ");
                estado = br.readLine();

                if (estado.equals("quit")) continue;

                // Envio del estado. La key sera la fecha y hora actuales
                key = new Date().toString();
                ProducerRecord<String, String> pr = new ProducerRecord<String, String>(topic, key, estado);
kp.send(pr);

                System.out.println("Enviado a topic " + topic + " la clave " + key + " con el estado > " + estado + "\n");
            } catch(Exception e) {
                System.out.println("Error > " + e.getMessage());
            }
        }

        // Cerrar el stream
        kp.close();

        System.out.println("**** FIN ****");
    }

}


Enlaces de interés







miércoles, 6 de mayo de 2020

Utiliza bases de datos en tus proyectos web sin servidores

EFEMem Web Database
¿Imaginas poder usar una base de datos en tu web de forma exclusiva y sin necesidad de hacer llamadas remotas, como una API?
EFEMem DB es una base de datos NoSQL ultra ligera, rápida, sencilla y eficiente, basada en clave-valor, que se ejecuta enteramente en memoria. Está enteramente escrita en JavaScript, y se puede ejecutar directamente en el front-end, a través de páginas web, aplicaciones PWA (Progressive Web Applications) o aplicaciones móviles híbridas (desarrolladas con Ionic, React Native, Nativescript, Xamarin, etc).

Casos de uso

¿Cuáles son los escenarios ideales para usar una base de datos en un proyecto web?. He aquí algunos casos:
  • Pérdida conexión. Tu aplicación puede seguir funcionando cuando no tienes conectividad con el servidor. Cuando recuperes la conexión, puedes volcar toda la información recopilada al servidor. Esta característica asegura la alta disponibilidad de tu aplicación.
  • Juegos HTML. Los datos de las partidas se guardan en cada navegador o dispositivo.
  • Datos de sesión de usuario
  • Datos exclusivos de la aplicación web
  • Datos de configuración. Permite cambios "en caliente", eliminando la característica de datos estáticos.
  • Monitorización de datos. Puedes, por ejemplo, conectar tus dispositivos IoT a tu web, y almacenar un rango fijo de datos cíclicos (por ejemplo, las últimas 24 horas).
  • Datos caché de uso frecuente
  • Datos maestros


Características principales

Los puntos fuertes de EFEMem DB son los siguientes:
  • NoSQL. Olvídate de la complejidad de relaciones de tablas o esquemas estrictos. Utiliza pares de clave-valor.
  • Acceso ultra-rápido de lectura y escritura (unos pocos nanosegundos)
  • No requiere de instalación en un servidor ni de un canal de sockets. Es una librería JavaScript que trabaja directamente con los datos.
  • Configuración en caliente. Puedes cambiar la configuración sin reiniciar el motor de base de datos.
  • Modo de datos reciclados. Mediante este modo se puede configurar un máximo de claves. Cuando se alcanza este límite, el siguiente dato se guardará tras eliminar, automáticamente, la cave más antigua.
  • Datos con tiempo de vida. Puedes guardar un dato que expire en un tiempo concreto (expresado en segundos).
  • Comandos poderosos. Puedes gestionar tus datos mediante unos pocos y sencillos comandos.
  • Persistencia. Puedes guardar los datos de forma permanente. Para ello, utiliza localStorage y restaurarlo al iniciar la base de datos o cuando quieras.
  • Espacios. Puedes organizar tus claves mediante espacios. Esto permite tener claves con el mismo nombre y distinto valor, en diferentes espacios. También ayuda a localizar e identificar las claves.
  • Patrones de nombre. Puedes buscar y acceder de forma eficiente a multitud de espacios y claves utilizando patrones de nombre, en lugar de acceder uno a uno.
  • Riqueza de tipos de datos. Puedes usar números enteros o reales, cadenas de texto, valores booleanos, arrays u objetos JSON


Comandos básicos

Lo mejor para entender cómo funciona EFEMem DB es el ejemplo.

Importar la librería

Lo primero es descargar la librería efememdb.js, que contiene todo el motor de la base de datos. Se encuentra en el repositorio Github: https://github.com/efememdb/EFEMemDB
Una vez descargado, en nuestro código HTML, importamos la librería:
<SCRIPT src="[/path/]efememdb.js"></SCRIPT>


/ Una vez importado, el motor de EFEMem DB entra en funcionamiento. Si hubo datos persistidos, automáticamente se cargarán estos para su uso.

Guardar datos con set()

Comencemos con el comando set(). Este comando guarda una clave y su valor:
<SCRIPT> var result = efemem.set("saludo", "Hola Mundo"); </SCRIPT>


Si no se especifica un nombre de espacio, asumirá el espacio 'public' por defecto.
Si el espacio y la clave no existen, se creará esta nueva clave en dicho espacio. Si ya existía previamente el espacio y la clave, el valor de ésta se actualizará con el nuevo valor.
El resultado de este comando sería el siguiente:
{   "ok":true,   "cmd":"set(key, value[,space[,due]])",   "data":{     "key":"saludo",     "space":"public",     "value":"Hola Mundo",     "due":"9999-12-31T22:59:59.000Z" },   "msg":"Key 'saludo' saved successfully in space 'public'",   "affected":1,   "time":"< 1 ms" }


Si se produjese un error, la respuesta de cualquier comando de EFEMem DB sería la siguiente:
{   "ok":false,   "msg":"[mensaje de error]", }


Recuperar datos con get()

El comando get() permite recuperar datos a través de la clave:
<SCRIPT> result = efemem.get("saludo"); </SCRIPT>


Si no se especifica un nombre de espacio, por defecto todos los espacios existentes. Lo mismo ocurre si no se especifica un nombre de clave. El comando get() (al igual que otros comandos), utilizan patrones de nombre para referirse a un espacio o a una clave. El nombre que se pase, se tomará como "parte del nombre". Es decir, que la cadena de caracteres especificada se utilizará como parte del nombre, ya sea al principio, en el medio o al final, muy similar a los patrones o comodines utilizados en un sistema operativo. Si se especifica un patrón 'al', es como si se especificara '*al*', con lo que buscaría cualquier nombre que empiece por 'al', termine en 'al' o que contenga 'al' en cualquier parte del nombre.
El resultado de este comando retorna un array o lista con todas las claves encontradas:
{   "ok":true,   "cmd":"get(key[,space])",   "data":[     {"key":"public~saludo","value":"Hola Mundo"}   ],   "msg":"1 values found",   "affected":1,   "time":"1 ms" }


Lista de comandos

Los comandos set() y get() son los comandos más básicos y utilizados de EFEMem DB. Pero tienes a tu disposición un conjunto de comandos que te ayudarán a gestionar de forma eficiente tu base de datos:
  • copy(): Copia claves de un espacio a otro
  • delete(): Elimina claves en los espacios indicados
  • get(): Recupera claves y valores de los espacios indicados
  • getConfig(): Recupera el valor de un parámetro de configuración
  • info(): Muestra la información sobre el uso de EFEMemDB
  • keys(): Retorna las claves definidas en los espacios indicados
  • memory(): Muestra la memoria utilizada por las claves de los espacios indicados
  • move(): Mueve claves de un espacio a otro
  • persist(): Guarda o persiste los datos actuales
  • rename(): Cambia el nombre de una clave
  • restore(): Carga o recupera los datos persistidos
  • set(): Guarda una clave en un espacio
  • setConfig(): Crea o modifica un parámetro de configuración
  • spaceInfo(): Muestra la información de un espacio dado
  • spaces(): Retorna la lista de espacios utilizados


Enlaces de interés

Imagen original en pxfuel: https://www.pxfuel.com/en/free-photo-elhvj

domingo, 2 de diciembre de 2012

Gestión de software y paquetes en Fedora

El gestor de paquetes yum

La distribución Fedora trae consigo un potente y avanzado gestor de paquetes llamado yum, basado en el clásico rpm. Este gestor se utiliza desde la consola de comandos. He aquí los comandos más básicos:

$ man yum
Ayuda sobre yum

$ yum list
Lista todos los paquetes del repositorio de yum

$ yum list available
Lista los paquetes disponibles

$ yum list updates
Lista los paquetes con actualizaciones

$ yum list installed
Lista los paquetes instalados

$ yum install paquete1 [paquete2...]
Instala los paquetes especificados

$yum update paquete1 [paquete2...]
Actualiza los paquetes especificados

$ yum check-update
Verifica si hay alguna actualización nueva del alguno de los paquetes instalados

$ yum remove paquete1 [paquete2...]
Elimina un paquete del sistema

$ yum erase paquete1 [paquete2...]
Idem a remove


Centro de Software Yum Extender (yumex)
A muchos usuarios, el uso de comandos les puede parecer complicado, y prefieren una interfaz gráfica que le permita gestionar mejor los paquetes. Esta interfaz se llama Yum Extender, y permite buscar y seleccionar los paquetes que necesitamos de una manera muy visual e intuitiva, ya sea por el nombre de la aplicación, por la categoría de software o el grupo, de forma muy similar al Centro de Software que tiene Ubuntu o Linux Mint.


Para instalar Yum Extender, escribir el siguiente comando en la consola:

$ su -c 'yum install yumex'

Solicitará la constraseña de administador para su instalación. 


El gestor de paquetes apt

El gestor de paquetes apt es el utilizado por las distribuciones basadas en Debian, tales como Ubuntu o Linux Mint. Además de su sencillez, destaca por la cantidad de software que contiene sus repositorios.

Es posible añadir este gestor de paquetes a Fedora, complementando a yum. Para ello, lo instalamos mediante el siguiente comando desde la consola:

$ sudo yum install apt

Los comandos más básicos de apt son los siguientes:

$ apt-get -h
Ayuda sobre apt

$ apt-get install paquete1 [paquete2...]
Instala los paquetes especificados

$ apt-get remove paquete1 [paquete2...]
Desinstala los paquetes especificados

$ apt-get update paquete
Actualiza el paquete a la última versión

$ apt-get upgrade
Actualiza el sistema a la última versión de todos los paquetes instalados


Centro de Software Synaptic

El centro de software Synaptic es el utilizado por las distribuciones basadas en Debian, basada en apt, y permite la instalación de paquetes rpm y deb.


Para instalar Synaptic, ejecutar el siguiente comando desde la consola:

$ sudo yum install synaptic

Por defecto, Synaptic utilizará el repositorio de Fedora para la gestión de paquetes, pero es posible especificar otros repositorios y ampliar así el inventario y las posibilidades de software de nuestro sistema Linux.



Enlaces de Interés
Comandos yum | Tutorial de comandos yum
Comandos apt | Tutorial de comandos apt
Yum Extender | Página oficial de Yum Extender
Synaptic | Página oficial de Synaptic

martes, 10 de julio de 2012

Primeros pasos en MongoDB

Introducción
MongoDB es una base de datos opensource que está teniendo mucha aceptación por las prestaciones que ofrece en entorno Web 2.0, aunque puede ser utilizada en cualquier tipo de situaciones. MongoDB acerca el sistema de almacenamiento y gestión de datos tipo clave/valor, puliendo la diferencia con respecto a los sistemas de bases de datos relacionales. Este sistema permite una tremenda rapidez y escalabilidad, frente a la funcionalidad de los sistemas de bases de datos tradicionales.

Las principales características de MongoDB son las siguientes:
- Software abierto
- Escalable
- Alto rendimiento
- Alta disponibilidad (puede trabajar en modo maestro-esclavo)
- Orientado a documentos (no es relacional)
- Simplicidad basada en esquemas de tipo JSON
- Consultas dinámicas
- Completo soporte de índices, incluyendo índices secundarios, objetos internos, arrays (cadenas) embebidos, geospacial
- Rápido, actualizaciones in situ.
- Perfilado de consultas
- Almacenamiento eficiente de datos binarios en objetos largos, tales como vídeos o fotografías
- Replicación y soporte a prueba de fallos
- Auto fragmentación para escalabilidad a nivel de nube.
- Agregación compleja mediante MapReduce
- Acceso y gestión mediante drivers en multitud de lenguajes de programación: C, C++, C#, .NET, Java, JavaScript, PHP, Phyton, Ruby, Perl, etc.
- Soporte, formación y consultoría.

En este artículo (que espero sea el primero de muchos), se realiza una pequeña introducción a MongoDB.

Orientación a documentos
La información en MongoDB no se almacena en tablas (con sus correspondiente filas y columnas), si no en colecciones (estructuradas o no) cuyos datos forman parejas de clave y valor. Estos datos se almacenan con un estilo JSON, en formato binario llamado BSON. Un ejemplo de este estilo se puede apreciar en el siguiente documento:

{ nombre: ‘Rafael’,
apellidos: ‘Hernamperez Martin’,
fechaingreso: Date(’03-22-2010’),
seleccion: [‘Aprenda MongoDB’,’Flex 4 en una semana’,’AJAX para Dummies’],
comentarios: [{autor: ‘adan3000’, comentario: ‘Buena eleccion’},
{autor: ‘majopero’, comentario: ‘Te has pasado’, puntuacion:5}
]
}


Una colección sería similar a una tabla, y un documento sería similar a una fila. Habría que distinguir claramente, pues la colección puede tener documentos con estructura similar pero no igual (algunos documentos podrían tener más o menos claves). Asimismo, una única clave podría tener una colección de valores (array o cadena, como en el caso de la clave “seleccion"), o bien podría ser también una sub-colección con sus respectivos documentos (como en el caso de la clave “comentarios”).

La sintaxis en formato JSON es muy fácil de entender, eliminando los problemas de errores que pueden ocurrir en el formato XML (ambos son muy sinérgicos), además de reducir el tamaño de la información a transportar.

Este formato es utilizado no sólo para entenderlos, sino también para gestionarlos y realizar consultas en sus campos internos. Por ejemplo:

db.compras.find({'nombre':'Rafael'})

Nota: si falla, probar con comillas dobles

Esta consulta localizaría todos los documentos dentro de la colección “compras” cuya clave “nombre” tenga el valor “Rafael”.

Otro ejemplo:

db.compras.find({'comentarios.autor':'adan3000'})

Esta consulta localizaría todos los documentos dentro de la colección “compras” cuyo comentario haya sido realizado por el “autor” llamado “adan3000”.


Instalación
Para nuestros propósitos utilizaremos Windows como plataforma de operaciones. También puede instalarse en sistemas operativos OS X, Linux y Solaris (ver instrucciones en http://www.mongodb.org/display/DOCS/Quickstart)

En primer lugar hay que crear la siguiente ruta de directorio: “c:\data\db”. Esta ruta es la ruta por defecto para los archivos de bases de datos.

A continuación descargar el archivo zip (ocupa apenas 13MB) de la siguiente URL:
http://www.mongodb.org/display/DOCS/Downloads
y descomprimirlo en la ruta que deseemos (por ejemplo en C:\ donde creará un subdirectorio llamado “mongodb-winxx-xxxx”).


Arrancar un servidor MongoDB
MongoDB se ejecuta principalmente en su consola. Para ello se accede al modo consola DOS de Windows (pulsar AltGr+R, escribir “cmd” (sin las comillas) y Aceptar).

Acceder al directorio “bin” de donde se descomprimió el fichero zip (ejemplo):

cd c:\mongodb-win32-i386-1.2.4\bin

Ahora, para lanza MongoDB por defecto, ejecutar el ejecutable:

mongod

Con esto, MongoDB accederá a las bases de datos almacenadas en el directorio
c:\data\db y usando el puerto 27017. Si se desea cambiar el directorio de ficheros de bases de datos o el puerto, usar los siguientes parámetros:

mongod --dbpath [rutadirectorio] --port [puerto]

Si acaso saltase el cortafuegos, desbloquear el acceso para poder usarlo.

Para parar la base de datos, pulsar Ctrl+C. Con ello, MongoDB esperará hasta que todas las operaciones se hayan completado, guardando las últimas transacciones y cerrando los ficheros.

Existen otros parámetros que pueden ser usados por el motor de MongoDB:
-h (--help): Muestra información sobre los parámetros permitidos
--logpath rutafichero: Especifica fichero de log
--logappend: añade al log, en lugar de sobreescribirlo
--cpu: log periódico de la CPU y de los tiempos de entrada/salida
--fork: ejecución como demonio
--auth: Activa la seguridad
--noauth: Desactiva la seguridad (por defecto)
--nohttpinterface: Desactiva la interfaz http (localhost:27018)
--master: Designa este servidor como maestro (entorno de alta disponibilidad)
--slave: Designa este servidor como esclavo (entorno de alta disponibilidad)
--autoresync: Resincronización automática del servidor esclavo.
--source servidor:puerto: Para un servidor esclavo especifica dónde está el servidor maestro para la replicación

Una forma sencilla de arrancar el servidor MongoDB sin repetir los parámetros, es añadiendo éstos a un fichero de configuración. El formato del fichero sería el siguiente:

#comentario
parametro1 = valor1
parametro2 = valor 2
…


Para lanzar el servidor usando este fichero, ejecutar:

mongod –config ficheroconfiguración
mongod –f ficheroconfiguración


Consola de MongoDB
Una vez arrancado el servidor, podemos utilizar la consola de MongoDB para interactuar con las bases de datos. Para ello, acceder al modo consola DOS de Windows (pulsar AltGr+R, escribir “cmd” (sin las comillas) y Aceptar).

Acceder al directorio “bin” de donde se descomprimió el fichero zip (ejemplo):

cd c:\mongodb-win32-i386-1.2.4\bin

Ahora, lanzar la consola de MongoDB:

mongo

(nótese que no tiene la "d" final).

En esta nueva consola, nos permitirá escribir los comandos necesarios para interactuar con el servidor (mongod), de tal forma que podamos gestionar documentos y estructuras o acceder a la información (entre muchas acciones). Cada comando ha de estar acompañado por un “Enter” para su ejecución.

Por defecto se conecta a una base de datos llamada “test” (por defecto).

Para mostrar la base de datos en uso:
db

Para autentificar un usuario (sólo cuando se ejecuta el servidor en modo seguridad):
db.auth(usuario,contraseña)

Para salir de la consola MongoDB, escribir el comando
exit

Para conseguir ayuda sobre los comandos disponibles, escribir el comando
help

Para mostrar las bases de datos disponibles:
show dbs

Para mostrar las colecciones de la base de datos actual:
show collections

Para mostrar los usuarios de la base de datos actual:
show users

Para utilizar una base de datos:
use nombrebasedatos

Para mostrar ayuda sobre los métodos de base de datos:
db.help()

Para mostrar ayuda sobre los métodos para la colección foo:
db.foo.help()

Para mostrar los objetos en una colección foo:
db.foo.find()

Referencias
Sitio oficial de MongoDB: http://www.mongodb.org


Safe Creative #1003225810323

Conceptos básicos de MongoDB

Filosofía de almacenamiento en MongoDB

La filosofía de las bases de datos NOSQL suele ser chocante para todos los que llevan años trabajando en bases de datos relacionales. El no tener el concepto de una tabla, o de una integridad referencial con su relación de clave maestra a clave foránea, se hace difícil imaginar cómo puede funcionar y cómo pueden relacionarse y entenderse los datos.

MongoDB tiene el concepto de la información almacenada como clave/valor. Estos pares se almacenan en forma de documento u objeto dentro de una colección. Podemos imaginar un símil entre clave/valor como campo/valor, entre un objeto o documento y una fila, y entre colección y tabla. Este concepto puede ayudar a entender un poco mejor este sistema, pero no hay que olvidar que es un símil, no una equiparación.

Un documento u objeto (los dos términos se refieren a lo mismo), aunque se asemeje a una fila, en realidad no tiene nada que ver, pues en una base de datos relacional, cada fila tiene una organización estructurada común entre todas las filas. En MongoDB, cada fila puede tener su propia estructura, tener más o menos campos, e incluso tener campos que en sí mismos son arrays (contener varios valores) o incluso contener otro documento como valor, o incluso un array de documentos. Esto, entendido bien, nos puede dar una idea de la potencia que ello implica, pues es posible tener en un mismo objeto, de manera incrustada, otros objetos, sin necesidad de implicar a la base de datos en varias tablas, definir claves y relacionar dichas claves. Por ejemplo, imaginemos la clásica relación “Categoría” y “Producto”. En un sistema de base de datos tradicional, se definirían dos tablas:

Tabla categoría:
- idcategoria: integer: PRIMARY KEY
- nombrecategoria: char(30)

Tabla producto:
- idproducto: integer: PRIMARY KEY
- nombreproducto: char(30)
- idcategoria: integer

Internamente, el gestor de base de datos debe estar constantemente velando para que los datos clave sean únicos, no nulos y que no violan las reglas de integridad referencial, realizando complejas operaciones de índices y actualización de éstos. Estas operaciones son transparentes para los usuarios y desarrolladores. Es cómodo, pero sobrecargan los tiempos de CPU y penalizan otras operaciones que pueden ser más importantes.

En una base de datos MongoDB se requeriría únicamente una colección de objetos, cada uno de los cuales puede tener una estructura propia (no tiene por qué ser la misma).

{producto: “Perdiz escabechada”, categoria:[“carne”,”conserva”]}
{producto:”Naranja”, categoria:”fruta”]}
{producto:”Sal”}


De este simple ejemplo se pueden extraer algunas reflexiones:
- El almacenamiento físico gana mucho, al no estar supeditada a una estructura fija y definida. Se pueden omitir claves (campos) si se desea, y los campos de texto ocupan sólo el número de caracteres que contiene, no un tamaño fijo.
- Se prescinde de campos id, que dificultan el entendimiento de los datos.
- Se centraliza todo en una única colección, y no añade la dificultad de las relaciones.
- Un producto puede no estar asociado a una categoría, o bien estar asociado a varias categorías. En este último caso, en una base de datos relacional, requeriría de una tercera tabla intermedia con la colección de relaciones, y el trabajo extra en el código para reconstruir las mismas.
- Si bien el control de la redundancia en las categorías es un esfuerzo por parte del código, en el caso de una base de datos relacional, también habría que hacer un esfuerzo en código cuando se determina si hay redundancia por los errores que emite la base de datos (clave duplicada, infracción de integridad…).
- El modo de almacenamiento es más natural para la máquina y para el humano, pues toda la información está en el mismo documento, en lugar de repartido. Esto evita al código repartir la información (al guardar) y de reunirla (al acceder).
- En un modelo relacional, utilizar id’s reduce el espacio de almacenamiento en tablas extensas (ocupa mucho menos un número que un texto), pero complica el desarrollo y el acceso. MongoDB reduce y optimiza espacio de almacenamiento en los campos de texto, supliendo este espacio e incluso mejorándolo. Asimismo, el código para guardar o acceder a la información es mucho más simple (no hay que realizar relaciones (los típicos join o el uso de varias consultas a varias tablas) ni realizar varias actualizaciones por cada una de las tablas involucradas).

Otro ejemplo de almacenamiento en un documento u objeto sería el siguiente:
{ nombre: ‘Rafael’,
apellidos: ‘Hernamperez Martin’,
fechaingreso: Date(’03-22-2010’),
seleccion: [‘Aprenda MongoDB’,’Flex 4 en una semana’,’AJAX para Dummies’],
comentarios: [{autor: ‘adan3000’, comentario: ‘Buena eleccion’},
{autor: ‘majopero’, comentario: ‘Te has pasado’, puntuacion:5}
]
}


La clave “comentarios” es un array de documentos. Un documento puede contener, asimismo, documentos asociados a una clave. Es lógico suponer que el nivel de anidamiento puede ser tan profundo como uno desee.

El formato de datos utilizado por MongoDB es JSON, una especificación estándar para representar la información. Es similar a la de XML, pero reduce el contenido a expresar y haciendo más legible y natural la interpretación de la información. El último documento en formato XML sería el siguiente:
<documento>
  <nombre>Rafael</nombre>
  <apellidos>Hernamperez Martin</apellidos>
  <fechaingreso>03-22-2010</fechaingreso>
  <seleccion>
    <titulo>Aprenda MongoDB</titulo>
    <titulo>Flex 4 en una semana</titulo>
    <titulo>AJAX para Dummies</titulo>
  </seleccion>
  <comentarios>
    <comment autor=”adan3000” comentario=”Buena elección”/>
    <comment autor=”majopero” comentario=”Te has pasado” puntuación=”5”/>
  </comentarios>
</documento>


Alguno se estará preguntando cómo mantener la consistencia de los datos en las aplicaciones. Por ejemplo, en una aplicación es conveniente evitar al usuario teclear la categoría, pudiendo seleccionar una ya predeterminada en una lista para asegurar que sea unívoca y que no haya multitud de referencias a un mismo valor que esté redundante porque se diferencia en una letra o está mal escrito. Se puede crear una colección de categorías, en un formato muy similar al de una tabla (usando documentos con una única clave), y usar ésta como se haría normalmente, o incluso añadir un documento en nuestra colección cuya clave sea un array de valores posibles. Aunque su estructura no tenga nada que ver con el resto de documentos almacenados en la misma. Se accede a dicho documento dentro de la colección y se extraen los posibles valores. La primera opción es más sencilla y legible. La última es más óptima en cuanto almacenamiento y gestión por parte del motor de base de datos (trabaja en la misma colección y comparte los mismos ficheros). Otra solución intermedia, y a la vez elegante, sería definir una colección exclusiva para almacenar documentos que contengan series de datos maestros (categorías, tipos, etc.)

Otra de las ventajas de utilizar este sistema de información, es que no tienes tantas limitaciones a la hora de escalar la información si los requisitos cambian (cosa que ocurre, pues nadie conoce el futuro). En bases de datos relacionales, añadir nuevos campos a una tabla, o una nueva tabla relacionada o maestra y normalizar una base de datos que lleva ya tiempo en producción, es cuanto menos un engorro y un agujero de problemas.

Una vez se entienden estos sencillos conceptos, el adaptar nuestros desarrollos a este tipo de bases de datos es sencillo, e incluso nos beneficiaremos de una mayor legilibilidad y rapidez.


Paso a paso
El movimiento se demuestra andando, y para aprender a caminar en MongoDB procederemos a realizar, paso a paso, un ejemplo práctico. El propósito del mismo es tener una colección de datos personales llamada “agenda”, dentro de una base de datos llamada “ejemplo”. En el ejemplo se verá cómo crear la base de datos, la colección y los datos, y a continuación se verá como realizar consultas a dichos datos.

Arranque del servidor y de la consola MongoDB

Primeramente, arrancar el servidor de MongoDB desde una consola DOS (para Linux, los pasos son muy similares):

cd c:\mongodb-win32-i386-1.2.4\bin
mongod


A continuación, arrancar la consola de MongoDB (dbShell) en otra consola DOS:

cd c:\mongodb-win32-i386-1.2.4\bin
mongo



Creación de la base de datos y de la colección

Aunque no se haya creado aún la base de datos, utilizar ésta (asumirá que se va a utilizar para ser creada):

> use ejemplo
switched to db ejemplo


A continuación crear un objeto que contendrá un documento, el cual se insertará en la colección “agenda” (aún no creada):

> doc = {nombre: "Rafael", apellido1: "Gonzalez", apellido2: "Martin", telefono: "912406790"}

{
"nombre" : "Rafael",
"apellido1" : "Gonzalez",
"apellido2" : "Martin",
"telefono" : "912406790"
}


El siguiente paso es añadir este objeto (documento) a la colección “agenda”:

> db.agenda.save(doc)

Se puede abreviar el proceso en un solo paso, creando directamente el objeto:

> db.agenda.save({nombre: "Rafael", apellido1: "Gonzalez", apellido2: "Martin", telefono: "912406790"})

Esto equivaldría a la sentencia SQL:

INSERT INTO agenda (nombre, apellido1, apellido2, telefono) VALUES (‘Rafael’, ‘Gonzalez’, ‘Martin’, ‘912406790’)

Automáticamente, MongoDB crea los objetos por asunción. De esta manera, crea la base de datos “ejemplo”, y dentro de ésta crea la colección “agenda”, y dentro de ésta crea y agrega el documento “doc”.


Verificaciones

Para verificar todo lo anterior primero comprobaremos qué base de datos está en uso:

> db
ejemplo


A continuación, listaremos las bases de datos creadas con MongoDB:

> show dbs

admin
ejemplo
local
test


Las bases de datos “admin”, “local” y “test” son las bases de datos que por defecto tiene MongoDB.

La siguiente verificación será comprobar qué colecciones disponemos en la base de datos en uso (“ejemplo”):

> show collections
agenda
system.indexes


La colección “system.indexes” es creada y mantenida de forma automática por MongoDB para el control y gestión de los índices de las colecciones.

Para visualizar solamente las colecciones no internas de MongoDB, se puede usar el siguiente comando:

> db.getCollectionNames()
[ "agenda", "system.indexes" ]


Para conocer a qué base de datos pertenece una determinada colección:

> db.agenda.getDB()
ejemplo


Para conocer qué comandos se puede usar para tratar la colección:

> db.agenda.help()


Consultas a los datos

Para visualizar todos los objetos (documentos) de la colección:

> db.agenda.find()
{ "_id" : ObjectId("4ba8a3be5b3d00000000710f"), "nombre" : "Rafael", "apellido1" : "Gonzalez", "apellido2" : "Martin", "telefono" : "912406790" }


Automáticamente, MongoDB asigna un ID único a cada objeto de la colección (clave “_id”).

Vamos a añadir más documentos a la colección:

db.agenda.save({nombre:"Carlos",apellido1:"Sanchez",apellido2:"Sanchez",telefono:"91240890012",email:"carlosss@hotmail.com"}) db.agenda.save({nombre:"Javier",apellido1:"Cristobal",apellido2:"Nombela",telefono:"925407561",email:"javichuc@yahoo.es"})
db.agenda.save({nombre:"Yolanda",apellido1:"Ballesteros",apellido2:"Lopez",telefono:"925406902"})
db.agenda.save({nombre:"Antonio",apellido1:"Blazquez",apellido2:"Fernandez",telefono:"937607812"})
db.agenda.save({nombre:"Jose Miguel", apellido1:"Carvajal", apellido2:"Gomez", telefono:"983679103", email:"picachu234@gmx.com"})
db.agenda.save({nombre:"Juan Carlos", apellido1:"Blazquez", apellido2:"Gil", telefono:"925403789"})


Lista de todos los objetos de la colección:

> db.agenda.find()
{ "_id" : ObjectId("4ba8a3be5b3d00000000710f"), "nombre" : "Rafael", "apellido1" : "Gonzalez", "apellido2" : "Martin", "telefono" : "912406790" }
{ "_id" : ObjectId("4ba8aac55b3d000000007110"), "nombre" : "Carlos", "apellido1" : "Sanchez", "apellido2" : "Sanchez", "telefono" : "91240890012", "email" : "carlosss@hotmail.com" }
{ "_id" : ObjectId("4ba8ab435b3d000000007111"), "nombre" : "Javier", "apellido1" : "Cristobal", "apellido2" : "Nombela", "telefono" : "925407561", "email" : "javichuc@yahoo.es" }
{ "_id" : ObjectId("4ba8ac3f5b3d000000007112"), "nombre" : "Yolanda", "apellido1" : "Ballesteros", "apellido2" : "Lopez", "telefono" : "925406902" }
{ "_id" : ObjectId("4ba8ac865b3d000000007113"), "nombre" : "Antonio", "apellido1" : "Blazquez", "apellido2" : "Fernandez", "telefono" : "937607812" }
{ "_id" : ObjectId("4ba8acde5b3d000000007114"), "nombre" : "Jose Miguel", "apellido1" : "Carvajal", "apellido2" : "Gomez", "telefono" : "983679103", "email" : "picachu234@gmx.com" }
{ "_id" : ObjectId("4ba8af123433000000003118"), "nombre" : "Juan Carlos", "apellido1" : "Blazquez", "apellido2" : "Gil", "telefono" : "925403789" }


Esto equivaldría a la sentencia SQL:

SELECT * FROM agenda

Cuando la colección contiene múltiples objetos será necesario introducir criterios en la búsqueda. El comando “find” permite pasar como parámetros dichos criterios (en formato JSON), los cuales recogen el par (clave/valor) a encontrar. El siguiente comando localiza todos los objetos en cuyo primer apellido sea “Blazquez”:

> db.agenda.find({"apellido1":"Blazquez"})
{ "_id" : ObjectId("4ba8ac865b3d000000007113"), "nombre" : "Antonio", "apellido1" : "Blazquez", "apellido2" : "Fernandez", "telefono" : "937607812" }
{ "_id" : ObjectId("4ba8af123433000000003118"), "nombre" : "Juan Carlos", "apellido1" : "Blazquez", "apellido2" : "Gil", "telefono" : "925403789" }


Lo anterior equivaldría a la sentencia SQL:

SELECT * FROM agenda WHERE apellido1=”Blazquez”

Mediante el siguiente comando sabremos cuántos objetos tiene la colección:

> db.agenda.count()

En un conjunto de datos, para limitar el número de objetos retornados, se especificaría añadiendo el comando limit():

> db.agenda.find().limit(3)
{ "_id" : ObjectId("4ba8a3be5b3d00000000710f"), "nombre" : "Rafael", "apellido1" : "Gonzalez", "apellido2" : "Martin", "telefono" : "912406790" }
{ "_id" : ObjectId("4ba8aac55b3d000000007110"), "nombre" : "Carlos", "apellido1" : "Sanchez", "apellido2" : "Sanchez", "telefono" : "91240890012", "email" : "carlosss@hotmail.com" }
{ "_id" : ObjectId("4ba8ab435b3d000000007111"), "nombre" : "Javier", "apellido1" : "Cristobal", "apellido2" : "Nombela", "telefono" : "925407561", "email" : "javichuc@yahoo.es" }


El comando count() también se puede añadir a find() para saber cuántos objetos ha retornado:

> db.agenda.find({apellido1:"Blazquez"}).count()
2


En el caso de querer visualizar solamente el primero de un conjunto de datos retornados, se usaría el comando findOne():

> db.agenda.findOne({apellido1:"Blazquez"})
{
"_id" : ObjectId("4ba8ac865b3d000000007113"),
"nombre" : "Antonio",
"apellido1" : "Blazquez",
"apellido2" : "Fernandez",
"telefono" : "937607812"
}


Para especificar más criterios, éstos se separan por comas:

> db.agenda.find({"apellido1":"Blazquez","nombre":"Antonio"})
{ "_id" : ObjectId("4ba8ac865b3d000000007113"), "nombre" : "Antonio", "apellido1" : "Blazquez", "apellido2" : "Fernandez", "telefono" : "937607812" }


Su equivalente en SQL sería el siguiente:

SELECT * FROM agenda WHERE apellido1=”Blazquez” AND nombre=”Antonio”

El comando find() retorna realmente un cursor, el cual puede ser utilizado para un acceso más controlado. El siguiente ejemplo se declara una variable que recoge el cursor del comando find(), situándose antes del primer registro. A continuación se declara un bucle while que se repetirá mientras el cursor tenga elementos o no alcance el final (hasNext()). En este bucle se imprimirá, en formato JSON, el objeto actual sobre el cual está situado el cursor. Mediante el comando next(), el cursor avanzará al siguiente objeto.

> var cursor = db.agenda.find()
> while (cursor.hasNext()) { print(tojson(cursor.next())); }
{
"_id" : ObjectId("4ba8a3be5b3d00000000710f"),
"nombre" : "Rafael",
"apellido1" : "Gonzalez",
"apellido2" : "Martin",
"telefono" : "912406790"
}
…


El siguiente ejemplo, recoge el cursor y accede directamente al tercer objeto del mismo:

> var cursor=db.agenda.find()
> print(tojson(cursor[3]))
{
"_id" : ObjectId("4ba8ac3f5b3d000000007112"),
"nombre" : "Yolanda",
"apellido1" : "Ballesteros",
"apellido2" : "Lopez",
"telefono" : "925406902"
}


La variable “cursor” se podría ver como un array de objetos, por lo que si se quiere acceder a cualquier clave del mismo, se especifica mediante un punto, como si fuera una propiedad:

> print(cursor[3].nombre, cursor[3].apellido1, cursor[3].telefono)
Yolanda Ballesteros 925406902



Safe Creative #1003235820060

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

miércoles, 11 de enero de 2012

Conceptos básicos de Ruby

En el presente post vamos a tomar un barniz del lenguaje de programación Ruby. Para ello, se presupone que el lector ya tiene instalado Ruby en su equipo (Descarga de Ruby). Recomiendo leer mi post anterior (Primera toma de contacto con Ruby), el cual da unas nociones preliminares, y que seguro será útil para el lector.

Para entender los conceptos claramente, lo mejor es ilustrarlo con un ejemplo muy sencillo:


La línea 1 es un comentario. No ejecuta nada. Es un texto que sirve para aclarar qué se va a hacer. El comentario abarca desde que aparece el símbolo pragma (#), hasta el final de la línea.

La línea 2 (print('Escribe tu nombre: '), visualiza en pantalla el texto encerrado entre las comillas, sin realizar un retorno de carro (línea siguiente). Da igual utilizar comillas simples o dobles.

La línea 3 declara una variable (nombre), a la cual se le asigna (mediante el símbolo de "igual") la expresión que hay a la derecha, en este caso, la función gets, la cual captura mediante teclado una cadena texto, hasta que el usuario pulsa Enter. El nombre de la función se debe a "get" (obtener) y "s" ("string" o cadena de texto).

La línea 4 realiza lo mismo que la línea 2, pero con otro texto. La línea 5 define otra variable (edad), a la cual le asigna la cadena capturada por gets, pero cuyo resultado (la cadena), es convertida a un número entero mediante la función to_i ("to integer" o "a entero"). Esta línea es interesante porque se pueden explicar varias cosas interesantes. En primer lugar, las funciones suelen escribirse con paréntesis, aunque no tengan argumentos. En la línea 3 se omitieron los paréntesis, ya que Ruby lo interpreta de la misma manera. Otro punto interesante es que, en Ruby, todo es un objeto. Los objetos tienen propiedades (valores) y acciones (métodos o funciones), a los cuales se accede escribiendo un punto después del objeto, y escribiendo el nombre de la propiedad o de la acción. En este caso, gets retorna una cadena de texto, que en sí es un objeto, por lo que tienen acciones asociadas, en este caso, la acción to_i, la cual retorna un valor numérico entero correspondiente de la conversión de la cadena a número. Si no es posible la conversión, retornará un cero. Resumiendo, la variable edad capturará en un número entero que el usuario introduce por teclado.

La línea 7 muestra un texto, al igual que print, pero puts añade un retorno de carro al final, cambiando de línea para la siguiente impresión. Dentro del texto a imprimir (entre comillas), aparece la expresión #{expr}, la cual sustituye todo por el valor contenido dentro como expr, la cual puede ser una variable, un cálculo o cualquier expresión que retorne un valor. En este caso, sería el valor de la variable nombre.

La línea 8, realiza una operación similar, pero demostrando que la variable nombre es un objeto de tipo string, y accediendo al método length, el cual retorna el número de caracteres de la cadena de texto. En este caso, se observará que retorna la longitud más uno, debido a que también atrapó el carácter de retorno de línea (al pulsar Enter). Para corregir este defecto se podría utilizar la expresión "#{nombre.length-1}"

La línea 13 realiza una evaluación condicional, preguntando si el valor (if (condición) then / si (condición) entonces) de la variable edad es menor de 18, en cuyo caso imprimirá en pantalla el literal "menor", si no (else) imprimirá el literal "mayor". El bloque de condiciones termina en la línea 17, con end.

Este código se guarda en un archivo (por ejemplo: pruebaruby1.rb). Para ejecutarlo, desde la consola de comandos se lanza la siguiente sentencia:

$ ruby pruebaruby1.rb



Escribe tu nombre: Rafael
Escribe tu edad: 40
Hola, Rafael
Tu nombre tiene 7 caracteres
Eres mayor de edad, y te quedan 27 anos para jubilarte
Encantado de conocerte. ADIOS

lunes, 29 de noviembre de 2010

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

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í: