Capítulo 1. Más allá de las bases de datos relacionales

Este trabajo se ha traducido utilizando IA. Agradecemos tus opiniones y comentarios: translation-feedback@oreilly.com

Si al principio la idea no es absurda, entonces no hay esperanza para ella.

Albert Einstein

Bienvenido a Cassandra: La Guía Definitiva. El objetivo de este libro es ayudar a los desarrolladores y administradores de bases de datos a comprender esta importante tecnología de bases de datos. A lo largo de este libro, exploraremos cómo se compara Cassandra con los sistemas tradicionales de gestión de bases de datos relacionales, y te ayudaremos a ponerla en práctica en tu propio entorno.

¿Qué tienen de malo las bases de datos relacionales?

Si hubiera preguntado a la gente qué querían, habrían dicho caballos más rápidos.

Henry Ford

Te pedimos que consideres un determinado modelo de datos, inventado por un pequeño equipo de una empresa con miles de empleados. Era accesible a través de una interfaz TCP/IP y estaba disponible en diversos lenguajes, como Java y los servicios web. Al principio, este modelo era difícil de entender, salvo para los informáticos más avanzados, hasta que una adopción más amplia ayudó a aclarar los conceptos. Utilizar la base de datos construida en torno a este modelo exigía aprender nuevos términos y pensar en el almacenamiento de datos de una forma diferente. Pero a medida que surgieron productos en torno a ella, más empresas y organismos públicos la utilizaron, en gran parte porque era rápida, capaz de procesar miles de operaciones por segundo. Los ingresos que generó fueron enormes.

Y entonces apareció un nuevo modelo.

El nuevo modelo era amenazador, principalmente por dos razones. En primer lugar, el nuevo modelo era muy diferente del antiguo, al que rebatió de forma contundente. Era amenazador porque puede resultar difícil comprender algo diferente y nuevo. Los debates subsiguientes pueden contribuir a atrincherar aún más a las personas en sus puntos de vista, puntos de vista que pueden haber heredado en gran medida del clima en el que aprendieron su oficio y de las circunstancias en las que trabajan. En segundo lugar, y quizá más importante, como barrera, el nuevo modelo resultaba amenazador porque las empresas habían realizado inversiones considerables en el modelo antiguo y estaban ganando mucho dinero con él. Cambiar de rumbo parecía ridículo, incluso imposible.

Por supuesto, estamos hablando de la base de datos jerárquica del Sistema de Gestión de la Información (SGI), inventada en 1966 en IBM.

El IMS se construyó para ser utilizado en el cohete lunar Saturno V. Su arquitecto fue Vern Watts, que le dedicó su carrera. Muchos de nosotros estamos familiarizados con la base de datos DB2 de IBM. La popularísima base de datos DB2 de IBM recibe su nombre como sucesora de DB1, el producto construido en torno al modelo de datos jerárquico IMS. IMS se lanzó en 1968, y posteriormente tuvo éxito en el Sistema de Control de Información al Cliente (CICS) y otras aplicaciones. Todavía se utiliza en la actualidad.

Pero en los años siguientes a la invención de IMS, el nuevo modelo, el modelo disruptivo, el modelo amenazador, era la base de datos relacional.

En su artículo de 1970, "Un modelo relacional de datos para grandes bancos de datos compartidos", el Dr. Edgar F. Codd también avanzó su teoría del modelo relacional de datos mientras trabajaba en el laboratorio de investigación de IBM en San José. Este documento se convirtió en el trabajo fundacional de los sistemas de gestión de bases de datos relacionales.

El trabajo de Codd era antitético a la estructura jerárquica de IMS. Entender y trabajar con una base de datos relacional exigía aprender nuevos términos, como relaciones, tuplas y forma normal, todo lo cual debió de sonar muy extraño a los usuarios de IMS. Presentaba ciertas ventajas clave sobre su predecesora, como la capacidad de expresar relaciones complejas entre múltiples entidades, mucho más allá de lo que podían representar las bases de datos jerárquicas.

Aunque estas ideas y su aplicación han evolucionado en cuatro décadas, la base de datos relacional sigue siendo claramente una de las aplicaciones de software con más éxito de la historia. Se utiliza en la forma de Microsoft Access en empresas unipersonales, y en gigantescas corporaciones multinacionales con clusters de cientos de instancias afinadas que representan almacenes de datos de varios terabytes. Las bases de datos relacionales almacenan facturas, registros de clientes, catálogos de productos, libros de contabilidad, esquemas de autenticación de usuarios... el mundo mismo, podría parecer. No hay duda de que la base de datos relacional es una faceta clave del panorama tecnológico y empresarial moderno, y que nos acompañará en sus diversas formas durante muchos años, al igual que el IMS en sus diversas formas. El modelo relacional presentó una alternativa al IMS, y cada uno tiene sus usos.

Así que la respuesta breve a la pregunta "¿Qué tienen de malo las bases de datos relacionales?" es "Nada".

Sin embargo, hay una respuesta bastante más larga, que dice que de vez en cuando nace una idea que cambia ostensiblemente las cosas y engendra una especie de revolución. Y sin embargo, desde otro punto de vista, tales revoluciones, vistas estructuralmente, no son más que la historia de siempre. IMS, RDBMS, NoSQL. El caballo, el coche, el avión. Cada uno se basa en el estado de la técnica, cada uno intenta resolver ciertos problemas, y por eso cada uno es bueno en ciertas cosas y menos bueno en otras. Coexisten, incluso ahora.

