Skip to content
May 25 11

CouchDB – Ejemplo de implementación

by Cristian Requena

En este ejemplo se ha implementado un símil básico de Twitter para realizar las operaciones típicas de una base de datos. Gracias a la estructura DAO con la que se ha codificado se podrá reaprovechar para probar otras bases de datos, manteniendo una parte en común.

El diagrama de clases es el siguiente:

Análisis PFC

 

Como única inicialización de la base de datos, ha sido necesario crear dos documentos de diseño de CouchDB, que permiten la obtención de todos los tweets de un usuario y todos los tweets de un tag. Su código es el siguiente:

{
 "_id": "_design/tweets",
 "_rev": "1-c8aaf3665c01a82e0e31701f50424c4c",
 "language": "javascript",
 "views": {
 "tweetsByUser": {
 "map": "function(tweet) { if (tweet.tipo == 'tweet') { emit(tweet.username, tweet); } };"
 },
 "tweetsByTag": {
 "map": "function(tweet) { if (tweet.tipo == 'tweet') { for (var tag in tweet.tags) { emit(tweet.tags[tag], tweet); } } };"
 }
 }
}

Su funcionamiento, como se observa, es muy simple. La vista “tweetsByUser” retorna un conjunto clave/valor donde la clave es el nombre de usuario y el valor es el tweet, mientras que la vista “tweetsByTag” realiza un pequeño bucle por todos los tags que pueda tener un tweet, para luego retornarse con cada uno de ellos como clave.

Estas vistas son filtrables mediante la clave (por ello se retorna como tal el valor que se desea condicionar) y además, como que CouchDB las indexa, proveen de un acceso de consulta muy rápido.

El resto del código del ejemplo es bastante básico, ya que se basa en 50 threads que machacan la base de datos con creaciones, lecturas, modificaciones y borrados de documentos en paralelo. Asimismo se muestra el tiempo empleado en cada operación, que raramente supera los 20 ms.

El ejemplo está disponible en este enlace .

 

Para finalizar veremos un conjunto de capturas de pantalla con el proceso en ejecución, donde se observa el comportamiento de la base de datos en todo momento:

Estado inicial

Creación y modificación de tweets y usuarios

Base de datos con 14000+ documentos

Borrado de usuarios y finalización

Feb 5 11

Distribución en CouchDB

by Cristian Requena

Una de las máximas prioridades durante el diseño de sistemas de información es el hacer que todo funcione. Cuando estos sistemas se encuentran expuestos a cualquier usuario, es decir, cuando están abiertos al acceso desde Internet, no solamente importa que funcionen: es necesario asegurarse de que lo hacen para todo el mundo, independientemente del lugar desde donde esté accediendo a nuestro entorno.

En resumen, se hace necesaria la distribución de datos entre varias bases de datos autónomas, para intentar asegurar un máximo tiempo de disponibilidad.

CouchDB provee de un mecanismo de replicación entre los distintos servidores, que simplemente traslada, en tiempo real, las peticiones a las instancias con las que está relacionado un servidor en concreto. Por ejemplo, si disponemos de tres servidores (A, B y C) y configuramos una replicación circular (A contra B, B contra C y C contra A), cualquier modificación que realicemos en una de estas instancias se traspasará instantáneamente al resto de servidores. En caso de caída de alguno de ellos, el proceso de replicación quedará pausado hasta que la instancia de destino se levante de nuevo.

La replicación de CouchDB también puede ejecutarse manualmente, que es algo bastante útil en el caso de bases de datos off-line (por ejemplo, la de un terminal que no pueda disponer de acceso a red y que deba ser “vaciado” periódicamente).

Proceso de replicación on-line desde el servidor de origen:



Datos replicados en el servidor de destino:

El proceso de replicación off-line es idéntico al que se ha tratado más arriba, con la salvedad de que es ejecutado manualmente.

Oct 25 10

MVCC en CouchDB. Ejemplo de uso desde Java.

by Cristian Requena

