Monday, March 29, 2010

Parece que las bases de datos relacionales tienen competencia fuerte.

Una nueva ola de almacenar datos esta revolucionando la insdustria en los ultimos meses, y definitivamente nombres reconocidos en la industria estan reconociendo sus bondades, pero de que estamos hablando? Al parecer el tradicional LAMP, una combinacion ganadora en los ultimos anios y que ha hecho uso extensivo de bases de datos relacionales como MySQL y PostgreSQL, parece cosa del pasado, y es que la nueva modalidad de bases de datos no relacionales o (Non-SQL como tambien les llaman en Ingles) son la nueva panacea en el almacenamiento de informacion (o mejor dicho documentos).

Pero que esta motivando este cambio:

  • En muchos casos las bases de datos relacionales no son la respuesta a todos los requerimientos de almacenamiento de datos.
  • Las bases de datos relacionales presentan obstaculos para su uso en ambientes distribuidos.
  • La normalizacion usualmente impacta en el desempeno de la base de datos.
  • En muchas aplicaciones solamente es necesario hacer busquedas basadas en la llave primaria.


Los nuevos protagonistas en el almacenamiento de bases de datos difieren en algunas funcionalidades, pero en general todos se basan en un conjunto de caracteristicas de alto nivel, entre las cuales podemos mencionar:
  1. Son bases de datos no normalizadas, sin necesidad de esquemas y orientadas a documentos
  2. Bases de datos simples, basadas en el conjunto llave : valor; metodos de busqueda estan disponibles y se basan en busquedas de la llave primaria.
  3. Capacidad de uso distribuido, o crecimiento horizontal, donde replicas de la base de datos pueden ubicarse en diversos nodos de distribucion de contenidos para los usuarios.
  4. Replicacion es un punto importante como se menciono anteriormente, entonces esta debe ser una caracteristica importante de la base de datos.
  5. La base de datos se accede mediante llamadas usando el formato RESTful, o mediante API
  6. Soporte del metodo de programacion o framework Map Reduce introducido por Google para el analisis de datos.
  7. Consistencia.
Apache CouchDB

Hace unas semanas el grupo de usuarios de Python de Utah hizo una presentacion sobre esta base de datos y en ellas se demostro su funcionalidad en el desarrollo de una pequena aplicacion, tambien se hablo un poco de la historia de la base de datos y sus caracteristicas, las cuales pueden encontrarse en el sitio web oficial, pero basicamente CouchDB es una base de datos orientada a documentos.

A continuacion mostramos como instalar la aplicacion en Fedora 12, asi como la libreria de Python para acceder a la base de datos; luego un programa que accede a la base de datos y muestra como hacer ciertas operaciones con los datos de la misma.



[fdiaz@fedora12 ~]$ sudo yum install couchdb python-couchdb
[sudo] password for fdiaz:
Loaded plugins: presto, refresh-packagekit
adobe-linux-i386 | 951 B 00:00
rpmfusion-free-updates | 3.3 kB 00:00
rpmfusion-nonfree-updates | 3.3 kB 00:00
updates/metalink | 15 kB 00:00
Setting up Install Process
Resolving Dependencies
--> Running transaction check
---> Package couchdb.i686 0:0.10.0-2.fc12 set to be updated
--> Processing Dependency: libicu-devel for package: couchdb-0.10.0-2.fc12.i686
--> Processing Dependency: libjs.so.1 for package: couchdb-0.10.0-2.fc12.i686
--> Processing Dependency: erlang for package: couchdb-0.10.0-2.fc12.i686
---> Package python-couchdb.noarch 0:0.6.1-2.fc12 set to be updated
--> Running transaction check
---> Package erlang.i686 0:R13B-04.1.fc12 set to be updated
--> Processing Dependency: libodbc.so.2 for package: erlang-R13B-04.1.fc12.i686
---> Package js.i686 0:1.70-8.fc12 set to be updated
---> Package libicu-devel.i686 0:4.2.1-7.fc12 set to be updated
--> Running transaction check
---> Package unixODBC.i686 0:2.2.14-9.fc12 set to be updated
--> Finished Dependency Resolution