Así que examinemos por un momento por qué podrías considerar una alternativa a la base de datos relacional, del mismo modo que el propio Codd, hace cuatro décadas, examinó el Sistema de Gestión de la Información y pensó que quizá no era la única forma legítima de organizar la información y resolver los problemas de datos, y que quizá, para determinados problemas, podría resultar fructífero considerar una alternativa.

Te encuentras con problemas de escalabilidad cuando tus aplicaciones relacionales tienen éxito y aumenta su uso. La necesidad de reunir datos relacionados de varias tablas mediante uniones es inherente a cualquier base de datos relacional relativamente normalizada, incluso de tamaño modesto, y las uniones pueden ser lentas. La forma en que las bases de datos adquieren consistencia suele ser mediante el uso de transacciones, que requieren bloquear alguna parte de la base de datos para que no esté disponible para otros clientes. Esto puede llegar a ser insostenible con cargas muy pesadas, ya que los bloqueos implican que los usuarios competidores empiecen a hacer cola, esperando su turno para leer o escribir los datos.

Normalmente abordas estos problemas de una o varias de las siguientes maneras, a veces en este orden:

  • Lanza hardware al problema añadiendo más memoria, procesadores más rápidos y actualizando los discos. Esto se conoce como escalado vertical. Esto puede aliviarte durante un tiempo.

  • Cuando vuelven a surgir los problemas, la respuesta parece ser similar: ahora que una caja está al máximo, añades hardware en forma de cajas adicionales en un clúster de base de datos. Ahora tienes el problema de la replicación y coherencia de los datos durante el uso habitual y en escenarios de conmutación por error. Antes no tenías ese problema.

  • Ahora tienes que actualizar la configuración del sistema de gestión de la base de datos. Esto puede significar optimizar los canales que utiliza la base de datos para escribir en el sistema de archivos subyacente. Desactivas el registro o journaling, que con frecuencia no es una opción deseable (o, según tu situación, legal).

  • Tras haber prestado toda la atención que has podido al sistema de base de datos, te vuelves hacia tu aplicación. Intentas mejorar tus índices. Optimizas las consultas. Pero es de suponer que a esta escala no ignorabas por completo la optimización de índices y consultas, y ya los tenías en bastante buena forma. Así que esto se convierte en un doloroso proceso de hurgar en el código de acceso a los datos para encontrar oportunidades de ajuste. Esto podría incluir la reducción o reorganización de las uniones, la eliminación de funciones que consumen muchos recursos, como el procesamiento XML dentro de un procedimiento almacenado, etc. Por supuesto, es de suponer que estabas haciendo ese procesamiento XML por alguna razón, así que si tienes que hacerlo en algún sitio, trasladas ese problema a la capa de aplicación, con la esperanza de resolverlo allí y cruzando los dedos para no romper algo más mientras tanto.

  • En empleas una capa de caché. Para sistemas más grandes, esto podría incluir cachés distribuidas como Redis, memcached, Hazelcast, Aerospike, Ehcache o Riak. Ahora tienes un problema de coherencia entre las actualizaciones en la caché y las actualizaciones en la base de datos, que se agrava en un clúster.

  • Vuelves a centrar tu atención en la base de datos y decides que, ahora que la aplicación está construida y entiendes las rutas de consulta principales, puedes duplicar algunos de los datos para que se parezcan más a las consultas que acceden a ellos. Este proceso, llamado desnormalización, es antitético a las cinco formas normales que caracterizan el modelo relacional, e infringe Las 12 Reglas de Codd para los datos relacionales. Te recuerdas a ti mismo que vives en este mundo, y no en alguna nube teórica, y entonces te comprometes a hacer lo que debas para que la aplicación vuelva a responder a niveles aceptables, aunque ya no sea "pura".

Las 12 reglas de Codd

Codd proporcionó una lista de 12 reglas (en realidad son 13, numeradas del 0 al 12) formalizando su definición del modelo relacional como respuesta a la divergencia de las bases de datos comerciales respecto a sus conceptos originales. Codd presentó sus reglas en un par de artículos de la revista CompuWorld en octubre de 1985, y las formalizó en la segunda edición de su libro The Relational Model for Database Management (El modelo relacional para la gestión de bases de datos), actualmente agotado. Aunque las reglas de Codd representan un sistema ideal que las bases de datos comerciales han implementado normalmente sólo de forma parcial, han seguido ejerciendo una influencia clave sobre el modelado relacional de datos hasta nuestros días.

Es probable que esto te suene familiar. A escala web, los ingenieros pueden plantearse legítimamente si esta situación no es similar a la afirmación de Henry Ford de que, llegados a cierto punto, lo que quieres no es simplemente un caballo más rápido. Y han hecho un trabajo impresionante e interesante.