En anteriores anotaciones se comentó el control de concurrencia de CouchDB, MVCC, que es una funcionalidad que le otorga la no dependencia de bloqueos u otros mecanismos similares.

Un control de concurrencia es un mecanismo de integridad que se basa en la necesidad de disponer de la última modificación de un dato para poderlo volver a modificar, esto es, se requiere obtener el archivo/documento/etc. más reciente para poderlo actualizar. A pequeña escala es algo trivial (-Pepe, pásame el documento de la versión, que añado un párrafo; -Paco, pásame el documento que borro una imagen; etc.), pero cuando se trata con un gran conjunto de datos se torna imposible saber quién dispone del documento más reciente/más actualizado sin la ayuda externa de una utilidad tal como CVS, Subversion, etc.
Las empresas y organizaciones de desarrollo de software, así como los grupos de desarrolladores, utilizan estos mecanismos para modificar el código producido, pero cuando se trata del área de gestión de bases de datos, y siempre que se trate de bases de datos relacionales, no queda otra opción que el uso de bloqueos (normalmente automáticos de los mismos RDBMS), sin tener la posibilidad de saber si el documento fue previamente modificado desde su obtención.

El objetivo de CouchDB en esta área es el de proveer de un MVCC fiable que veremos en acción en el siguiente ejemplo, cuando se intente modificar dos veces un mismo documento partiendo de una misma versión.

Session s = new Session("lamec",5984); // Crear una sesión/conexión contra CouchDB.
Database db = s.getDatabase("demo"); // Acceder a una base de datos del servidor.
Document doc1 = db.getDocument("es.nosql.demo.clientes.12345678Z");	// Obtener un documento
Document doc2 = db.getDocument("es.nosql.demo.clientes.12345678Z");	// Obtener el mismo documento en la misma revisión
doc1.put("Apellidos", "González1 Zapatero1");
doc2.put("Apellidos", "González2 Zapatero2");
db.saveDocument(doc1); // Persistir el documento.
db.saveDocument(doc2); // Persistir el documento por segunda vez, partiendo de la misma revisión.

Resultado:

ADVERTENCIA: Error adding document - conflict Document update conflict.
RESP: [null] /demo/es.nosql.demo.clientes.12345678Z [409]  => {"error":"conflict","reason":"Document update conflict."}

En resumen, se permite la primera modificación del documento pero posteriormente, cuando se intenta modificarlo partiendo de una revisión antigua, se crea un conflicto y no se permite el cambio.

Una forma rápida de lidiar con estos Warnings es el de, simplemente, dejar hacer a CouchDB. Esto se consigue modificando la revisión del documento a la última disponible justo antes de lanzar su grabación:

Session s = new Session("lamec",5984); // Crear una sesión/conexión contra CouchDB.
Database db = s.getDatabase("demo"); // Acceder a una base de datos del servidor.
Document doc1 = db.getDocument("es.nosql.demo.clientes.12345678Z");	// Obtener un documento
Document doc2 = db.getDocument("es.nosql.demo.clientes.12345678Z");	// Obtener un documento
doc1.put("apellidos", "González1 Zapatero1");
doc2.put("apellidos", "González2 Zapatero2");
db.saveDocument(doc1);	// Persistir el documento.
doc2.setRev(db.getDocument(doc2.getId()).getRev());	// Modificar la revisión.
db.saveDocument(doc2);	// Persistir el documento partiendo de la última modificación.

Et voilà: se actualiza el documento con los últimos cambios, descartando la modificación de doc1:

Otra forma de gestionarlo es detectar cuando se produce un error de actualización y revisar manualmente los cambios entre una revisión y otra, para producir un documento que una ambas revisiones. Esto es más laborioso, pero puede ser interesante según el tipo de entidades con las que se trabaje.

Oct 21 10

Comparación Clase Asociación entre SQL y CouchDB. Consulta de datos.

by Cristian Requena

En este artículo se realizará la comparación del enfoque de una clase asociación típica entre una base de datos relacional (Oracle, MySQL, SQLServer, etc) y CouchDB, que como sabemos, es NoSQL.