Dependencies Resolved

================================================================================
Package Arch Version Repository Size
================================================================================
Installing:
couchdb i686 0.10.0-2.fc12 fedora 513 k
python-couchdb noarch 0.6.1-2.fc12 updates 83 k
Installing for dependencies:
erlang i686 R13B-04.1.fc12 updates 33 M
js i686 1.70-8.fc12 fedora 346 k
libicu-devel i686 4.2.1-7.fc12 updates 616 k
unixODBC i686 2.2.14-9.fc12 updates 381 k

Transaction Summary
================================================================================
Install 6 Package(s)
Upgrade 0 Package(s)

Total download size: 35 M
Is this ok [y/N]: y
Downloading Packages:
Setting up and reading Presto delta metadata
updates/prestodelta | 689 kB 00:03
Processing delta metadata
Package(s) data still to download: 35 M
(1/6): couchdb-0.10.0-2.fc12.i686.rpm | 513 kB 00:02
(2/6): erlang-R13B-04.1.fc12.i686.rpm | 33 MB 03:16
(3/6): js-1.70-8.fc12.i686.rpm | 346 kB 00:02
(4/6): libicu-devel-4.2.1-7.fc12.i686.rpm | 616 kB 00:03
(5/6): python-couchdb-0.6.1-2.fc12.noarch.rpm | 83 kB 00:00
(6/6): unixODBC-2.2.14-9.fc12.i686.rpm | 381 kB 00:02
--------------------------------------------------------------------------------
Total 169 kB/s | 35 MB 03:30
Running rpm_check_debug
Running Transaction Test
Finished Transaction Test
Transaction Test Succeeded
Running Transaction
Installing : unixODBC-2.2.14-9.fc12.i686 1/6
Installing : erlang-R13B-04.1.fc12.i686 2/6
Installing : js-1.70-8.fc12.i686 3/6
Installing : libicu-devel-4.2.1-7.fc12.i686 4/6
Installing : couchdb-0.10.0-2.fc12.i686 5/6
Installing : python-couchdb-0.6.1-2.fc12.noarch 6/6

Installed:
couchdb.i686 0:0.10.0-2.fc12 python-couchdb.noarch 0:0.6.1-2.fc12

Dependency Installed:
erlang.i686 0:R13B-04.1.fc12 js.i686 0:1.70-8.fc12
libicu-devel.i686 0:4.2.1-7.fc12 unixODBC.i686 0:2.2.14-9.fc12

Complete!
You have new mail in /var/spool/mail/fdiaz
[fdiaz@fedora12 ~]$

Una vez la aplicacion se ha instalado, podemos iniciar el servicio mediante el siguiente comando:

[fdiaz@fedora12 ~]$ sudo /etc/init.d/couchdb start
Starting couchdb: Apache CouchDB has started, time to relax.
[ OK ]

Para acceder a la base de datos, hacemos uso de un explorador y nos dirigimos a la siguiente direccion:
http://localhost:5984/_utils/index.html

El servicio responde con una aplicacion web en la cual podremos acceder a la base de datos y hacer algunas operaciones, les invito a que le den un vistazo; pero programaticamente, podemos hacer uso del lenguaje de programacion Python para hacer las mismas operaciones como veremos a continuacion en el siguiente programa:

================ couchDBTest.py ===============

#! /usr/bin/python

import httplib, simplejson # Librerias para efectuar las llamadas HTTP y para acceder
# a los elementos json devueltos por la base de datos.

def Imprimir(s):
"""Imprimir muestra el resultado json de un objeto HTTP Respond"""
# Respuesta HTTP -> Diccionario de Python -> cadena
print simplejson.dumps(simplejson.loads(s.read()), sort_keys=True, indent=4)


class Couch:
"""Utilizaremos una clase para encapsular las acciones de la base de datos"""

def __init__(self, servidor, puerto=5984, options=None):
self.servidor = servidor
self.puerto = puerto