Por tanto, debemos comenzar aquí reconociendo que el modelo relacional es simplemente un modelo. Es decir, pretende ser una forma útil de ver el mundo, aplicable a determinados problemas. No pretende ser exhaustivo, cerrando el caso a todas las demás formas de representar datos, para no volver a examinarlas nunca más, sin dejar lugar a alternativas. Si miras la historia a largo plazo, el modelo del Dr. Codd fue bastante perturbador en su época. Era nuevo, con un vocabulario y unos términos nuevos y extraños ,como tuplas:palabras familiaresutilizadas de una forma nueva y diferente. El modelo relacional fue objeto de sospechas, y sin duda sufrió a sus vehementes detractores. Encontró oposición incluso en la propia empresa del Dr. Codd, IBM, que tenía un conjunto de productos muy lucrativos en torno a IMS y no necesitaba que un joven advenedizo le cortara el pastel.

Pero el modelo relacional disfruta ahora, posiblemente, del mejor asiento de la casa dentro del mundo de los datos. SQL cuenta con un amplio apoyo y se entiende bien. Se enseña en cursos universitarios introductorios. Los proveedores de Plataforma como Servicio (PaaS) basados en la nube, como Amazon Web Services, Google Cloud Platform, Microsoft Azure, Alibaba y Rackspace, proporcionan acceso a bases de datos relacionales como servicio, incluyendo funciones automatizadas de monitoreo y mantenimiento. A menudo, la base de datos que acabas utilizando viene dictada por las normas arquitectónicas de tu organización. Incluso en ausencia de tales normas, es prudente aprender lo que tu organización ya tiene para una plataforma de base de datos. Tus colegas de desarrollo e infraestructura tienen considerables conocimientos adquiridos con esfuerzo.

Aunque sólo sea por ósmosis (o inercia), a lo largo de los años has aprendido que una base de datos relacional es una solución única para todos.

Así que quizá una pregunta mejor no sea: "¿Qué tienen de malo las bases de datos relacionales?", sino más bien: "¿Qué problema tienes tú?".

Es decir, quieres asegurarte de que tu solución se ajusta al problema que tienes. Hay ciertos problemas que las bases de datos relacionales resuelven muy bien. Pero la explosión de la web, y en particular de las redes sociales, significa una explosión correspondiente en el volumen de datos con los que debes tratar. Cuando Tim Berners-Lee trabajó por primera vez en la web a principios de los 90, lo hizo para intercambiar documentos científicos entre doctores de un laboratorio de física. Ahora, por supuesto, la web se ha vuelto tan omnipresente que la utiliza todo el mundo, desde esos mismos científicos hasta legiones de niños de cinco años que intercambian emoji sobre gatitos. Esto significa en parte que debe soportar enormes volúmenes de datos; el hecho de que lo haga es un monumento a la ingeniosa arquitectura de la web.

Pero cuando las bases de datos relacionales tradicionales empezaron a ceder bajo su peso, quedó claro que se necesitaban nuevas soluciones.

Un rápido repaso a las bases de datos relacionales

Aunque es probable que estés familiarizado con ellas, vamos a centrarnos brevemente en algunos de los conceptos fundamentales de las bases de datos relacionales. Esto nos dará una base sobre la que considerar los avances más recientes del pensamiento en torno a las compensaciones inherentes a los sistemas de datos distribuidos, especialmente los sistemas de datos distribuidos muy grandes, como los que se requieren a escala web.

Hay muchas razones por las que la base de datos relacional se ha hecho tan abrumadoramente popular en las últimas cuatro décadas. Una importante es el Lenguaje de Consulta Estructurado (SQL), que es rico en funciones y utiliza una sintaxis sencilla y declarativa. SQL se adoptó oficialmente por primera vez como norma del Instituto Nacional Estadounidense de Normalización (ANSI) en 1986; desde entonces, ha pasado por varias revisiones y también se ha ampliado con sintaxis propietaria del proveedor, como T-SQL de Microsoft y PL/SQL de Oracle, para proporcionar funciones adicionales específicas de implementación.

SQL es potente por varias razones. Permite al usuario representar relaciones complejas con los datos, utilizando sentencias que forman el Lenguaje de Manipulación de Datos (LMD) para insertar, seleccionar, actualizar, eliminar, truncar y combinar datos. Puedes realizar una rica variedad de operaciones utilizando funciones basadas en el álgebra relacional para encontrar un valor máximo o mínimo en un conjunto, por ejemplo, o para filtrar y ordenar resultados. Las sentencias SQL permiten agrupar valores agregados y ejecutar funciones de resumen. SQL proporciona un medio para crear, alterar y eliminar directamente estructuras de esquema en tiempo de ejecución mediante el Lenguaje de Definición de Datos (DDL) . SQL también te permite conceder y revocar derechos a usuarios y grupos de usuarios utilizando la misma sintaxis.

SQL es fácil de usar. La sintaxis básica puede aprenderse rápidamente, y conceptualmente SQL y los RDBMS ofrecen una barrera de entrada baja. Los desarrolladores noveles pueden llegar a dominarlo fácilmente y, como suele ocurrir en un sector acosado por los cambios rápidos, los plazos ajustados y los presupuestos desorbitados, la facilidad de uso puede ser muy importante. Y no sólo la sintaxis es fácil de usar; hay muchas herramientas robustas que incluyen interfaces gráficas intuitivas para ver y trabajar con tu base de datos.