El objetivo, pues, es sencillo: representar en SQL y en CouchDB el diagrama UML mostrado a continuación.

Desde el punto de vista típico de una base de datos SQL se crean 2 tablas de “entidad” (“Clientes” y “Métodos de Pago”) y otra que las “relaciona” (“Métodos de Pago de Clientes”), resultando algo como lo siguiente:

CREATE TABLE clientes (
nif		CHAR(14) NOT NULL,
nombre		CHAR(20) NOT NULL,
apellidos	CHAR(30) NOT NULL,
CONSTRAINT pk_clientes PRIMARY KEY (nif)
);

CREATE TABLE metodos_pago (
id_metodopago	CHAR(2) NOT NULL,
d_metodopago	CHAR(20) NOT NULL,
CONSTRAINT pk_metodos_pago PRIMARY KEY (id_metodopago)
);

CREATE TABLE pago_clientes (
nif		CHAR(14) NOT NULL,
id_metodopago	CHAR(2) NOT NULL,
id_metodo	CHAR(3) NOT NULL,
datos_pago	CHAR(100),
CONSTRAINT pk_pago_clientes PRIMARY KEY (nif, id_metodo_pago, id_metodo),
CONSTRAINT fk_pago_clientes_clientes FOREIGN KEY nif REFERENCES clientes (nif),
CONSTRAINT fk_pago_clientes_pagos FOREIGN KEY id_metodopago REFERENCES metodos_pago (id_metodopago)
);

En resumen: se crean las tres tablas con un conjunto de restricciones, de modo que resultado de introducir valores en el esquema es un set no redundante y normalizado.

Para dar respuesta a esta casuística, en CouchDB hay dos planteamientos típicos: utilizar dos documentos (uno perteneciente a la clase “Clientes” y otro de “Métodos de Pago”, relacionando estos últimos con los primeros mediante un campo) o bien utilizar un solo documento (y, por lo tanto, que dentro de un documento de tipo “Clientes” convivan los datos personales de un cliente y los datos de sus métodos de pago).

La primera posibilidad también es posible llevarla a cabo en una base de datos SQL, a costa de redundar los datos. Esto produce una pérdida de rendimiento, ya que precisamente los esquemas de las bases de datos relacionales requieren, como ya sabemos, de la máxima normalización para ser eficientes.

Por otra parte las restricciones de esquema de las bases de datos relacionales tornan imposible la realización de la segunda opción: requeriría de 999 (la relación entre “Clientes” y “Métodos de Pago” es 1..999) campos adicionales… Impensable.

Veamos la resolución en CouchDB de estos dos casos:

Caso 1: Dos documentos

Uno de los documentos pertenecería a la entidad “Clientes” propiamente dicha, y donde el “_id” del documento sería su NIF para proveer su unicidad:

{
   "_id": "es.nosql.demo.clientes.12346578Z",
   "_rev": "1-a7b1dbc4475bcca028654c46116995f7",
   "tipo": "clientes"
   "nombre": "Perico",
   "apellidos": "Eldel Ospalotes"
}

El otro sería “Métodos de Pago por Clientes”, algo parecido a lo siguiente:

{
   "_id": "es.nosql.demo.metodospagoclientes._id_único",
   "_rev": "1-acb341a7b1dbc4475bcca028654c4611",
   "tipo": "pagoclientes",
   "cliente": "es.nosql.demo.clientes.12346578Z",
   "metodopago": "Paypal",
   "datospago": "blog@nosql.es"
}

Por último será necesario crear un documento de diseño que contenga una vista que relacione los dos documentos anteriores y confeccione un resultado:

{
   "_id": "_design/pagoclientes",
   "_rev": "1-6ae9b200597ec5d4899e904edb6a8aec",
   "language": "javascript",
   "views": {
       "pagoclientes": {
           "map": "function(doc) {if (doc.tipo == 'clientes') {emit([doc._id, 0], doc);} else { if (doc.tipo = 'pagoclientes') {emit([doc._id, 1], doc); } } }"
       }
   }
}