def conectar(self):
return httplib.HTTPConnection(self.servidor, self.puerto) # Hacemos una coneccion
# al servicio de la base de datos mediante metodos HTTP
# Operaciones basicas en la base de datos

def creaDB(self, nombreBD):
"""crea una nueva base de datos en el servidor"""

r = self.put(''.join(['/',nombreBD,'/']), "")
Imprimir(r)

def borraDB(self, nombreBD):
"""Elimina la base de datos del servidor"""
r = self.delete(''.join(['/',nombreBD,'/']))
Imprimir(r)

def listaDB(self):
"""Muestra un listado de las bases de datos disponibles en el servidor"""
Imprimir(self.get('/_all_dbs'))

def infoDb(self, nombreBD):
"""Muestra informacion sobre la base de datos"""
r = self.get(''.join(['/', nombreBD, '/']))
Imprimir(r)

# Operaciones sobre los documentos en la base de datos

def listDoc(self, nombreBD):
"""Muestra una lista de todos los documentos en la base de datos"""

r = self.get(''.join(['/', nombreBD, '/', '_all_docs']))
Imprimir(r)

def abreDoc(self, nombreBD, docId):
"""Abre un documento en una base de datos en particular"""
r = self.get(''.join(['/', nombreBD, '/', docId,]))
Imprimir(r)


def guardaDoc(self, nombreBD, contenido, docId=None):
"""Guarda o crea un documento en la base de datos especificada"""
if docId:
r = self.put(''.join(['/', nombreBD, '/', docId]), contenido)
else:
r = self.post(''.join(['/', nombreBD, '/']), contenido)
Imprimir(r)

def borraDoc(self, nombreBD, docId):
"""Elimina un documento, especificado por su docID, de la base de datos
segun la documentacion de CouchDB, la llamada debe identificar el documento
por medio de la revision del mismo, asi que primero hacemos un get segun el docid,
luego de-serializamos el objeto json, lo cual nos devuelve un diccionario y de el
extraemos el item '_rev' que es el parametro que pasamos para finalmente borrar
el documento.
"""
t = self.get(''.join(['/', nombreBD, '/', docId,]))
obj = simplejson.loads(t.read())
r = self.delete(''.join(['/', nombreBD, '/',docId,'?rev=',obj['_rev']]))
Imprimir(r)

# metodos HTTP

def get(self, uri):
c = self.conectar()
encabezado = {"Accept": "application/json"}
c.request("GET", uri, None, encabezado)
return c.getresponse()

def post(self, uri, contenido):
c = self.conectar()
encabezado = {"Content-type": "application/json"}
c.request('POST', uri, contenido, encabezado)
return c.getresponse()

def put(self, uri, contenido):
c = self.conectar()
if len(contenido) > 0:
encabezado = {"Content-type": "application/json"}
c.request("PUT", uri, contenido, encabezado)
else:
c.request("PUT", uri, contenido)
return c.getresponse()

def delete(self, uri):
c = self.conectar()
c.request("DELETE", uri)
return c.getresponse()

def main():
foo = Couch('localhost', '5984')
print "\nCreamos la base de datos 'bddoc':"
foo.creaDB('bddoc')

print "\nLista de bases de datos en el servidor:"
foo.listaDB()

print "\nCreamos un documento en la base de datos 'bddoc' con id: 00001:"
doc = """
{
"valor":
{
"Titulo":"Programacion Python y CouchDB",
"Autor":"FcoDiaz",
"Fecha":"2010-03-29T11:30:12-06:00",
"Tags":["couchDB", "python", "REST", "json"],
"Contenido":"Practicando un rato con CouchDB y Python.",
"Idioma":"Espanol"
}
}
"""
foo.guardaDoc('bddoc', doc,'00001')

doc2 = """
{
"valor":
{
"Titulo":"Programacion en C++",
"Autor":"FcoDiaz",
"Fecha":"2010-03-29T11:30:12-06:00",
"Tags":["C++", "Programacion"],
"Contenido":"Practicando un rato con C++.",
"Editorial":"FcoDiaz Publicaciones"
}
}
"""
print "\nCrea un segundo documento, el id sera seleccionado por la BD"
foo.guardaDoc('bddoc', doc2)