En parte porque es un estándar, SQL te permite integrar fácilmente tu RDBMS con una amplia variedad de sistemas. Todo lo que necesitas es un controlador para tu lenguaje de aplicación, y ya estás en marcha de una forma muy portátil. Si decides cambiar el lenguaje de implementación de tu aplicación (o el proveedor de tu RDBMS), a menudo puedes hacerlo sin problemas, suponiendo que no te hayas arrinconado utilizando montones de extensiones propietarias .

Transacciones, ACID y Two-Phase Commit

Además de las características ya mencionadas, los RDBMS y SQL también admiten transacciones. Una característica clave de las transacciones es que se ejecutan virtualmente al principio, lo que permite al programador deshacer (mediante la reversión) cualquier cambio que pueda haber ido mal durante la ejecución; si todo ha ido bien, la transacción puede confirmarse de forma fiable. Como dice Jim Gray, una transacción es "una transformación de estado" que tiene las propiedades ACID (ver "El concepto de transacción: virtudes y limitaciones").

ACID es un acrónimo de Atómico, Consistente, Aislado, Duradero, que son los indicadores que puedes utilizar para evaluar que una transacción se ha ejecutado correctamente y que ha tenido éxito:

Atómica
Atómico significa "todo o nada"; es decir, cuando se ejecuta una sentencia, todas las actualizaciones de la transacción deben tener éxito para que se considere correcta. No hay fallo parcial cuando una actualización tiene éxito y otra actualización relacionada falla. El ejemplo habitual es el de las transferencias monetarias en un cajero automático: la transferencia requiere un cargo en una cuenta y un abono en otra. Esta operación no puede subdividirse; ambas deben tener éxito.
Consistente
Coherente significa que los datos pasan de un estado correcto a otro correcto, sin posibilidad de que los lectores puedan ver valores diferentes que no tienen sentido juntos. Por ejemplo, si una transacción intenta eliminar un cliente y su historial de pedidos, no puede dejar filas de pedidos que hagan referencia a la clave principal del cliente eliminado; éste es un estado incoherente que provocaría errores si alguien intentara leer esos registros de pedidos.
Aislado
Aislado significa que las transacciones que se ejecuten simultáneamente no se enredarán entre sí; cada una se ejecutará en su propio espacio. Es decir, si dos transacciones diferentes intentan modificar los mismos datos al mismo tiempo, una de ellas tendrá que esperar a que finalice la otra.
Duradero
Una vez que una transacción ha tenido éxito, los cambios no se pierden. Esto no implica que otra transacción no vaya a modificar posteriormente los mismos datos; sólo significa que los escritores pueden estar seguros de que los cambios están disponibles para que la siguiente transacción trabaje con ellos cuando sea necesario.

El debate sobre la compatibilidad con las transacciones surge rápidamente como un punto delicado en las conversaciones sobre los almacenes de datos no relacionales, así que dediquemos un momento a revisar lo que esto significa realmente. A primera vista, las propiedades ACID parecen tan obviamente deseables que ni siquiera merecen una conversación. Es de suponer que nadie que gestione una base de datos sugeriría que las actualizaciones de datos no tienen que perdurar durante cierto tiempo; ése es el objetivo mismo de hacer actualizaciones: que estén ahí para que otros las lean. Sin embargo, un examen más sutil podría llevarte a querer encontrar una forma de afinar un poco estas propiedades y controlarlas ligeramente. Como se suele decir, no hay almuerzo gratis en Internet, y una vez que veas cómo estás pagando por las transacciones, puede que empieces a preguntarte si hay alguna alternativa.

Las transacciones se vuelven difíciles bajo una carga pesada. Cuando intentas escalar horizontalmente una base de datos relacional, convirtiéndola en distribuida, ahora debes tener en cuenta las transacciones distribuidas, en las que la transacción no opera simplemente dentro de una única tabla o una única base de datos, sino que se extiende por múltiples sistemas. Para seguir respetando las propiedades ACID de las transacciones, ahora necesitas un gestor de transacciones que orqueste a través de los múltiples nodos.

Con el fin de tener en cuenta la finalización con éxito a través de múltiples hosts, se introduce la idea de una confirmación en dos fases (a veces denominada "2PC"). La confirmación en dos fases es un algoritmo de uso común para lograr el consenso en sistemas distribuidos, que implica dos conjuntos de interacciones entre hosts conocidos como fase de preparación y fase de confirmación. Dado que la confirmación en dos fases bloquea todos los recursos asociados, sólo es útil para operaciones que pueden completarse muy rápidamente. Aunque a menudo tus operaciones distribuidas pueden completarse en menos de un segundo, no siempre es así. Algunos casos de uso requieren la coordinación entre varios hosts que puede que no controles tú mismo. Las operaciones que coordinan varias actividades diferentes pero relacionadas pueden tardar horas en actualizarse.

Bloqueos de confirmación en dos fases; es decir, los clientes ("consumidores competidores") deben esperar a que finalice una transacción anterior para poder acceder al recurso bloqueado. El protocolo esperará a que un nodo responda, aunque haya muerto. Es posible evitar una espera eterna en este caso, porque se puede establecer un tiempo de espera que permita al nodo coordinador de la transacción decidir que el nodo no va a responder y que debe abortar la transacción. Sin embargo, un bucle infinito sigue siendo posible con 2PC; esto se debe a que un nodo puede enviar un mensaje al nodo coordinador de la transacción acordando que está bien que el coordinador consigne toda la transacción. El nodo esperará entonces a que el coordinador envíe una respuesta de confirmación (o una respuesta de retroceso si, por ejemplo, otro nodo no puede confirmar); si el coordinador no funciona en este caso, es posible que ese nodo espere eternamente.