La vista “pagoclientes” devuelve un JSON con dos (o n) filas: la primera contiene el documento de tipo “cliente”, mientras que la segunda (y sucesivas) contiene los datos de pago del cliente.
Este es un comportamiento análogo al que se consigue con las operaciones JOIN en SQL.

Caso 2: Un único documento

Tal y como reza el eslógan de CouchDB: relax. Y ahora veremos por qué.

Como sabemos, CouchDB es una base de datos de documentos, y los almacena como estructuras de datos JSON. Gracias a esto, podemos definir el contenido de un campo como una lista infinita de valores.

Esto significa que el siguiente documento es, sencillamente, factible:

{
   "_id": "es.nosql.demo.clientes.12346578Z",
   "_rev": "1-a7b1dbc4475bcca028654c46116995f7",
   "tipo": "clientes"
   "nombre": "Perico",
   "apellidos": "Eldel Ospalotes",
   "metodospago": [{"tipo": "Paypal", "datospago": "blog@nosql.es"}, {"tipo": "Efectivo"}, {"tipo": "Mastercard", "datospago": "4567-9875-9863-4456/999"}]
}

Después, y en tan sólo una eficiente consulta (ya que los datos se obtienen de forma directa) se podrán obtener los datos.

Esta segunda opción implica, si cabe, aún más replicación de datos, pero debido al eficiente motor de vistas de CouchDB donde por cada vista se mantiene un árbol binario (B+Tree) de búsqueda, esto no implica problemática alguna.

Oct 10 10

Uso de CouchDB desde Java

by Cristian Requena

La base de datos de documentos CouchDB provee una interfaz de comunicación REST para su uso. Esta interfaz nos permite utilizar la API de la base de datos y trabajar con JSON, lo que significa que en ningún momento se requiere de una plataforma concreta para su uso.

Para mostrar cómo acceder a CouchDB desde Java, he usado la siguiente configuración:

CouchDB4J es un paquete de bibliotecas que además contiene unas útiles clases (Database, Document, Session, View, etc.) que permiten utilizar CouchDB de una forma muy sencilla, simplemente instanciando un par de ellas y manejando un par de métodos.
El ejemplo más sencillo sería algo como lo siguiente:

Session s = new Session("lamec",5984); // Crear una sesión/conexión contra CouchDB.
Database db = s.getDatabase("demo"); // Acceder a una base de datos del servidor.
Document newdoc = new Document(); // Crear un documento vacío.
newdoc.put("Apellidos","González Zapatero"); // Crear un par clave:valor.
newdoc.put("Nombre","Mariano");
db.saveDocument(newdoc); // Persistir el documento.

Este fragmento de código generará un documento que contendrá los valores creados, que en este caso podrían ser unos datos personales.
Para consultar la creación de dicho documento, CouchDB provee de una interfaz web, Futon, que permite la visualización, edición y eliminación de documentos. En Futon, el documento que se ha creado desde Java tendría un aspecto similar a este:

En próximas publicaciones se seguirá trabajando con CouchDB y se hará un especial hincapié en los documentos de diseño (Design Documents) y en la creación de vistas.

Jun 1 10

Consistencia en CouchDB

by Cristian Requena