print "\nMuestra todos los documentos en la base de datos 'bddoc'"
foo.listDoc('bddoc')

print "\nRecuperamos el documento con codigo 00001 de la base de datos 'bddoc':"
foo.abreDoc('bddoc', '00001')
raw_input("Presiona ENTER para continuar")

print "\nBorramos el documento con codigo '00001' de la base de datos 'bddoc':"
foo.borraDoc('bddoc', '00001')


print "\nNuevamente mostramos los documentos en la base de datos 'bddoc'"
foo.listDoc('bddoc')
raw_input("Presiona ENTER para continuar")

print "\nInformacion de la base de datos 'bddoc':"
foo.infoDb('bddoc')

print "\nBorra la base de datos 'bddoc':"
foo.borraDB('bddoc')

print "\nLista de bases de datos en el servidor:"
foo.listaDB()

if __name__ == "__main__":
main()

==========
Para ejecutar el programa, lo guardamos y posteriormente podemos usar la siguiente linea de comandos para correr el programa, el resultado del programa puede verse a continuacion; he agregado algunas pausas para que mientras el programa espera por la tecla Return para continuar, se pueda observar el estado de la base de datos mediante el navegador, como se explico anteriormente y donde se podran observar la nueva base de datos y los documentos creados.

[fdiaz@fedora12 Python]$ python couchDBTest.py

Creamos la base de datos 'bddoc':
{
"ok": true
}

Lista de bases de datos en el servidor:
[
"bddoc"
]

Creamos un documento en la base de datos 'bddoc' con id: 00001:
{
"id": "00001",
"ok": true,
"rev": "1-7ddb0ea5569142bae2ce8879d56adb97"
}

Crea un documento, el codigo sera seleccionado por la BD
{
"id": "d5907db4ebd8fb240a8be36e24d56c43",
"ok": true,
"rev": "1-75ca9f69056f030781dfefd128c664c9"
}

Muestra todos los documentos en la base de datos 'bddoc'
{
"offset": 0,
"rows": [
{
"id": "00001",
"key": "00001",
"value": {
"rev": "1-7ddb0ea5569142bae2ce8879d56adb97"
}
},
{
"id": "d5907db4ebd8fb240a8be36e24d56c43",
"key": "d5907db4ebd8fb240a8be36e24d56c43",
"value": {
"rev": "1-75ca9f69056f030781dfefd128c664c9"
}
}
],
"total_rows": 2
}

Recuperamos el documento con codigo 00001 de la base de datos 'bddoc':
{
"_id": "00001",
"_rev": "1-7ddb0ea5569142bae2ce8879d56adb97",
"valor": {
"Autor": "FcoDiaz",
"Contenido": "Practicando un rato con CouchDB y Python.",
"Fecha": "2010-03-29T11:30:12-06:00",
"Idioma": "Espanol",
"Tags": [
"couchDB",
"python",
"REST",
"json"
],
"Titulo": "Programacion Python y CouchDB"
}
}
Presiona ENTER para continuar

Borramos el documento con codigo '00001' de la base de datos 'bddoc':
{
"id": "00001",
"ok": true,
"rev": "2-cf6064d2287d8c685d90eecebf58dfb6"
}

Nuevamente mostramos los documentos en la base de datos 'bddoc'
{
"offset": 0,
"rows": [
{
"id": "d5907db4ebd8fb240a8be36e24d56c43",
"key": "d5907db4ebd8fb240a8be36e24d56c43",
"value": {
"rev": "1-75ca9f69056f030781dfefd128c664c9"
}
}
],
"total_rows": 1
}
Presiona ENTER para continuar

Informacion de la base de datos 'bddoc':
{
"compact_running": false,
"db_name": "bddoc",
"disk_format_version": 4,
"disk_size": 4589,
"doc_count": 1,
"doc_del_count": 1,
"instance_start_time": "1269893376165905",
"purge_seq": 0,
"update_seq": 3
}

Borra la base de datos 'bddoc':
{
"ok": true
}