Así que para dar cuenta de estas deficiencias en la confirmación en dos fases de las transacciones distribuidas, el mundo de las bases de datos recurrió a la idea de la compensación. La compensación, utilizada a menudo en los servicios web, significa en términos sencillos que la operación se consigna inmediatamente, y luego, en caso de que se notifique algún error, se invoca una nueva operación para restaurar el estado correcto.

Hay unos cuantos patrones básicos y bien conocidos para la acción compensatoria que los arquitectos tienen que considerar con frecuencia como alternativa a la confirmación en dos fases. Entre ellas se incluyen anular la transacción si falla, decidir descartar las transacciones erróneas y reconciliarlas más tarde. Otra alternativa es reintentar las operaciones fallidas más tarde, previa notificación. En un sistema de reservas o un teletipo de venta de acciones, no es probable que éstas satisfagan tus necesidades. Para otro tipo de aplicaciones, como las de facturación o venta de entradas, esto puede ser aceptable.

El problema del compromiso en dos fases

Gregor Hohpe, arquitecto de Google, escribió una maravillosa y citada entrada de blog titulada "Starbucks no utiliza Two-Phase Commit". Muestra en términos reales lo difícil que es escalar el commit bifásico y destaca algunas de las alternativas que se mencionan aquí. Es una lectura fácil, divertida y esclarecedora. Si te interesa profundizar, el exhaustivo libro de Martin Kleppman Designing Data-Intensive Applications (O'Reilly) contiene un excelente debate en profundidad sobre la confirmación en dos fases y otros algoritmos de consenso.

Los problemas que introduce la 2PC para los desarrolladores de aplicaciones incluyen la pérdida de disponibilidad y una mayor latencia durante los fallos parciales. Ninguno de ellos es deseable. Así que, una vez que has tenido la suerte de tener el éxito suficiente como para necesitar escalar tu base de datos más allá de una sola máquina, ahora tienes que averiguar cómo gestionar las transacciones en varias máquinas y seguir haciendo que se apliquen las propiedades ACID. Tengas 10, 100 o 1.000 máquinas de base de datos, la atomicidad sigue siendo necesaria en las transacciones como si estuvieras trabajando en un único nodo. Pero ahora es una píldora mucho, mucho más grande de tragar.

Esquema

Una característica a menudo alabada de los sistemas de bases de datos relacionales son los ricos esquemas que ofrecen. Puedes representar tus objetos de dominio en un modelo relacional. Ha surgido toda una industria en torno a herramientas (caras) como el modelador de datos CA ERwin para apoyar este esfuerzo. Sin embargo, para crear un esquema correctamente normalizado, te ves obligado a crear tablas que no existen como objetos de negocio en tu dominio. Por ejemplo, un esquema para una base de datos universitaria podría requerir una tabla "alumno" y una tabla "curso". Pero debido a la relación "muchos a muchos" que existe en este caso (un estudiante puede realizar muchos cursos al mismo tiempo, y un curso tiene muchos estudiantes al mismo tiempo), tienes que crear una tabla de unión. Esto contamina un modelo de datos prístino, en el que preferirías tener sólo alumnos y cursos. También te obliga a crear sentencias SQL más complejas para unir estas tablas. Las sentencias join, a su vez, pueden ser lentas.

De nuevo, en un sistema de tamaño modesto, esto no supone un gran problema. Pero las consultas complejas y las uniones múltiples pueden volverse onerosamente lentas una vez que tengas que manejar un gran número de filas en muchas tablas.

Por último, no todos los esquemas se adaptan bien al modelo relacional. Un tipo de sistema que ha aumentado su popularidad en la última década es el sistema de procesamiento de eventos complejos o sistema de procesamiento de flujos, que representa los cambios de estado en un flujo muy rápido. A menudo resulta útil contextualizar los eventos en tiempo de ejecución frente a otros eventos que puedan estar relacionados, con el fin de inferir alguna conclusión que sirva de apoyo a la toma de decisiones empresariales. Aunque los flujos de eventos pueden representarse en términos de una base de datos relacional, como con el KSQL de Apache Kafka, a menudo es un tramo incómodo.

Si eres desarrollador de aplicaciones, sin duda estarás familiarizado con los numerosos marcos de mapeo objeto-relacional (ORM) que han surgido en los últimos años para ayudar a aliviar la dificultad de mapear los objetos de la aplicación a un modelo relacional. De nuevo, para los sistemas pequeños, ORM puede ser un alivio. Pero también introduce nuevos problemas propios, como mayores requisitos de memoria, y a menudo contamina el código de la aplicación con un código de mapeo cada vez más difícil de manejar. He aquí un ejemplo de un método Java que utiliza Hibernate para "aliviar la carga" de tener que escribir el código SQL:

@CollectionOfElements
@JoinTable(name="store_description",
  joinColumns = @JoinColumn(name="store_code"))
@MapKey(columns={@Column(name="for_store",length=3)})
@Column(name="description")
private Map<String, String> getMap() {
  return this.map;
}
//... etc.

¿Es seguro que aquí no hemos hecho más que trasladar el problema? Por supuesto, en algunos sistemas, como los que hacen un amplio uso del intercambio de documentos, como los servicios o las aplicaciones basadas en XML, no siempre hay mapeos claros a una base de datos relacional. Esto agrava el problema.

Arquitectura Sharding y Shared-Nothing

Si no puedes dividirlo, no puedes escalarlo.

Randy Shoup, Arquitecto distinguido, eBay

Otra forma de intentar escalar una base de datos relacional es introducir la fragmentación en su arquitectura. Esto se ha utilizado con buenos resultados en grandes sitios web como eBay, que soporta miles de millones de consultas SQL al día, y en otras aplicaciones web modernas. La idea es dividir los datos de modo que, en lugar de alojarlos todos en un único servidor o replicar todos los datos en todos los servidores de un clúster, divides partes de los datos horizontalmente y las alojas cada una por separado.

Por ejemplo, considera una gran tabla de clientes en una base de datos relacional. Lo menos perturbador (para el personal de programación, al menos) es escalar verticalmente añadiendo CPU, añadiendo memoria y consiguiendo discos duros más rápidos, pero si sigues teniendo éxito y añades más clientes, en algún momento (quizás hasta las decenas de millones de filas), probablemente tendrás que empezar a pensar en cómo puedes añadir más máquinas. Cuando lo hagas, ¿te limitarás a copiar los datos para que los tengan todas las máquinas? ¿O, por el contrario, divides esa única tabla de clientes para que cada base de datos tenga sólo algunos de los registros, conservando su orden? Entonces, cuando los clientes ejecuten consultas, sólo cargarán la máquina que tiene el registro que buscan, sin carga en las demás máquinas.

Parece claro que, para fragmentar, necesitas encontrar una buena clave por la que ordenar tus registros. Por ejemplo, podrías dividir tus registros de clientes en 26 máquinas, una por cada letra del alfabeto, y cada una albergaría sólo los registros de los clientes cuyos apellidos empezaran por esa letra en concreto. Sin embargo, es probable que ésta no sea una buena estrategia: probablemente no haya muchos apellidos que empiecen por "Q" o "Z", por lo que esas máquinas permanecerán inactivas mientras las máquinas "J", "M" y "S" se disparan. Podrías fragmentar según algo numérico, como el número de teléfono, la fecha de "miembro desde" o el nombre del estado del cliente. Todo depende de cómo se distribuyan tus datos concretos.

Hay tres estrategias básicas para determinar la estructura de los fragmentos:

Segmentación funcional o por fragmentos basada en características

Este es el enfoque adoptado por Randy Shoup, Arquitecto Distinguido de eBay, que en 2006 ayudó a hacer madurar la arquitectura del sitio para soportar muchos miles de millones de consultas al día. Con esta estrategia, los datos se dividen no dividiendo los registros en una sola tabla (como en el ejemplo del cliente comentado antes), sino dividiendo en bases de datos separadas las características que no se solapan mucho entre sí. Por ejemplo, en eBay, los usuarios están en un fragmento, y los artículos en venta en otro. Este enfoque depende de la comprensión de tu dominio para que puedas segmentar los datos limpiamente.

Fragmentación basada en claves

En este enfoque, encuentras una clave en tus datos que los distribuirá uniformemente entre los shards. Así, en lugar de almacenar simplemente una letra del alfabeto para cada servidor, como en el (ingenuo e impropio) ejemplo anterior, utilizas un hash unidireccional sobre un elemento de datos clave y distribuyes los datos entre las máquinas según el hash. En esta estrategia es habitual encontrar claves basadas en el tiempo o numéricas sobre las que hacer el hash.

Tabla de consulta

En este enfoque, también conocido como fragmentación basada en directorios, uno de los nodos del clúster actúa como un directorio de "Páginas Amarillas" y busca qué nodo tiene los datos a los que intentas acceder. Esto tiene dos desventajas obvias. La primera es que afectará al rendimiento cada vez que tengas que recorrer la tabla de búsqueda como un salto adicional. La segunda es que la tabla de búsqueda no sólo se convierte en un cuello de botella, sino en un único punto de fallo.

La fragmentación puede minimizar la contención en función de tu estrategia y te permite no sólo escalar horizontalmente, sino también escalar con mayor precisión, ya que puedes añadir potencia a los fragmentos concretos que la necesiten.

La fragmentación podría denominarse un tipo de arquitectura de "nada compartido" específica de las bases de datos. Una arquitectura de nada compartido es aquella en la que no hay un estado centralizado (compartido), sino que cada nodo de un sistema distribuido es independiente, por lo que no hay contención de clientes por recursos compartidos.

La arquitectura de no compartir nada ha sido popularizada más recientemente por Google, que ha escrito sistemas como su base de datos Bigtable y su implementación MapReduce que no comparten estado y, por tanto, son capaces de un escalado casi infinito. La base de datos Cassandra es una arquitectura de no compartir nada, ya que no tiene controlador central ni noción de réplicas primarias/secundarias; todos sus nodos son iguales.

Más sobre la Arquitectura de Nada Compartida

El término fue acuñado por primera vez por Michael Stonebraker, de la Universidad de California en Berkeley, en su artículo de 1986 "The Case for Shared Nothing". Son sólo unas pocas páginas. Si le echas un vistazo, verás que muchas de las características de la arquitectura de datos distribuidos de nada compartido, como la facilidad de alta disponibilidad y la capacidad de escalar a un número muy grande de máquinas, son precisamente las cosas en las que Cassandra destaca.

Muchas bases de datos no relacionales lo ofrecen automáticamente y es muy práctico; crear y mantener a mano fragmentos de datos personalizados es una propuesta perversa. Por ejemplo, MongoDB, de la que hablaremos más adelante, ofrece capacidades de autodistribución para gestionar la conmutación por error y el equilibrio de nodos. Es bueno entender la fragmentación en términos de arquitectura de datos en general, pero especialmente en términos de Cassandra en particular. Cassandra utiliza un enfoque similar a la fragmentación basada en claves para distribuir los datos entre los nodos, pero lo hace automáticamente.

Escala web

En resumen, las bases de datos relacionales son muy buenas para resolver ciertos problemas de almacenamiento de datos, pero debido a su enfoque, también pueden crear problemas propios cuando llega el momento de escalar. Entonces, a menudo necesitas encontrar una forma de deshacerte de tus uniones, lo que significa desnormalizar los datos, lo que implica mantener varias copias de los datos y alterar gravemente tu diseño, tanto en la base de datos como en tu aplicación. Además, es casi seguro que tendrás que encontrar una forma de evitar las transacciones distribuidas, que se convertirán rápidamente en un cuello de botella. Estas acciones compensatorias no están soportadas directamente en ningún RDBMS, salvo en los más caros. E incluso si puedes escribir una comprobación tan enorme, seguirás necesitando elegir cuidadosamente las claves de partición hasta el punto de que nunca podrás ignorar por completo la limitación.

Y lo que es quizás más importante, al ver algunas de las limitaciones de los RDBMS y, en consecuencia, algunas de las estrategias que los arquitectos han utilizado para mitigar sus problemas de escalado, empieza a surgir lentamente una imagen. Es una imagen que hace que algunas soluciones NoSQL parezcan quizás menos radicales y menos aterradoras de lo que podías pensar al principio, y más bien una expresión y encapsulación natural de parte del trabajo que ya se estaba haciendo para gestionar bases de datos muy grandes.

Debido a algunas de las decisiones de diseño inherentes a los RDBMS, no siempre es tan fácil escalar como otras posibilidades más recientes que tienen en cuenta la estructura de la web. Sin embargo, no es sólo la estructura de la web lo que tienes que tener en cuenta, sino también su crecimiento fenomenal, porque a medida que se dispone de más y más datos, necesitas arquitecturas que permitan a tu organización aprovechar estos datos casi en tiempo real para apoyar la toma de decisiones, y ofrecer funciones y capacidades nuevas y más potentes a tus clientes.

Escala de datos, antes y ahora

Se ha dicho, aunque es difícil de verificar, que el poeta inglés del siglo XVII John Milton había leído realmente todos los libros publicados sobre la faz de la tierra. Milton conocía muchas lenguas (incluso estaba aprendiendo navajo en el momento de su muerte), y dado que el número total de libros publicados en aquella época era de miles, esto habría sido posible. El tamaño de los almacenes de datos del mundo ha crecido algo desde entonces.

Con el rápido crecimiento de la web, hay una gran variedad de tipos de datos que deben almacenarse, procesarse y consultarse, y cierta variedad de empresas que utilizan esos datos. Piensa no sólo en los datos de los clientes de los minoristas o proveedores conocidos, y no sólo en el contenido de vídeo digital, sino también en el necesario paso a la televisión digital y en el crecimiento explosivo del correo electrónico, la mensajería, los teléfonos móviles, la RFID, el uso de la Voz sobre IP (VoIP) y el Internet de las Cosas (IoT). Las empresas que proporcionan contenidos -y los negocios de valor añadido de terceros creados a su alrededor- requieren soluciones de datos muy escalables. Considera también que un desarrollador de aplicaciones empresariales o un administrador de bases de datos típico puede estar acostumbrado a pensar en las bases de datos relacionales como el centro del universo. Entonces podría sorprenderse al saber que, dentro de las empresas, alrededor del 80% de los datos son no estructurados.

El auge de NoSQL

El reciente interés por las bases de datos no relacionales refleja la creciente sensación de necesidad en la comunidad de desarrollo de software de soluciones de datos a escala web. El término "NoSQL" empezó a ganar popularidad hacia 2009 como forma abreviada de describir estas bases de datos. Históricamente, el término ha sido objeto de mucho debate, pero ha surgido un consenso en que el término se refiere a las bases de datos no relacionales que admiten una semántica "no sólo SQL".

Varios expertos han intentado organizar estas bases de datos en algunas categorías generales; examinemos algunas de las más comunes:

Almacenes clave-valor

En un almacén clave-valor, los elementos de datos son claves que tienen un conjunto de atributos. Todos los datos relevantes para una clave se almacenan con la clave; los datos se duplican con frecuencia. Entre los almacenes de valores clave más populares están Dynamo DB de Amazon, Riak y Voldemort. Además, muchas tecnologías populares de almacenamiento en caché actúan como almacenes de valores clave, como Oracle Coherence, Redis y Memcached.

Almacenes de columna

En un almacén de columnas, también conocido como almacén de columnas anchas o almacén orientado a columnas, los datos se almacenan por columnas en lugar de por filas. Por ejemplo, en un almacén de columnas, todas las direcciones de los clientes pueden almacenarse juntas, lo que permite recuperarlas en una sola consulta. Entre los almacenes de columnas más populares están HBase de Apache Hadoop, Apache Kudu y Apache Druid.

Almacenes de documentos

La unidad básica de almacenamiento en una base de datos de documentos es el documento completo, a menudo almacenado en un formato como JSON, XML o YAML. Entre los almacenes de documentos más populares están MongoDB, CouchDB y varias ofertas de nube pública.

Bases de datos gráficas

Las bases de datos de grafos representan los datos como un grafo: una red de nodos y perímetros que conectan los nodos. Tanto los nodos como los perímetros pueden tener propiedades. Dado que dan mayor importancia a las relaciones, las bases de datos de grafos como Neo4j, JanusGraph y DataStax Graph han demostrado ser populares para crear redes sociales y aplicaciones web semánticas.

Bases de datos de objetos

Las bases de datos de objetos almacenan los datos no en términos de relaciones y columnas y filas, sino en términos de objetos, tal y como se entienden desde la disciplina de la programación orientada a objetos. Esto hace que sea sencillo utilizar estas bases de datos desde aplicaciones orientadas a objetos. Las bases de datos de objetos como db4o e InterSystems Caché te permiten evitar técnicas como los procedimientos almacenados y las herramientas de mapeo objeto-relacional (ORM). La base de datos de objetos más utilizada es Simple Storage Service (S3) de Amazon Web Services.

Bases de datos XML

Las bases de datos XML son una forma especial de bases de datos de documentos, optimizadas específicamente para trabajar con datos descritos en el Lenguaje de Marcado eXtensible (XML). Las llamadas bases de datos "nativas XML" incluyen BaseX y eXist.

Bases de datos multimodelo

Las bases de datos que admiten más de uno de estos estilos han ido ganando popularidad. Estas bases de datos "multimodelo" se basan en una base de datos primaria subyacente (casi siempre un almacén relacional, de clave-valor o de columnas) y exponen modelos adicionales como API sobre esa base de datos subyacente. Algunos ejemplos son Microsoft Azure Cosmos DB, que expone API de documentos, columnas anchas y gráficos sobre un almacén de valores clave, y DataStax Enterprise, que ofrece una API de gráficos sobre el modelo de columnas anchas de Cassandra. Las bases de datos multimodelo a menudo se promocionan por su capacidad para soportar un enfoque conocido como persistencia políglota, en el que diferentes microservicios o componentes de una aplicación pueden interactuar con los datos utilizando más de uno de los modelos que hemos descrito aquí. Trataremos un ejemplo de persistencia políglota en el Capítulo 7.

Más información sobre las bases de datos NoSQL

Para obtener una lista completa de bases de datos NoSQL, consulta el sitio NoSQL. El sitio DB-Engines también proporciona clasificaciones de popularidad de bases de datos populares por tipo, actualizadas mensualmente.

Existe una gran variedad en los objetivos y características de estas bases de datos, pero suelen compartir una serie de características comunes. La más obvia de ellas está implícita en el nombre NoSQL: estas bases de datos admiten modelos de datos, lenguajes de definición de datos (DDL) e interfaces que van más allá del SQL estándar disponible en las bases de datos relacionales populares. Además, estas bases de datos suelen ser sistemas distribuidos sin control centralizado. Hacen hincapié en la escalabilidad horizontal y la alta disponibilidad, en algunos casos a costa de una fuerte consistencia y semántica ACID. Suelen permitir un desarrollo y una implementación rápidos. Adoptan enfoques flexibles para la definición de esquemas, y en algunos casos no requieren que se defina ningún esquema por adelantado. Admiten casos de uso de Big Data y analítica.

Durante la última década, ha habido un gran número de ofertas comerciales y de código abierto en el espacio NoSQL. Su adopción y calidad han variado mucho, pero han surgido líderes en las categorías que acabamos de comentar, y muchas se han convertido en tecnologías maduras con grandes bases de instalación y soporte comercial. Nos complace informar de que Cassandra es una de esas tecnologías, en la que profundizaremos en el próximo capítulo.

Resumen

El modelo relacional ha servido bien a la industria del software durante las últimas cuatro décadas, pero el nivel de disponibilidad y escalabilidad que requieren las aplicaciones modernas ha llevado al límite la tecnología tradicional de bases de datos relacionales.

La intención de este libro no es convencerte mediante argumentos ingeniosos de que adoptes una base de datos no relacional como Apache Cassandra. Sólo pretendemos presentarte lo que Cassandra puede hacer y cómo lo hace para que puedas tomar una decisión con conocimiento de causa y empezar a trabajar con ella de forma práctica si te parece aplicable.

Quizás la pregunta definitiva, entonces, no sea "¿Qué tienen de malo las bases de datos relacionales?", sino más bien "¿Qué tipo de cosas haría con los datos si no fueran un problema?". En un mundo que ahora trabaja a escala web y mira hacia el futuro, Apache Cassandra podría ser una parte de la respuesta.

Get Cassandra: La Guía Definitiva, (Revisada) Tercera Edición, 3ª Edición now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.