En el anterior artículo relacionado con CouchDB (http://www.nosql.es/blog/nosql/couchdb.html) se habló acerca de su control de múltiples versiones concurrentes, haciendo hincapié especialmente en el no-bloqueo de la lectura de los documentos pese a que estén en plena actualización.
En esta ocasión profundizaremos un poco más en la consistencia eventual que ofrece CouchDB y en cómo se resuelven los distintos conflictos que pueden aparecer. Aunque antes sería interesante echar un vistazo a la siguiente figura:

Fuente de la imagen
El Teorema del CAP (Consistencia, Disponibilidad “Availability” y Tolerancia a particiones “Partition Tolerance”) se refiere a que no es posible disponer en un mismo sistema distribuído de gestión de datos de estas tres características. Las RDBMS como Oracle, AS400 o SQLServer optaron en su día y siguen explotando a día de hoy el fragmento de máxima consistencia y resistencia a particiones, el algoritmo de Paxos busca la máxima consistencia y disponibilidad y, finalmente, las bases de datos NoSQL como CouchDB sacrifican la consistencia estricta en favor de la máxima disponibilidad y resistencia a particiones. Y es de esta característica de CouchDB, de la consistencia eventual, de lo que se hablará más adelante.

CouchDB identifica la versión de un documento mediante un contador incremental, por lo que sabe, en todo momento, si una actualización se está realizando sobre la última versión de un documento o no. De una forma análoga a la de sistemas de control de versiones del estilo de Subversion, CouchDB exige que una modificación (commit) se realice sobre la versión más reciente del documento; en caso contrario, hace falta hacer lo mismo que con Subversion: obtener la última versión del documento y fusionar los cambios localmente. De esta forma, CouchDB asegura la integridad de las actualizaciones de forma local, al contrario de lo que ocurre con las bases de datos relacionales, donde se pueden realizar updates en cualquier momento, sin conocer previamente el estado/versión del registro.

La consistencia eventual que se ha mencionado anteriormente se consigue mediante replicados incrementales, que son los procesos ejecutados periódicamente para comunicar cambios entre servidores. En este punto viene algo muy interesante de CouchDB y de su sistema de versionado: si se ha modificado un documento en dos nodos distintos de un cluster y se realiza el proceso de sincronización, ¿qué ocurre?

  1. Se marca el documento como “En conflicto”, de forma (otra vez) análoga a Subversion y otros sistemas de control de versiones.
  2. La versión más reciente (o la que tiene el contador de revisión más alto) se almacena como última revisión.
  3. La otra versión no se deshecha: se persiste como “penúltima” versión, para que permanezca en el histórico del documento y sea consultable en cualquier caso.

Este mecanismo de sincronismo que, a simple vista, es tan sencillo hace que CouchDB trabaje de forma transparente y consistentemente con distintos nodos, y, en definitiva, la hace escalable, que es de lo que se trata.

May 25 10

Uso de Google BigTable (Parte 1)

by Cristian Requena

Google BigTable es la base de datos que sustenta el almacén de datos de Google, que a su vez es una característica que Google pone al alcance de los desarrolladores de forma pseudo-gratuita para que éstos publiquen aplicaciones con acceso a bases de datos no relacionales,aunque esto último es algo realmente transparente.

Para empezar el desarrollo de una aplicación, es primordial hacerse con el entorno de programación adecuado, que en este caso se compone por Eclipse y un plugin de Google Web Toolkit. Tras la instalación de estos componentes, se podrá desarrollar, probar y publicar las aplicaciones web directamente desde la interfaz de Eclipse. Acerca de este aspecto, Google pone a disposición de los desarrolladores multitud de documentación de forma abierta.

Ejemplo de acceso a BigTable: Descargar Google BigTable Demo 1.

Nota: para probar/modificar este ejemplo es necesario, obviamente, disponer de una cuenta en el appengine de Google, si bien es usable directamente en Eclipse.

El paquete com.nosql.es contiene la definición de las clases a persistir, y las tareas de acceso a datos se llevan a cabo desde la clase NoSQLTestImpl del paquete com.nosql.es.server.

El plugin de Google Web Toolkit realiza todas las tareas de definición de datos de forma transparente al desarrollador, ya que integra la implementación JDO de Datanucleous para realizar todas las tareas de gestión de datos. Esto se traduce en que para poder persistir información contra el almacén de datos de Google se deben realizar las siguientes tareas:

  • Se “puede” marcar la clase a persistir mediante la Annotation @PersistenceCapable(identityType = IdentityType.APPLICATION). Entrecomillo el “puede” porque hay otros valores de IdentityType que tienen otros comportamientos.
  • Los atributos a persistir se deben marcar con la Annotation @Persistent.
  • El atributo que realice la función de clave primaria se debe marcar con @PrimaryKey

Una vez que el motor de DataNucleous nos serialice (de forma transparente) la clase ya se podrán efectuar las operaciones de grabación y/o lectura de instancias de la misma contra la base de datos, es decir, contra el almacén de datos de Google (léase BigTable). Estas operaciones se basan, como se puede observar en el código, en obtener una instancia de persistencia (PersistenceManager) para ejecutar las operaciones makePersistent o get[…].

En próximos ejemplos se profundizará más acerca del lenguaje de consulta del almacén de datos de google, GQL.

Apr 16 10

MapReduce

by Cristian Requena

Lo que en un primer momento me pareció una característica que solamente aparecía en CouchDB he empezado a ver que es otro de los pilares donde se sustentan muchas de las implementaciones de bases de datos NoSQL.

A lo largo de este post se cubrirán los aspectos fundamentales de los frameworks MapReduce tales como Hadoop, una implementación open-source en Java que está mantenida, como Cassandra y CouchDB, por Apache.

En los lenguajes funcionales (Haskell, Erlang, OCaml, etc.) es muy corriente el uso de las funciones de alto nivel map y fold/reduce.

  • map(list[], oper) aplica la operacion oper a la lista list, retornando una nueva lista cuyos elementos han sido operados, individualmente, por oper.
    Por ejemplo, si disponemos de la lista list[1,2,3] y de la operación suma x = x+1 y realizamos la operación map(list, suma) el resultado de la misma será [2,3,4].
  • fold(list[], oper) aplica la operación oper a la lista list, retornando un elemento producto de la operación de los elementos de list entre sí.
    Por ejemplo, si disponemos de la lista list[1,2,3] y de la operación suma (x:xs) = x+suma(xs) y realizamos la operación fold(lista, suma), el resultado de la misma será 6.
    Debido a la posibilidad de que la operación no sea asociativa, los lenguajes de programación ofrecen, normalmente, dos operaciones: una “hacia la derecha”, o foldr y otra “hacia la izquierda”, o foldl.

En general, en el área de la computación distribuida Map se utiliza para fraccionar una operación compleja entre varios nodos y Fold/Reduce para recoger los resultados y unificarlos.

Por su parte, los frameworks MapReduce toman la base de las operaciones mencionadas anteriormente para crear una operación genérica y más compleja, cuyo funcionamiento es realmente útil para las bases de datos NoSQL: en vez de usarse sobre listas de valores unidimensionales, ésta toma como parámetros entrantes una lista de tuplas de tipo (clave, valor) y devuelve una lista de valores. Entre las operaciones map (distribuida) y reduce (normalmente localizada) se genera una lista de tuplas (clave, valor) con valores temporales, de las que reduce filtra solamente las que tengan una determinada clave.

Un ejemplo del uso de MapReduce por las bases de datos NoSQL puede venir dado por MongoDB. Esta implementación hace uso de MapReduce para realizar operaciones sobre los resultados devueltos por una consulta, por ejemplo de contaje de repeticiones.

En definitiva, MapReduce es fundamental en las bases de datos NoSQL para permitir la utilización de funciones de agregación de datos, ya que al carecer de esquema son mucho más complicadas que en las bases de datos relacionales clásicas (RDBMS).

Apr 10 10

MongoDB

by Cristian Requena

MongoDB es otra de las muchas bases de datos NoSQL existentes, cuya particularidad es que intenta unir las ventajas de los almacenes clave-valor (léase, Dynamo) y de las bases de datos relacionales clásicas (RBDMS). Del mismo modo que CouchDB, es una base de datos de documentos sin esquema, pero está desarrollada en C++ en lugar de Erlang.

Para almacenar los documentos, MongoDB utiliza una serialización binaria de JSON, llamada BSON, que es una lista ordenada de elementos simples. El núcleo de la base de datos es capaz de interpretar su contenido, de modo que lo que a simple vista parece un contenido binario, realmente es un documento que contiene varios elementos. Estos datos están limitados a un tamaño máximo de 4 MB; para tamaños superiores se requiere del uso de GridFS.

En el área funcional, MongoDB permite la realización de operaciones de modificación de documentos enviando solamente el diferencial de datos, esto es, la modificación del mismo se realiza dentro del servidor, no en la parte cliente. Además, las escrituras a disco no son simultáneas a la realización de la operación, sino que difieren unos 2 segundos, en los que se puede modificar varias veces el registro antes de ser persistido. Además, en el caso de incrementar el tamaño del documento y de que los nuevos datos no se puedan almacenar junto los antiguos, se mueve el documento entero hasta un área vacía del archivo de datos (esto es, se evita en gran medida la fragmentación del mismo).

Un aspecto relevante de MongoDB es que soporta consultas dinámicas (comportamiento análogo al de SQL), es decir, se pueden formular sobre cualquier valor de los documentos y no solamente en los indexados. Por otra parte, la escalabilidad horizontal de esta base de datos está limitada, actualmente, a 20 nodos, aunque el objetivo es alcanzar una cifra cercana a los 1000.

También dispone de MapReduce, del mismo modo que CouchDB, por lo que las diferencias más visibles entre ambas bases de datos son, aparte del lenguaje de programación en el que están implementadas, la interfaz entre estas y el desarrollador.

Apr 6 10

CouchDB

by Cristian Requena

CouchDB es una base de datos de documentos, de código abierto, mantenida por Apache igual que Cassandra.

Algo muy curioso es que está desarrollada en Erlang, un lenguaje de programación muy robusto y fiable, que se ejecuta sobre una máquina virtual de alto rendimiento en el área del paralelismo, que además es cross-platform. Para la realización de consultas utiliza un motor orientado a tablas consultable mediante JavaScript.

Un documento de CouchDB, que se distingue mediante un Id único, es un conjunto de campos identificados por etiquetas, siendo estos valores numéricos, alfanuméricos, fechas, listas, mapas asociativos, etc. Permite la creación de vistas, que son el mecanismo que permite la combinación de documentos para retornar valores de varios documentos, es decir, CouchDB permite la realización de las operaciones JOIN típicas de SQL.

Esta base de datos no tiene esquema, por lo que contiene datos semiestructurados y permite la inclusión de nuevos tipos de documentos (es decir, permite realizar la definición de datos) sin implicar la modificación de los anteriores.

CouchDB es distribuida y eventualmente consistente, por lo que de igual modo que Cassandra, implementa sistemas de replicación y de resincronización en caso de splits entre nodos. La comunicación de actualizaciones se realiza incrementalmente. De todos modos ofrece las características ACID de las bases de datos relacionales, pero permitiendo siempre la lectura de documentos, es decir, que estas nunca se bloquean, ya que al iniciarlas se crea una “imagen” del documento que tras ser consultada de destruye. En resumen, CouchDB ofrece un mecanismo de control de múltiples versiones concurrentes, MVCC.

Internamente, los documentos se almacenan en un potente arbol binario mediante su identificador y un número de secuencia, que es incremental en cada actualización y global para toda la base de datos, que permite la localización cronológica de los cambios.

Como se muestra en la anterior esquemática, obtenida de la página oficial de CouchDB, esta base de datos se compone de un núcleo desarrollado en Erlang y de dos enlaces externos: SpiderMonkey, una implementación de JavaScript programada en C, y Lucene Java, una biblioteca de búsqueda en Java.

Algo realmente interesante es que CouchDB persiste índices de las vistas para dar respuestas más rápidas en posteriores accesos a las mismas; esto se hace notorio concretamente con grandes volumenes de documentos.

Seguramente el concepto de bases de datos de documentos sea algo a lo que muchos desarrolladores no estén acostumbrados, pero conociendo el gran rendimiento de Erlang en entornos distribuidos es posible que a gran escala se trate de una base de datos tan rápida y fiable como cualquier otra, ofreciendo, además, un modelo de datos sin esquema y las características ACID.