Lista de bases de datos en el servidor:
[]
[fdiaz@fedora12 Python]$



Algunas cosas que se observan de al menos los dos elementos de datos presentados es que la estructura de datos utilizada es un diccionario de Python y que los elementos de cada uno de los datos pueden variar, como es el caso del Idioma y Editorial, lo cual da una gran flexibilidad a los datos en caso que estos cambien constantemente de forma y estructura. Las bases de datos no relacionales han existido desde hace ya buen rato, de hecho yo trabaje en una llamada Minisis, alla por los 90's, y la base de datos era execelente para almacenar el catalogo de libros de una biblioteca; como podran ver la orientacion de estas bases de datos es hacia los documentos, donde estos forman un elemento primordial en sistemas recientes que ademas abarcan una gran cantidad de usuarios como lo por ejemplo las redes sociales. Tambien puede ser utilizada en sistemas no tan complejos que requiran bases de datos sencillas y donde la recuperacion de datos esta mas enfocada en busquedas sobre la llave primaria.

Finalmente algunas companias que estan haciendo el cambio a este tipo de bases de datos tenemos:

Facebook y Twitter: Cassandra
Google: Big Table
LinkedIn: Voldemort


Mas informacion sobre CouchDB: http://couchdb.apache.org/
O sobre la libreria de Python para CouchDB: http://wiki.apache.org/couchdb/Getting_started_with_Python

Hasta la proxima.

Sunday, March 28, 2010

Que es la de-duplicacion de datos?

En la gerga de computadoras, la De-duplicacion de datos es una forma caracteristica de compresion donde los datos redundantes son eliminados, con la finalidad de optimizar el almacenamiento de la misma. En el proceso, la informacion que se encuentra mas de una vez es eliminada, dejando solamente una copia del mismo. Para agilizar el proceso de verificacion si los datos han sido almacenados con anterioridad, se utilizan indices que identifican a los datos(o archivos). Por ejemplo, en el caso de un sistema de correo, un mismo archivo puede encontrarse multiples veces en el sistema, suponiendo que es un archivo de un megabyte, y este se encuentra unas 100 veces en el sistema, el archivo utiliza 100MB de espacio. Con el sistema de de-duplicacion de datos, el archivo es almacenado solamente una vez, cualquier uso adicional de este mismo archivo solamente se hace referencia a la unica copia existente por medio del sistema de indices. Sistemas que por lo general se benefician de este metodo son los respaldos (backup) de informacion.

http://ostatic.com/blog/sdfs-a-robust-deduplication-file-system-for-linux

Friday, March 5, 2010

Que es synergy?

Synergy es una aplicacion que permite compartir un solo teclado y un raton entre diferentes computadoras, aun con diferentes sistemas operativos y sin la necesidad de un equipo especial, solamente acceso a la red. La idea es aliviar a quienes tiene que atender multiples sistemas y cada uno de ellos tiene un teclado, un raton y su respectivo monitor o pantalla.

El programa es simple, solo require de la configuracion de un server en una de las computadoras, y luego clientes que se conectan a esta por medio de la red, luego solo es cuestion de apuntar el mouse a la pantalla deseada y listo. Synergy tambien combina el porta papeles (clipboard) de las diferentes computadoras en uno solo, de esa forma es posible hacer cut-paste entre los diferentes sistemas. Ademas permite sincronizar los protectores de pantalla, de esa forma todos se activan o desactivan al mismo tiempo.

Modo de empleo

Paso 1. Seleccione la computadora que sera el servidor, o sea, la computadora que compartira el teclado y el raton con las demas, en mi caso, tengo dos computadoras, el desktop y la laptop, asi que la desktop es la que funciona como server.

Paso 2. Instalacion del programa, aqui depende del sistema, pero el programa es soportado en vario SO como Win, MacOS, Linux y Unix, asi que seleccione el mejor metodo de acuerdo a sus necesidades.

Paso 3. Configuracion del servidor. Primero es necesario crear un archivo de configuracion con los detalles de los sistemas involucrados y sus ubicaciones, un ejemplo de uno de estos archivos es el siguiente, su nombre es synergy.conf:


section: screens
desktop:
laptop:
end
section: links
desltop:
right = laptop
laptop:
left = desktop
end


Donde desktop y laptop son los nombres en la red de los sistemas a conectar, es necesario verificar que estos nombres puedan resolverse ya sea por medio local (/etc/hosts) o por medio de un DNS.

Luego se inicia el servicio que atendera a los clientes por medio del comando:

synergys -f -c synergy.conf


Lo anterior ejecutara el server y mostrara lo que esta sucediendo mientras se conectan otros clientes; al momento de configurar el sistema es recomendable hacer uso de la opcion -f para resolver errores, una vez estos han sido resueltos, se puede cambiar -f por -d para ejecutarlo como un daemon del sistema.
En el caso de Windows, hay una aplicacion grafica que configura todo lo anterior.

Paso 4. Iniciar los clientes.
Para iniciar los clientes en Unix/Linux/Mac simplemente se ejecuta el siguiente comando:


synergyc -f desktop


Donde desktop es el hostname de la maquina que esta compartiendo el teclado, en Windows el programa Synergy se encarga de conectar el cliente con el servidor. Es importante notar que el cliente debe ser capaz de resolver el nombre de la maquina servidor por medio local (/etc/hosts) o por medio de un DNS.

Demo:
http://www.youtube.com/watch?v=Id3GHAruhAk

Website:
http://synergy2.sourceforge.net/index.html

Wednesday, March 3, 2010

Usos del comando test

El comando if del Bourne Shell fue disenado para trabajar con comandos, y dado que en todos los scripts siempre es necesario comparar valores (numeros, cadenas de texto, archivos) el Bourne shell incluyo el comando test

Dependiendo del tipo de valores pasados al comando, este puede comparar valores, verificar permisos en archivos, o inclusive verificar la existencia de archivos.

Para indicarle al comando el tipo de comparacion por hacer, se debe pasar las opciones adecuadas al momento de contruir la sentencia que se desea verificar, asi el comando usa estos parametros para determinar que tipo de comparacion tiene que realizar. Luego el comando test termina, y devuelve el numero 0 si la expresion fue evaluada como verdadera, de lo contarrio un valor de 1 si la sentencia fue evaluada como falsa. Si dentro de la expresion se ha cometido un error, entonces el comando devolvera un valor mayor que 1.

El siguiente ejemplo muestra el comando test en una comparacion de valores numericos:

x=5
y=10
echo -n "test -eq: "
if (test $x -eq $y) then
echo "x=y"
else
echo "x diferente de y"
fi


El siguiente ejemplo muestra como comparar cadenas de texto:

cadena="Hola mundo"
echo "test -z: "
if (test -z $cadena)
echo "Longitud de la cadena es 0"
else
echo "Longitud de la cadena no es 0"
fi


Este ejemplo determina si un archivo existe o no:

archivo="/etc/samba/smb.conf"
echo "test -f: "
if (test -f archivo) then
echo "Archivo existe"
else
echo "Archivo no existe"
fi


Mas informacion sobre el comando y que otros tipos de opciones de comparacion hay consulte el manual mediante el siguiente comando:

man test


Para finalizar, posiblemente en muchos de los script que hemos visto, el comando test es reemplazado por su comando equivalente que es el simbolo [, si este es otro comando que existe como tal en el sistema, sino intenten el siguiente comando para ver donde se encuentra:


$which [
/usr/bin/[


Asi, podemos tomar uno de los ejemplos anteriores se convertiria en:

archivo="/etc/samba/smb.conf"
echo "test -f: "
if [ -f archivo ]; then
echo "Archivo existe"
else
echo "Archivo no existe"
fi


Con respecto al ejemplo anterior, es necesario dejar un espacio en blanco despues del "[" y antes del "]", y luego es necesario un punto y coma ";" luego del simbolo "]" y antes de la palabra then, o sino pueden bajar la palabra then a la siguiente linea y no necesitan en simbolo ";"

Hasta la proxima.