Guía sobre NoSQL y Persistencia Políglota
Guía sobre NoSQL y Persistencia Políglota
Pramod J. Sadalage
Martin Fowler
Sadalage, Pramod J.
Destilado NoSQL: una breve guía para el mundo emergente de la
persistencia políglota / Pramod J Sadalage, Martin Fowler.
p. cm.
Incluye referencias bibliográficas e índice.
ISBN 978-0-321-82662-6 (pbk. : alk. papel) -- ISBN 0-321-82662-0 (pbk. :
alk. papel) 1. Bases de datos: innovaciones tecnológicas. 2. Sistemas de
almacenamiento y recuperación de información. I. Fowler, Martin,
1963- II. Título.
QA76.9.D32S228
2013 005.74--dc23
Derechos de autor © 2013 Pearson Education, Inc.
Todos los derechos reservados. Impreso en los Estados Unidos de América. Esta publicación está
protegida por derechos de autor, y se debe obtener el permiso del editor antes de cualquier
reproducción prohibida, almacenamiento en un sistema de recuperación o transmisión en cualquier
forma o por cualquier medio, electrónico, mecánico, fotocopia, grabación o similar. Para obtener
permiso para usar el material de este trabajo, envíe una solicitud por escrito a Pearson Education,
Inc., Departamento de Permisos, One Lake Street, Upper Saddle River, Nueva Jersey 07458, o puede
enviar su solicitud por fax al (201) 236-3290.
ISBN-13: 978-0-321-82662-6
ISBN-10: 0-321-82662-0
Texto impreso en los Estados Unidos en papel reciclado en RR Donnelley en Crawfordsville,
Indiana. Primera impresión, agosto de 2012
Para mis maestros Gajanan Chinchwadkar,
Dattatraya Mhaskar y Arvind Parchure. Tú eres el
que más me inspiraste, gracias.
—Pramod
Para Cindy
—Martín
Contenido
Prefacio
Parte I: Comprender
Capítulo 1: ¿Por qué
NoSQL?
1.1El valor de las bases de datos relacionales
1.1.1Introducción a los datos persistentes
1.1.2Concurrencia
1.1.3Integración
1.1.4Un modelo (en su mayoría) estándar
1.2Desajuste de impedancia
1.3Bases de datos de aplicaciones e integración
1.4Ataque de los cúmulos
1.5La aparición de NoSQL
1.6Puntos clave
Capítulo 2: Modelos de datos agregados
2.1Agregados
2.1.1Ejemplo de relaciones y agregados
2.1.2Consecuencias de la orientación agregada
2.2Modelos de datos clave-valor y de documentos
2.3Tiendas Column-Family
2.4Resumen de bases de datos orientadas a agregados
2.5Lecturas adicionales
2.6Puntos clave
Capítulo 3: Más detalles sobre los modelos de datos
3.1Relaciones
3.2Bases de datos de grafos
3.3Bases de datos sin esquema
3.4Vistas materializadas
3.5Modelado para el acceso a datos
3.6Puntos clave
Capítulo 4: Modelos de distribución
4.1Servidor único
4.2Partición
4.3Replicación maestro-esclavo
4.4Replicación punto a punto
4.5Combinación de particionamiento y replicación
4.6Puntos clave
Capítulo 5: Consistencia
5.1Coherencia de la actualización
5.2Coherencia de lectura
5.3Consistencia relajante
5.3.1El teorema de la CAP
5.4Durabilidad relajante
5.5Quórumes
5.6Lecturas adicionales
5.7Puntos clave
Capítulo 6: Sellos de versión
6.1Transacciones comerciales y del sistema
6.2Sellos de versión en varios nodos
6.3Puntos clave
Capítulo 7: Map-Reduce
7.1Mapa Básico-Reducir
7.2Particionamiento y combinación
7.3Composición de cálculos de map-reduce
7.3.1Un ejemplo de map-reduce en dos etapas
7.3.2Incremental Map-Reduce
7.4Lecturas adicionales
7.5Puntos clave
Parte II: Implementación
Capítulo 8: Bases de datos clave-valor
8.1¿Qué es un almacén de clave-valor?
8.2Características del almacén de clave-valor
8.2.1Consistencia
8.2.2Transacciones
8.2.3Características de consulta
8.2.4Estructura de los datos
8.2.5Escalada
8.3Casos de uso adecuados
8.3.1Almacenamiento de la información de la sesión
8.3.2Perfiles de usuario, preferencias
8.3.3Datos de la cesta de la compra
8.4Cuándo no usarlo
8.4.1Relaciones entre los datos
8.4.2Transacciones multioperación
8.4.3Consulta por datos
8.4.4Operaciones por conjuntos
Capítulo 9: Bases de datos de documentos
9.1¿Qué es una base de datos de documentos?
9.2Funciones
9.2.1Consistencia
9.2.2Transacciones
9.2.3Disponibilidad
9.2.4Características de consulta
9.2.5Escalada
9.3Casos de uso adecuados
9.3.1Registro de eventos
9.3.2Sistemas de gestión de contenidos, plataformas de blogs
9.3.3Analítica web o analítica en tiempo real
9.3.4Aplicaciones de comercio electrónico
9.4Cuándo no usarlo
9.4.1Transacciones complejas que abarcan diferentes operaciones
9.4.2Consultas contra la estructura de agregado variable
Capítulo 10: Tiendas de la familia de columnas
10.1¿Qué es un almacén de datos de familia de columnas?
10.2Funciones
10.2.1Consistencia
10.2.2Transacciones
10.2.3Disponibilidad
10.2.4Características de consulta
10.2.5Escalada
10.3Casos de uso adecuados
10.3.1Registro de eventos
10.3.2Sistemas de gestión de contenidos, plataformas de blogs
10.3.3Contadores
10.3.4Uso que caduca
10.4Cuándo no usarlo
Capítulo 11: Bases de datos de grafos
11.1 ¿Qué es una base de datos de grafos?
11.2 Funciones
11.2.1 Consistencia
11.2.2 Transacciones
11.2.3 Disponibilidad
11.2.4 Características de consulta
11.2.5 Escalada
11.3 Casos de uso adecuados
11.3.1 Datos conectados
11.3.2 Servicios de enrutamiento, despacho y basados en la ubicación
11.3.3 Motores de recomendación
11.4 Cuándo no usarlo
Capítulo 12: Migraciones de esquemas
12.1Cambios de esquema
12.2Cambios de esquema en RDBMS
12.2.1Migraciones para proyectos Green Field
12.2.2Migraciones en proyectos heredados
12.3Cambios de esquema en un almacén de datos NoSQL
12.3.1Migración incremental
12.3.2Migraciones en bases de datos de grafos
12.3.3Modificación de la estructura de agregados
12.4Lecturas adicionales
12.5Puntos clave
Capítulo 13: Persistencia políglota
13.1Necesidades dispares de almacenamiento de datos
13.2Uso del almacén de datos políglota
13.3Uso del servicio sobre el uso directo del almacén de datos
13.4Expansión para una mejor funcionalidad
13.5Elegir la tecnología adecuada
13.6Preocupaciones empresariales con la persistencia políglota
13.7Complejidad de la implementación
13.8Puntos clave
Capítulo 14: Más allá de NoSQL
14.1Sistemas de archivos
14.2Abastecimiento de eventos
14.3Imagen de memoria
14.4Control de versiones
14.5Bases de datos XML
14.6Bases de datos de objetos
14.7Puntos clave
Capítulo 15: Elección de la base de datos
15.1Productividad del programador
15.2Rendimiento del acceso a los datos
15.3Seguir con lo predeterminado
15.4Cubriendo sus apuestas
15.5Puntos clave
15.6Reflexiones finales
Índice de
bibliografía
Prefacio
Hemos pasado unos veinte años en el mundo de la informática empresarial. Hemos visto cambiar
muchas cosas en lenguajes, arquitecturas, plataformas y procesos. Pero a lo largo de todo este
tiempo, una cosa se ha mantenido constante: las bases de datos relacionales almacenan los datos. Ha
habido rivales, algunos de los cuales han tenido éxito en algunos nichos, pero en general la cuestión
del almacenamiento de datos para los arquitectos ha sido la cuestión de qué base de datos relacional
utilizar.
Hay mucho valor en la estabilidad de este reinado. Los datos de una organización duran mucho
más que sus programas (al menos eso es lo que la gente nos dice, hemos visto muchos programas
muy antiguos). Es valioso tener un almacenamiento de datos estable que se entienda bien y al que se
pueda acceder desde muchas plataformas de programación de aplicaciones.
Ahora, sin embargo, hay un nuevo rival en el bloque bajo la etiqueta de confrontación de NoSQL.
Nace de la necesidad de manejar grandes volúmenes de datos, lo que obligó a un cambio
fundamental hacia la construcción de grandes plataformas de hardware a través de clústeres de
servidores básicos. Esta necesidad también ha suscitado preocupaciones a largo plazo sobre las
dificultades de hacer que el código de la aplicación funcione bien con el modelo de datos
relacionales.
El término "NoSQL" está muy mal definido. Por lo general, se aplica a una serie de bases de
datos no relacionales recientes, como Cassandra, Mongo, Neo4J y Riak. Adoptan datos sin
esquema, se ejecutan en clústeres y tienen la capacidad de cambiar la coherencia tradicional por
otras propiedades útiles.
Los defensores de las bases de datos NoSQL afirman que pueden construir sistemas que sean más
eficientes, escalables mucho mejor y más fáciles de programar.
¿Es este el primer sondeo de la sentencia de muerte para las bases de datos relacionales, o un
pretendiente más al trono? Nuestra respuesta a eso es "ninguno". Las bases de datos relacionales son
una herramienta poderosa que esperamos usar durante muchas décadas más, pero vemos un cambio
profundo en el sentido de que las bases de datos relacionales no serán las únicas bases de datos en
uso. Nuestra opinión es que estamos entrando en un mundo de persistencia políglota en el que las
empresas, e incluso las aplicaciones individuales, utilizan múltiples tecnologías para la gestión de
datos. Como resultado, los arquitectos deberán estar familiarizados con estas tecnologías y ser
capaces de evaluar cuáles utilizar para las diferentes necesidades. Si no hubiéramos pensado eso, no
habríamos dedicado el tiempo y el esfuerzo a escribir este libro.
Este libro busca brindarle suficiente información para responder a la pregunta de si las bases de
datos NoSQL merecen una consideración seria para sus proyectos futuros. Cada proyecto es
diferente, y no hay forma de que podamos escribir un árbol de decisión simple para elegir el
almacén de datos adecuado. En cambio, lo que estamos intentando aquí es proporcionarle suficiente
información sobre cómo funcionan las bases de datos NoSQL, para que pueda hacer esos juicios
usted mismo sin tener que rastrear toda la web. Hemos hecho deliberadamente de este un libro
pequeño, para que puedas obtener esta visión general con bastante rapidez. No responderá a tus
preguntas de forma definitiva, pero debería reducir la gama de opciones que tienes que considerar y
ayudarte a entender qué preguntas tienes que hacer.
¿Por qué son interesantes las bases de datos NoSQL?
Vemos dos razones principales por las que las personas consideran usar una base de datos NoSQL.
•Productividad en el desarrollo de aplicaciones. Se dedica una gran cantidad de esfuerzo de
desarrollo de aplicaciones a la asignación de datos entre estructuras de datos en memoria y
una base de datos relacional. Una base de datos NoSQL puede proporcionar un modelo de
datos que se adapte mejor a las necesidades de la aplicación, lo que simplifica esa interacción
y da como resultado menos código para escribir, depurar y evolucionar.
•Datos a gran escala. A las organizaciones les resulta valioso capturar más datos y
procesarlos más rápidamente. Les resulta caro, si es que es posible, hacerlo con bases de
datos relacionales. La razón principal es que una base de datos relacional está diseñada para
ejecutarse en una sola máquina, pero suele ser más económico ejecutar grandes cantidades
de datos y computación en clústeres de muchas máquinas más pequeñas y baratas. Muchas
bases de datos NoSQL están diseñadas explícitamente para ejecutarse en clústeres, por lo
que se adaptan mejor a escenarios de macrodatos.
¿Qué hay en el libro?
Hemos dividido este libro en dos partes. La primera parte se concentra en los conceptos básicos
que creemos que necesita conocer para juzgar si las bases de datos NoSQL son relevantes para
usted y en qué se diferencian. En la segunda parte nos concentramos más en la implementación de
sistemas con bases de datos NoSQL.
El capítulo 1 comienza explicando por qué NoSQL ha tenido un aumento tan rápido: la necesidad
de procesar grandes volúmenes de datos llevó a un cambio, en los sistemas grandes, de escalar
verticalmente a escalar horizontalmente en clústeres. Esto explica una característica importante del
modelo de datos de muchas bases de datos NoSQL: el almacenamiento explícito de una estructura
enriquecida de datos estrechamente relacionados a los que se accede como una unidad. En este libro
llamamos a este tipo de estructura un agregado.
En el capítulo 2 se describe cómo se manifiestan los agregados en tres de los principales modelos
de datos en el terreno de NoSQL: clave-valor ("Key-Value and Document Data Models", p. 20),
documento ("Clave-Valor y Modelos de Datos de Documentos", p. 20), y la familia de columnas
("Column-Family Stores", p. 21) DatabasES. Los agregados proporcionan una unidad natural de
interacción para muchos tipos de aplicaciones, lo que mejora la ejecución en un clúster y facilita la
programación del acceso a los datos. El capítulo 3 se centra en la desventaja de los agregados: la
dificultad de manejar las relaciones ("Relaciones", p. 25) entre entidades en diferentes agregados.
Esto nos lleva naturalmente a las bases de datos de grafos ("Graph Databases", p. 26), un modelo de
datos NoSQL que no encaja en el campo orientado a agregados. También observamos la
característica común de las bases de datos NoSQL que funcionan sin un esquema ("Schemaless
Databases", p. 28)— una característica que proporciona una mayor flexibilidad, pero no tanto como
podría pensarse en un principio.
Una vez cubierto el aspecto del modelado de datos de NoSQL, pasamos a la distribución: el
Capítulo 4 describe cómo las bases de datos distribuyen los datos para ejecutarlos en clústeres. Esto
se descompone en fragmentación ("Sharding", p. 38) y la replicación, siendo esta última
amo-esclavo ("Replicación Maestro-Esclavo", p. 40) o peer-to-peer ("Peer-to-Peer Replication", p.
42) Replicación. Una vez definidos los modelos de distribución, podemos pasar a la cuestión de la
consistencia. Las bases de datos NoSQL proporcionan una gama más variada de opciones de
coherencia que las bases de datos relacionales, lo que es una consecuencia de ser amigables con los
clústeres. Por lo tanto, el capítulo 5 habla de cómo cambia la consistencia para las actualizaciones
("Update Consistency", p. 47) y dice ("Coherencia de lectura", p. 49), la función de los quórumes
("Quórumes", p. 57), y cómo incluso algo de durabilidad ("Durabilidad relajante", p. 56) se puede
negociar. Si has oído hablar de NoSQL, es casi seguro que has oído hablar del teorema CAP; la
sección "El teorema de la CAP" en la p. 53 explica qué es y cómo encaja.
Si bien estos capítulos se concentran principalmente en los principios de cómo se distribuyen los
datos y se mantienen coherentes, los dos capítulos siguientes hablan de un par de herramientas
importantes que hacen que esto funcione. En el capítulo 6 se describen los sellos de versión, que
sirven para realizar un seguimiento de los cambios y detectar incoherencias.
En el capítulo 7 se describe map-reduce, que es una forma particular de organizar la computación
paralela que encaja bien con los clústeres y, por lo tanto, con los sistemas NoSQL.
Una vez que hemos terminado con los conceptos, pasamos a los problemas de implementación
observando algunos ejemplos de bases de datos bajo las cuatro categorías clave: El Capítulo 8 usa
Riak como ejemplo de bases de datos clave-valor,
El Capítulo 9 toma MongoDB como ejemplo para las bases de datos de documentos, el Capítulo 10
elige Cassandra para explorar las bases de datos de familias de columnas y, finalmente, el Capítulo
11 toma Neo4J como ejemplo de bases de datos de grafos. Debemos enfatizar que este no es un
estudio exhaustivo: hay demasiados por ahí para escribir, y mucho menos para que los intentemos.
Nuestra elección de ejemplos tampoco implica ninguna recomendación. Nuestro objetivo aquí es
darle una idea de la variedad de tiendas que existen y de cómo las diferentes tecnologías de bases de
datos utilizan los conceptos que describimos anteriormente. Verás qué tipo de código necesitas
escribir para programar contra estos sistemas y tendrás una idea de la mentalidad que necesitarás
para usarlos.
Una afirmación común sobre las bases de datos NoSQL es que, dado que no tienen esquema, no
hay dificultad para cambiar la estructura de los datos durante la vida útil de una aplicación. No
estamos de acuerdo: una base de datos sin esquema todavía tiene un esquema implícito que necesita
cambiar la disciplina cuando se implementa, por lo que en el capítulo 12 se explica cómo realizar la
migración de datos tanto para esquemas seguros como para sistemas sin esquema.
Todo esto debería dejar claro que NoSQL no es una sola cosa, ni es algo que vaya a sustituir a las
bases de datos relacionales. El capítulo 13 analiza este mundo futuro de la persistencia políglota,
donde coexisten múltiples mundos de almacenamiento de datos, incluso dentro de la misma
aplicación. Luego, el capítulo 14 expande nuestros horizontes más allá de este libro, considerando
otras tecnologías que no hemos cubierto y que también pueden ser parte de este mundo políglota
persistente.
Con toda esta información, finalmente se encuentra en un punto en el que puede elegir qué
tecnologías de almacenamiento de datos usar, por lo que nuestro capítulo final (Capítulo 15, "Elegir
su base de datos", p. 147) ofrece algunos consejos sobre cómo pensar en estas opciones. En nuestra
opinión, hay dos factores clave: encontrar un modelo de programación productivo en el que el
modelo de almacenamiento de datos esté bien alineado con su aplicación y garantizar que pueda
obtener el rendimiento y la resiliencia del acceso a los datos que necesita. Dado que estamos en los
primeros días de la historia de la vida de NoSQL, nos tememos que no tenemos un procedimiento
bien definido a seguir, y tendrá que probar sus opciones en el contexto de sus necesidades.
Este es un breve resumen: hemos sido muy deliberados al limitar el tamaño de este libro. Hemos
seleccionado la información que creemos que es la más importante, para que usted no tenga que
hacerlo. Si vas a investigar seriamente estas tecnologías, tendrás que ir más allá de lo que cubrimos
aquí, pero esperamos que este libro te proporcione un buen contexto para iniciar tu camino.
También hay que subrayar que se trata de un campo muy volátil de la industria informática.
Aspectos importantes de estas tiendas cambian cada año: nuevas funciones, nuevas bases de datos.
Hemos hecho un gran esfuerzo para centrarnos en los conceptos, que creemos que será valioso
entender incluso cuando cambie la tecnología subyacente. Estamos bastante seguros de que la
mayor parte de lo que decimos tendrá esta longevidad, pero estamos absolutamente seguros de que
no todo lo hará.
¿Quién debería leer este libro?
Nuestro público objetivo para este libro son las personas que están considerando usar algún tipo de
base de datos NoSQL. Esto puede ser para un nuevo proyecto, o porque están chocando con barreras
que sugieren un cambio en un proyecto existente.
Nuestro objetivo es darle suficiente información para saber si la tecnología NoSQL tiene
sentido para sus necesidades y, de ser así, qué herramienta explorar con más profundidad. Nuestro
público principal es un arquitecto o un líder técnico, pero creemos que este libro también es
valioso para las personas involucradas en la gestión de software que desean obtener una visión
general de esta nueva tecnología. También creemos que si eres un desarrollador que quiere una
visión general de esta tecnología, este libro será un buen punto de partida.
No entramos en los detalles de la programación y la implementación de bases de datos específicas
aquí, dejamos eso
para libros especializados. También hemos sido muy firmes en el límite de páginas, para que este
libro sea una breve introducción. Este es el tipo de libro que creemos que deberías poder leer en
un vuelo de avión: no responderá a todas tus preguntas, pero debería darte un buen conjunto de
preguntas para hacer.
Si ya te has adentrado en el mundo de NoSQL, es probable que este libro no asigne ningún
elemento nuevo a tu almacén de conocimientos. Sin embargo, aún puede ser útil para ayudarte a
explicar lo que has aprendido a los demás. Es importante dar sentido a los problemas relacionados
con NoSQL, especialmente si está tratando de persuadir a alguien para que considere el uso de
NoSQL en un proyecto.
¿Qué son las bases de datos?
En este libro, hemos seguido un enfoque común de categorizar las bases de datos NoSQL de
acuerdo con su modelo de datos. A continuación se muestra una tabla de los cuatro modelos de
datos y algunas de las bases de datos que se ajustan a cada modelo. Esta no es una lista completa,
solo menciona las bases de datos más comunes con las que nos hemos encontrado. En el momento
de escribir este artículo, puede encontrar listas más completas en [Link] y
[Link] Para cada categoría, marcamos en cursiva la base de datos
que utilizamos como ejemplo en el capítulo correspondiente.
Nuestro objetivo es elegir una herramienta representativa de cada una de las categorías de las
bases de datos. Si bien hablamos de ejemplos específicos, la mayor parte de la discusión debe
aplicarse a toda la categoría, aunque estos productos son únicos y no se pueden generalizar como
tales. Elegiremos una base de datos para cada una de las bases de datos clave-valor, documento,
familia de columnas y grafos; Cuando corresponda, mencionaremos otros productos que pueden
satisfacer una necesidad de característica específica.
Esta clasificación por modelo de datos es útil, pero tosca. Las líneas entre los diferentes modelos
de datos, como la distinción entre bases de datos clave-valor y de documentos ("Modelos de datos de
clave-valor y documentos", p. 20), a menudo son borrosas. Muchas bases de datos no encajan
claramente en las categorías; por ejemplo, OrientDB se denomina a sí mismo una base de datos de
documentos y una base de datos de gráficos.
Reconocimientos
Nuestro primer agradecimiento a nuestros colegas de ThoughtWorks, muchos de los cuales han
estado aplicando NoSQL a nuestros proyectos de entrega durante los últimos años. Sus experiencias
han sido una fuente primaria tanto de nuestra motivación para escribir este libro como de
información práctica sobre el valor de esta tecnología. La experiencia positiva que hemos tenido
hasta ahora con los almacenes de datos NoSQL es la base de nuestra opinión de que se trata de una
tecnología importante y un cambio significativo en el almacenamiento de datos.
También nos gustaría agradecer a varios grupos que han dado charlas públicas, publicado artículos
y blogs sobre su uso de NoSQL. Gran parte del progreso en el desarrollo de software se oculta
cuando las personas no comparten con sus compañeros lo que han aprendido. Un agradecimiento
especial a Google y Amazon, cuyos artículos sobre Bigtable y Dynamo fueron muy influyentes en la
puesta en marcha del movimiento NoSQL. También agradecemos a las empresas que han
patrocinado y contribuido al desarrollo de código abierto de bases de datos NoSQL. Una diferencia
interesante con los cambios anteriores en el almacenamiento de datos es el grado en que el
movimiento NoSQL está arraigado en el trabajo de código abierto.
Un agradecimiento especial a ThoughtWorks por darnos el tiempo para trabajar en este libro.
Nos unimos a ThoughtWorks casi al mismo tiempo y hemos estado aquí durante más de una
década. ThoughtWorks sigue siendo un hogar muy hospitalario para nosotros, una fuente de
conocimiento y práctica, y un entorno acogedor para compartir abiertamente lo que aprendemos,
tan diferente de las organizaciones tradicionales de entrega de sistemas.
Bethany Anders-Beck, Ilias Bartolini, Tim Berglund, Duncan Craig, Paul Duvall, Oren Eini,
Perryn Fowler, Michael Hunger, Eric Kascic, Joshua Kerievsky, Anand Krishnaswamy, Bobby
Norton, Ade Oshineye, Thiyagu Palanisamy, Prasanna Pendse, Dan Pritchett, David Rice, Mike
Roberts, Marko Rodriquez, Andrew Slocum, Toby Tripp, Steve Vinoski, Dean Wampler, Jim
Webber y Wee Witthawaskul revisaron los primeros borradores de este libro y nos ayudaron a
mejorarlo con sus consejos.
Además, Pramod desea agradecer a la Biblioteca de Schaumburg por brindar un excelente
servicio y un espacio tranquilo para escribir; Arhana y Arula, mis hermosas hijas, por su
comprensión de que papá iría a la biblioteca y no las llevaría; Rupali, mi amada esposa, por su
inmenso apoyo y ayuda para mantenerme enfocado.
Parte I: Comprender
Capítulo 1. ¿Por qué NoSQL?
Durante casi todo el tiempo que hemos estado en la profesión del software, las bases de datos
relacionales han sido la opción predeterminada para el almacenamiento de datos serio,
especialmente en el mundo de las aplicaciones empresariales. Si usted es un arquitecto que
comienza un nuevo proyecto, es probable que su única opción sea qué base de datos relacional usar.
(Y a menudo ni siquiera eso, si su empresa tiene un proveedor dominante). Ha habido ocasiones en
las que una tecnología de bases de datos amenazó con tomar una parte de la acción, como las bases
de datos de objetos en la década de 1990, pero estas alternativas nunca llegaron a ninguna parte.
Después de un período tan largo de dominio, el entusiasmo actual por las bases de datos
NoSQL es una sorpresa. En este capítulo exploraremos por qué las bases de datos relacionales se
volvieron tan dominantes y por qué creemos que el auge actual de las bases de datos NoSQL no es
un destello en la sartén.
1.1.El valor de las bases de datos relacionales
Las bases de datos relacionales se han convertido en una parte tan integrada de nuestra cultura
informática que es fácil darlas por sentado. Por lo tanto, es útil revisar los beneficios que brindan.
1.1.1.Introducción a los datos persistentes
Probablemente el valor más obvio de una base de datos es mantener grandes cantidades de datos
persistentes. La mayoría de las arquitecturas de computadoras tienen la noción de dos áreas de
memoria: una "memoria principal" volátil rápida y un "almacén de respaldo" más grande pero más
lento. La memoria principal tiene un espacio limitado y pierde todos los datos cuando se pierde
energía o le sucede algo malo al sistema operativo. Por lo tanto, para mantener los datos, los
escribimos en un almacén de respaldo, comúnmente visto como un disco (aunque en estos días ese
disco puede ser memoria persistente).
La tienda de respaldo se puede organizar de muchas maneras. Para muchas aplicaciones de
productividad (como los procesadores de texto), es un archivo en el sistema de archivos del
sistema operativo. Sin embargo, para la mayoría de las aplicaciones empresariales, el almacén de
respaldo es una base de datos. La base de datos permite más flexibilidad que un sistema de
archivos para almacenar grandes cantidades de datos de una manera que permite a un programa de
aplicación obtener pequeños fragmentos de esa información de forma rápida y sencilla.
1.1.2.Concurrencia
Las aplicaciones empresariales tienden a tener muchas personas mirando el mismo cuerpo de datos
a la vez, posiblemente modificando esos datos. La mayoría de las veces están trabajando en
diferentes áreas de esos datos, pero ocasionalmente operan con el mismo bit de datos. Como
resultado, tenemos que preocuparnos por coordinar estas interacciones para evitar cosas como la
doble reserva de habitaciones de hotel.
La simultaneidad es notoriamente difícil de hacer bien, con todo tipo de errores que pueden
atrapar incluso a los programadores más cuidadosos. Dado que las aplicaciones empresariales
pueden tener muchos usuarios y otros sistemas que funcionan al mismo tiempo, hay mucho espacio
para que sucedan cosas malas. Las bases de datos relacionales ayudan a manejar esto controlando
todo el acceso a sus datos a través de transacciones. Si bien esto no es una panacea (todavía tiene
que manejar un error transaccional cuando intenta reservar una sala que acaba de agotarse), el
mecanismo transaccional ha funcionado bien para contener la complejidad de la simultaneidad.
Las transacciones también juegan un papel en el manejo de errores. Con las transacciones, puede
realizar un cambio y, si se produce un error durante el procesamiento del cambio, puede revertir la
transacción para limpiarlo.
1.1.3.Integración
Las aplicaciones empresariales viven en un ecosistema rico que requiere que varias aplicaciones,
escritas por diferentes equipos, colaboren para hacer las cosas. Este tipo de colaboración entre
aplicaciones es incómoda porque significa superar los límites de la organización humana. Las
aplicaciones a menudo necesitan usar los mismos datos y las actualizaciones realizadas a través de
una aplicación deben ser visibles para los demás.
Una forma común de hacer esto es la integración de bases de datos compartidas [Hohpe y
Woolf], donde varias aplicaciones almacenan sus datos en una sola base de datos. El uso de una
sola base de datos permite que todas las aplicaciones usen fácilmente los datos de las demás,
mientras que el control de simultaneidad de la base de datos maneja varias aplicaciones de la
misma manera que maneja varios usuarios en una sola aplicación.
1.1.4.Un modelo (en su mayoría) estándar
Las bases de datos relacionales han tenido éxito porque proporcionan los beneficios principales que
describimos anteriormente de una manera (en su mayoría) estándar. Como resultado, los
desarrolladores y profesionales de bases de datos pueden aprender el modelo relacional básico y
aplicarlo en muchos proyectos. Aunque existen diferencias entre las diferentes bases de datos
relacionales, los mecanismos centrales siguen siendo los mismos: los dialectos SQL de los diferentes
proveedores son similares, las transacciones funcionan prácticamente de la misma manera.
1.2.Desajuste de impedancia
Las bases de datos relacionales ofrecen muchas ventajas, pero de ninguna manera son perfectas.
Incluso desde sus primeros días, ha habido muchas frustraciones con ellos.
Para los desarrolladores de aplicaciones, la mayor frustración ha sido lo que comúnmente se
llama el desajuste de impedancia: la diferencia entre el modelo relacional y las estructuras de
datos en memoria. El modelo de datos relacional organiza los datos en una estructura de tablas y
filas, o más propiamente, relaciones y tuplas. En el modelo relacional, una tupla es un conjunto de
pares nombre-valor y una relación es un conjunto de tuplas. (La definición relacional de una tupla
es ligeramente diferente de la de las matemáticas y de muchos lenguajes de programación con un
tipo de datos de tupla, donde una tupla es una secuencia de valores). Todas las operaciones en SQL
consumen y devuelven relaciones, lo que conduce al álgebra relacional matemáticamente elegante.
Esta fundamentación en las relaciones aporta cierta elegancia y sencillez, pero también introduce
limitaciones. En concreto, los valores de una tupla relacional tienen que ser simples, es decir, no
pueden contener ninguna estructura, como un registro anidado o una lista. Esta limitación no se
aplica a las estructuras de datos en memoria, que pueden adoptar estructuras mucho más ricas que
las relaciones. Como resultado, si desea utilizar una estructura de datos en memoria más completa,
debe traducirla a una representación relacional para almacenarla en el disco. De ahí el desajuste de
impedancia, dos representaciones diferentes que requieren traducción (ver Figura 1.1).
Figura 1.1. Un pedido, que parece una sola estructura de agregado en la interfaz de usuario,
se divide en muchas filas de muchas tablas de una base de datos
relacional
La falta de coincidencia de impedancia es una fuente importante de frustración para los
desarrolladores de aplicaciones, y en la década de 1990 muchas personas creían que llevaría a que
las bases de datos relacionales fueran reemplazadas por bases de datos que replicaran las
estructuras de datos en memoria en el disco. Esa década estuvo marcada por el crecimiento de los
lenguajes de programación orientados a objetos, y con ellos llegaron las bases de datos orientadas a
objetos, que parecían ser el entorno dominante para el desarrollo de software en el nuevo milenio.
Sin embargo, mientras que los lenguajes orientados a objetos lograron convertirse en la fuerza
principal en la programación, las bases de datos orientadas a objetos se desvanecieron en la
oscuridad. Las bases de datos relacionales superaron el desafío al enfatizar su papel como
mecanismo de integración, respaldado por un lenguaje principalmente estándar de manipulación
de datos (SQL) y una creciente división profesional entre los desarrolladores de aplicaciones y los
administradores de bases de datos.
El desajuste de impedancia se ha hecho mucho más fácil de tratar gracias a la amplia
disponibilidad de marcos de mapeo relacional de objetos, como Hibernate e iBATIS que
implementan patrones de mapeo bien conocidos [Fowler PoEAA], pero el problema del mapeo sigue
siendo un problema. Los marcos de mapeo relacional de objetos eliminan una gran cantidad de
trabajo pesado, pero pueden convertirse en un problema propio cuando las personas se esfuerzan
demasiado por ignorar la base de datos y el rendimiento de las consultas se ve afectado.
Las bases de datos relacionales continuaron dominando el mundo de la informática empresarial en
la década de 2000, pero durante esa década comenzaron a abrirse grietas en su dominio.
1.3.Bases de datos de aplicaciones e integración
Las razones exactas por las que las bases de datos relacionales triunfaron sobre las bases de datos
OO siguen siendo objeto de un debate ocasional en los pubs para los desarrolladores de cierta
edad. Pero en nuestra opinión, el factor principal era el papel de SQL como mecanismo de
integración entre aplicaciones. En este escenario, la base de datos actúa como
Una base de datos de integración, con múltiples aplicaciones, generalmente desarrolladas por
equipos separados, que almacenan sus datos en una base de datos común. Esto mejora la
comunicación, ya que todas las aplicaciones funcionan con un conjunto coherente de datos
persistentes.
La integración de bases de datos compartidas tiene sus desventajas. Una estructura que está
diseñada para integrar muchas aplicaciones termina siendo más compleja (de hecho, a menudo
dramáticamente más compleja) de lo que necesita cualquier aplicación individual. Además, si una
aplicación desea realizar cambios en su almacenamiento de datos, debe coordinarse con todas las
demás aplicaciones que utilizan la base de datos. Las diferentes aplicaciones tienen diferentes
necesidades estructurales y de rendimiento, por lo que un índice requerido por una aplicación puede
causar un impacto problemático en las plaquitas de otra. El hecho de que cada aplicación suele ser
un equipo independiente también significa que la base de datos normalmente no puede confiar en
que las aplicaciones actualicen los datos de una manera que preserve la integridad de la base de
datos y, por lo tanto, debe asumir la responsabilidad de ello dentro de la propia base de datos.
Un enfoque diferente es tratar la base de datos como una base de datos de aplicaciones, a la que
solo se accede directamente desde una única base de código de aplicación que está a cargo de un
solo equipo. Con una base de datos de aplicación, solo el equipo que usa la aplicación necesita
conocer la estructura de la base de datos, lo que facilita mucho el mantenimiento y la evolución del
esquema. Dado que el equipo de la aplicación controla tanto la base de datos como el código de la
aplicación, la responsabilidad de la integridad de la base de datos se puede poner en el código de la
aplicación.
Las preocupaciones de interoperabilidad ahora pueden trasladarse a las interfaces de la aplicación,
lo que permite mejores protocolos de interacción y brinda soporte para cambiarlos. Durante la
década de 2000 vimos un cambio distintivo hacia los servicios web [Daigneau], donde las
aplicaciones se comunicaban a través de HTTP. Los servicios web permitieron una nueva forma de
un mecanismo de comunicación ampliamente utilizado: un desafío para el uso de SQL con bases de
datos compartidas. (Gran parte de este trabajo se realizó bajo el estandarte de "Arquitectura
Orientada a Servicios", un término más notable por su falta de un significado consistente).
Un aspecto interesante de este cambio hacia los servicios web como mecanismo de integración
fue que dio lugar a una mayor flexibilidad para la estructura de los datos que se intercambiaban. Si
se comunica con SQL, los datos deben estar estructurados como relaciones. Sin embargo, con un
servicio, puede utilizar estructuras de datos más completas con registros y listas anidados. Por lo
general, se representan como documentos en XML o, más recientemente, JSON. En general, con la
comunicación remota se desea reducir el número de viajes de ida y vuelta implicados en la
interacción, por lo que es útil poder poner una estructura rica de información en una sola solicitud o
respuesta.
Si va a utilizar servicios para la integración, la mayoría de las veces los servicios web, utilizando
texto a través de HTTP, es el camino a seguir. Sin embargo, si se trata de interacciones muy sensibles
al rendimiento, es posible que necesite un protocolo binario. Solo hazlo si estás seguro de que lo
necesitas, ya que es más fácil trabajar con los protocolos de texto (considera el ejemplo de Internet).
Una vez que haya tomado la decisión de utilizar una base de datos de aplicación, tendrá más
libertad para elegir una base de datos. Dado que hay un desacoplamiento entre su base de datos
interna y los servicios con los que se comunica con el mundo exterior, al mundo exterior no tiene
por qué importarle cómo almacena sus datos, lo que le permite considerar opciones no relacionales.
Además, hay muchas características de las bases de datos relacionales, como la seguridad, que son
menos útiles para una base de datos de aplicación porque pueden ser realizadas por la aplicación
adjunta.
Sin embargo, a pesar de esta libertad, no era evidente que las bases de datos de aplicaciones
condujeran a una gran carrera hacia almacenes de datos alternativos. La mayoría de los equipos
que adoptaron el enfoque de base de datos de aplicaciones se quedaron con las bases de datos
relacionales. Después de todo, el uso de una base de datos de aplicaciones produce muchas
ventajas incluso ignorando
la flexibilidad de la base de datos (por eso generalmente lo recomendamos). Las bases de datos
relacionales son familiares y suelen funcionar muy bien o, al menos, lo suficientemente bien.
Quizás, con el tiempo, podríamos haber visto el cambio a las bases de datos de aplicaciones para
abrir una grieta real en la hegemonía relacional, pero tales grietas vinieron de otra fuente.
1.4.Ataque de los cúmulos
A principios del nuevo milenio, el mundo de la tecnología se vio afectado por el estallido de la
burbuja de las puntocom de la década de 1990. Si bien esto hizo que muchas personas cuestionaran
el futuro económico de Internet, en la década de 2000 varias grandes propiedades web aumentaron
drásticamente su escala.
Este aumento de escala estaba ocurriendo en muchas dimensiones. Los sitios web comenzaron a
rastrear la actividad y la estructura de una manera muy detallada. Aparecieron grandes conjuntos de
datos: enlaces, redes sociales, actividad en registros, datos de mapeo. Con este crecimiento de los
datos vino un crecimiento de los usuarios, ya que los sitios web más grandes se convirtieron en
grandes propiedades que atendían regularmente a un gran número de visitantes.
Hacer frente al aumento de los datos y el tráfico requería más recursos informáticos. Para
manejar este tipo de aumento, tiene dos opciones: hacia arriba o hacia afuera. El escalado vertical
implica máquinas más grandes, más procesadores, almacenamiento en disco y memoria. Pero las
máquinas más grandes se vuelven cada vez más caras, sin mencionar que existen límites reales a
medida que aumenta su tamaño. La alternativa es usar muchas máquinas pequeñas en un clúster. Un
grupo de máquinas pequeñas puede usar hardware básico y termina siendo más barato en este tipo
de escalas. También puede ser más resistente: si bien los errores individuales de las máquinas son
comunes, el clúster general se puede construir para seguir funcionando a pesar de dichos errores, lo
que proporciona una alta confiabilidad.
A medida que las propiedades grandes se movían hacia los clústeres, eso reveló un nuevo
problema: las bases de datos relacionales no están diseñadas para ejecutarse en clústeres. Las bases
de datos relacionales agrupadas, como Oracle RAC o Microsoft SQL Server, funcionan según el
concepto de subsistema de disco compartido. Utilizan un sistema de archivos compatible con
clústeres que escribe en un subsistema de disco de alta disponibilidad, pero esto significa que el
clúster sigue teniendo el subsistema de disco como único punto de error. Las bases de datos
relacionales también podrían ejecutarse como servidores separados para diferentes conjuntos de
datos, fragmentando efectivamente ("Sharding", p. 38) la base de datos. Si bien esto separa la carga,
toda la fragmentación debe ser controlada por la aplicación, que tiene que realizar un seguimiento
de qué servidor de base de datos hablar para cada bit de datos. Además, perdemos los controles de
consulta, integridad referencial, transacciones o coherencia que cruzan particiones. Una frase que a
menudo escuchamos en este contexto de personas que han hecho esto es "actos antinaturales".
Estos problemas técnicos se ven agravados por los costos de las licencias. Las bases de datos
relacionales comerciales suelen tener un precio basado en la suposición de un solo servidor, por lo
que ejecutarse en un clúster elevó los precios y llevó a negociaciones frustrantes con los
departamentos de compras.
Este desajuste entre las bases de datos relacionales y los clústeres llevó a algunas organizaciones
a considerar una ruta alternativa para el almacenamiento de datos. Dos empresas en particular,
Google y Amazon, han sido muy influyentes. Ambos estaban a la vanguardia de la gestión de
grandes conglomerados de este tipo; Además, estaban capturando grandes cantidades de datos.
Estas cosas les dieron el motivo. Ambas eran empresas exitosas y en crecimiento con fuertes
componentes técnicos, lo que les dio los medios y la oportunidad. No era de extrañar que tuvieran el
asesinato en mente para sus bases de datos relacionales. A medida que avanzaba la década de 2000,
ambas empresas produjeron documentos breves pero muy influyentes sobre sus esfuerzos: BigTable
de Google y Dynamo de Amazon.
A menudo se dice que Amazon y Google operan a escalas muy alejadas de la mayoría de las
organizaciones, por lo que las soluciones que necesitaban pueden no ser relevantes para una
organización promedio. Si bien es cierto que la mayoría de los
Los proyectos de software no necesitan ese nivel de escala, también es cierto que cada vez más
organizaciones están comenzando a explorar lo que pueden hacer capturando y procesando más
datos, y a encontrarse con los mismos problemas. Entonces, a medida que se filtraba más
información sobre lo que Google y Amazon habían hecho, la gente comenzó a explorar la creación
de bases de datos en líneas similares, diseñadas explícitamente para vivir en un mundo de clústeres.
Si bien las amenazas anteriores a la dominación relacional resultaron ser fantasmas, la amenaza de
los grupos era seria.
1.5.La aparición de NoSQL
Es una maravillosa ironía que el término "NoSQL" apareciera por primera vez a finales de los 90
como el nombre de una base de datos relacional de código abierto [Strozzi NoSQL]. Dirigida por
Carlo Strozzi, esta base de datos almacena sus tablas como archivos ASCII, cada tupla representada
por una línea con campos separados por tabulaciones. El nombre proviene del hecho de que la base
de datos no utiliza SQL como lenguaje de consulta. En su lugar, la base de datos se manipula a
través de scripts de shell que se pueden combinar en las canalizaciones habituales de UNIX. Aparte
de la coincidencia terminológica, el NoSQL de Strozzi no tuvo ninguna influencia en las bases de
datos que describimos en este libro.
El uso de "NoSQL" que reconocemos hoy en día se remonta a una reunión el 11 de junio de
2009 en San Francisco organizada por Johan Oskarsson, un desarrollador de software con sede en
Londres. El ejemplo de BigTable y Dynamo había inspirado un montón de proyectos que
experimentaban con el almacenamiento de datos alternativo, y las discusiones sobre estos se
habían convertido en una característica de las conferencias sobre mejor software en ese momento.
Johan estaba interesado en obtener más información sobre algunas de estas nuevas bases de datos
mientras estaba en San Francisco para una cumbre de Hadoop. Como tenía poco tiempo allí, sintió
que no sería factible visitarlos a todos, por lo que decidió organizar un encuentro en el que todos
pudieran reunirse y presentar su trabajo a quien estuviera interesado.
Johan quería un nombre para la reunión, algo que fuera un buen hashtag de Twitter: corto,
memorable y sin demasiados resultados en Google para que una búsqueda del nombre encontrara
rápidamente la reunión. Pidió sugerencias en el canal IRC de #cassandra y obtuvo algunas,
seleccionando la sugerencia de "NoSQL" de Eric Evans (un desarrollador de Rackspace, sin
conexión con el DDD Eric Evans). Si bien tenía la desventaja de ser negativo y no describir
realmente estos sistemas, se ajustaba a los criterios del hashtag. En ese momento, estaban pensando
en nombrar solo una reunión y no esperaban que se pusiera de moda para nombrar toda esta
tendencia tecnológica [Oskarsson].
El término "NoSQL" se extendió como un reguero de pólvora, pero nunca ha sido un término que
haya tenido una definición fuerte. La convocatoria original [NoSQL Meetup] para la reunión pedía
"bases de datos de código abierto, distribuidas y no relacionales". Las charlas fueron de Voldemort,
Cassandra, Dynomite, HBase, Hypertable, CouchDB y MongoDB, pero el término nunca se ha
limitado a ese septeto original. No existe una definición generalmente aceptada, ni una autoridad
que la proporcione, por lo que todo lo que podemos hacer es discutir algunas características
comunes de las bases de datos que tienden a llamarse "NoSQL".
Para empezar, está el punto obvio de que las bases de datos NoSQL no usan SQL. Algunos de
ellos tienen lenguajes de consulta, y tiene sentido que sean similares a SQL para que sean más
fáciles de aprender. El CQL de Cassandra es así: "exactamente igual que SQL (excepto donde no lo
es)" [CQL]. Pero hasta ahora ninguno ha implementado nada que se ajuste incluso a la noción
bastante flexible de SQL estándar. Será interesante ver qué sucede si una base de datos NoSQL
establecida decide implementar un SQL razonablemente estándar; El único resultado predecible para
tal eventualidad es un montón de argumentos.
Otra característica importante de estas bases de datos es que generalmente son proyectos de
código abierto.
Aunque el término NoSQL se aplica con frecuencia a los sistemas de código cerrado, existe la
noción de que NoSQL es un fenómeno de código abierto.
La mayoría de las bases de datos NoSQL están impulsadas por la necesidad de ejecutarse en
clústeres, y esto es ciertamente cierto de las que se habló durante la reunión inicial. Esto tiene un
efecto en su modelo de datos, así como en su enfoque de coherencia. Las bases de datos relacionales
utilizan transacciones ACID (p. 19) para manejar la coherencia en toda la base de datos. Esto entra
en conflicto inherentemente con un entorno de clúster, por lo que las bases de datos NoSQL ofrecen
una gama de opciones para la coherencia y la distribución.
Sin embargo, no todas las bases de datos NoSQL están fuertemente orientadas a ejecutarse en
clústeres. Las bases de datos de grafos son un estilo de bases de datos NoSQL que utiliza un
modelo de distribución similar a las bases de datos relacionales, pero ofrece un modelo de datos
diferente que lo hace mejor en el manejo de datos con relaciones complejas.
Las bases de datos NoSQL generalmente se basan en las necesidades de los estados web de
principios del siglo XXI, por lo que generalmente solo los sistemas desarrollados durante ese
período de tiempo se denominan NoSQL, lo que descarta las montones de bases de datos creadas
antes del nuevo milenio, y mucho menos BC (antes de Codd).
Las bases de datos NoSQL funcionan sin un esquema, lo que le permite agregar campos
libremente a los registros de la base de datos sin tener que definir primero ningún cambio en la
estructura. Esto es particularmente útil cuando se trata de datos no uniformes y campos
personalizados que obligan a las bases de datos relacionales a usar nombres como customField6 o
tablas de campos personalizados que son difíciles de procesar y entender.
Todas las anteriores son características comunes de las cosas que vemos descritas como bases de
datos NoSQL. Ninguno de estos es definitorio, y de hecho es probable que nunca haya una
definición coherente de "NoSQL" (suspiro). Sin embargo, este tosco conjunto de características ha
sido nuestra guía a la hora de escribir este libro. Nuestro principal entusiasmo con este tema es que
el auge de NoSQL ha abierto la gama de opciones para el almacenamiento de datos. En
consecuencia, esta apertura no debe limitarse a lo que generalmente se clasifica como una tienda
NoSQL. Esperamos que otras opciones de almacenamiento de datos sean más aceptables, incluidas
muchas que son anteriores al movimiento NoSQL. Sin embargo, hay un límite a lo que podemos
discutir útilmente en este libro, por lo que hemos decidido concentrarnos en esta nodefinición.
Cuando escuchas por primera vez "NoSQL", una pregunta inmediata es ¿qué significa, un "no" a
SQL? La mayoría de las personas que hablan de NoSQL dicen que realmente significa "No solo
SQL", pero esta interpretación tiene un par de problemas. La mayoría de las personas escriben
"NoSQL", mientras que "No solo SQL" se escribiría "NOSQL". Además, no tendría mucho sentido
llamar a algo una base de datos NoSQL bajo el significado de "no sólo", porque entonces, Oracle o
Postgres encajarían en esa definición, demostraríamos que el negro es igual al blanco y todos serían
atropellados en los cruces peatonales.
Para resolver esto, le sugerimos que no se preocupe por lo que significa el término, sino por lo
que significa (lo cual se recomienda con la mayoría de los acrónimos). Por lo tanto, cuando
"NoSQL" se aplica a una base de datos, se refiere a un conjunto mal definido de bases de datos en
su mayoría de código abierto, en su mayoría desarrolladas a principios del siglo XXI, y en su
mayoría no utilizando SQL.
La interpretación "no sólo" tiene su valor, ya que describe el ecosistema que muchas personas
piensan que es el futuro de las bases de datos. De hecho, esto es lo que consideramos que es la
contribución más importante de esta forma de pensar: es mejor pensar en NoSQL como un
movimiento que como una tecnología. No creemos que las bases de datos relacionales vayan a
desaparecer, sino que seguirán siendo la forma más común de base de datos en uso. A pesar de que
hemos escrito este libro, seguimos recomendando las bases de datos relacionales.
Su familiaridad, estabilidad, conjunto de características y soporte disponible son argumentos
convincentes para la mayoría de los proyectos.
El cambio es que ahora vemos las bases de datos relacionales como una opción para el
almacenamiento de datos. Este punto de vista a menudo se denomina persistencia políglota, es
decir, el uso de diferentes almacenes de datos en diferentes circunstancias. En lugar de
simplemente elegir una base de datos relacional porque todo el mundo lo hace, necesitamos
comprender la naturaleza de los datos que estamos almacenando y cómo queremos manipularlos.
El resultado es que la mayoría de las organizaciones tendrán una combinación de tecnologías de
almacenamiento de datos para diferentes circunstancias.
Para que este mundo políglota funcione, nuestra opinión es que las organizaciones también
necesitan pasar de las bases de datos de integración a las bases de datos de aplicaciones. De
hecho, asumimos en este libro que utilizará una base de datos NoSQL como base de datos de
aplicación; Por lo general, no consideramos que las bases de datos NoSQL sean una buena opción
para las bases de datos de integración. No vemos esto como una desventaja, ya que creemos que
incluso si no usa NoSQL, cambiar a encapsular datos en servicios es una buena dirección a seguir.
En nuestro recuento de la historia del desarrollo de NoSQL, nos hemos concentrado en los
grandes volúmenes de datos que se ejecutan en clústeres. Si bien creemos que esta es la clave que
impulsó la apertura del mundo de las bases de datos, no es la única razón por la que vemos que los
equipos de proyecto consideran las bases de datos NoSQL. Una razón igualmente importante es la
vieja frustración con el problema del desajuste de impedancia. Las preocupaciones sobre el big data
han creado una oportunidad para que las personas piensen de nuevo sobre sus necesidades de
almacenamiento de datos, y algunos equipos de desarrollo ven que el uso de una base de datos
NoSQL puede ayudar a su productividad al simplificar el acceso a su base de datos, incluso si no
tienen necesidad de escalar más allá de una sola máquina.
Por lo tanto, mientras lee el resto de este libro, recuerde que hay dos razones principales para
considerar NoSQL. Una es manejar el acceso a los datos con tamaños y rendimiento que exijan un
clúster; La otra es mejorar la productividad del desarrollo de aplicaciones mediante el uso de un
estilo de interacción de datos más conveniente.
1.6.Puntos clave
•Las bases de datos relacionales han sido una tecnología exitosa durante veinte años,
proporcionando persistencia, control de concurrencia y un mecanismo de integración.
•Los desarrolladores de aplicaciones se han sentido frustrados con la falta de
coincidencia de impedancia entre el modelo relacional y las estructuras de datos en
memoria.
•Hay un movimiento que se aleja del uso de bases de datos como puntos de integración
hacia la encapsulación de bases de datos dentro de aplicaciones y la integración a través de
servicios.
•El factor vital para un cambio en el almacenamiento de datos fue la necesidad de soportar
grandes volúmenes de datos mediante la ejecución en clústeres. Las bases de datos
relacionales no están diseñadas para ejecutarse de forma eficaz en clústeres.
•NoSQL es un neologismo accidental. No hay una definición prescriptiva, todo lo que se
puede hacer es una observación de características comunes.
•Las características comunes de las bases de datos NoSQL son:
•No usar el modelo relacional
•Funcionamiento correcto en clústeres
•Código abierto
•Construido para las fincas web del siglo XXI
•Sin esquema
•El resultado más importante del auge de NoSQL es la persistencia políglota.
Capítulo 2. Modelos de datos agregados
Un modelo de datos es el modelo a través del cual percibimos y manipulamos nuestros datos. Para
las personas que utilizan una base de datos, el modelo de datos describe cómo interactuamos con los
datos de la base de datos. Esto es distinto de un modelo de almacenamiento, que describe cómo la
base de datos almacena y manipula los datos internamente. En un mundo ideal, deberíamos ignorar
el modelo de almacenamiento, pero en la práctica necesitamos al menos una idea de él,
principalmente para lograr un rendimiento decente.
En conversación, el término "modelo de datos" a menudo significa el modelo de los datos
específicos en una aplicación.
Un desarrollador puede señalar un diagrama entidad-relación de su base de datos y referirse a él
como su modelo de datos que contiene clientes, pedidos, productos y similares. Sin embargo, en
este libro usaremos principalmente "modelo de datos" para referirnos al modelo mediante el cual
la base de datos organiza los datos, lo que podría llamarse más formalmente un metamodelo.
El modelo de datos dominante de las últimas dos décadas es el modelo de datos relacional, que
se visualiza mejor como un conjunto de tablas, como una página de una hoja de cálculo. Cada
tabla tiene filas, y cada fila representa alguna entidad de interés. Describimos esta entidad a través
de columnas, cada una con un solo valor. Una columna puede hacer referencia a otra fila de la
misma tabla o de una tabla diferente, lo que constituye una relación entre esas entidades. (Estamos
usando terminología informal pero común cuando hablamos de tablas y filas; los términos más
formales serían relaciones y tuplas).
Uno de los cambios más obvios con NoSQL es el alejamiento del modelo relacional. Cada
solución NoSQL tiene un modelo diferente que utiliza, que dividimos en cuatro categorías
ampliamente utilizadas en el ecosistema NoSQL: clave-valor, documento, familia de columnas y
gráfico. De estos, los tres primeros comparten una característica común de sus modelos de datos
que denominaremos orientación agregada. En este capítulo explicaremos qué entendemos por
orientación agregada y qué significa para los modelos de datos.
2.1.Agregados
El modelo relacional toma la información que queremos almacenar y la divide en tuplas (filas). Una
tupla es una estructura de datos limitada: captura un conjunto de valores, por lo que no se puede
anidar una tupla dentro de otra para obtener registros anidados, ni se puede poner una lista de valores
o tuplas dentro de otra. Esta simplicidad sustenta el modelo relacional: nos permite pensar en todas
las operaciones como si operaran sobre tuplas y las devolvieran.
La orientación agregada adopta un enfoque diferente. Reconoce que, a menudo, desea operar
con datos en unidades que tienen una estructura más compleja que un conjunto de tuplas. Puede
ser útil pensar en términos de un registro complejo que permita que las listas y otras estructuras de
registros se aniden dentro de él. Como veremos, las bases de datos de clave-valor, de documentos
y de familias de columnas hacen uso de este registro más complejo.
Sin embargo, no existe un término común para este complejo registro; en este libro usamos el
término "agregado". Aggregate es un término que proviene de Domain-Driven Design [Evans]. En el
diseño controlado por dominios, un agregado es una colección de objetos relacionados que
deseamos tratar como una unidad. En particular, es una unidad para
Manipulación de datos y gestión de la consistencia. Por lo general, nos gusta actualizar los
agregados con
operaciones atómicas y comunicarse con nuestro almacenamiento de datos en términos de
agregados. Esta definición coincide muy bien con el funcionamiento de las bases de datos de
clave-valor, de documentos y de familias de columnas. El tratamiento con agregados hace que sea
mucho más fácil para estas bases de datos manejar el funcionamiento en un clúster, ya que el
agregado es una unidad natural para la replicación y el particionamiento. Los agregados también
suelen ser más fáciles de trabajar para los programadores de aplicaciones, ya que a menudo
manipulan los datos a través de estructuras de agregados.
2.1.1.Ejemplo de relaciones y agregados
Llegados a este punto, un ejemplo puede ayudar a explicar de qué estamos hablando. Supongamos
que tenemos que construir un sitio web de comercio electrónico; Vamos a vender artículos
directamente a los clientes a través de la web, y tendremos que almacenar información sobre los
usuarios, nuestro catálogo de productos, pedidos, direcciones de envío, direcciones de facturación y
datos de pago. Podemos usar este escenario para modelar los datos utilizando un almacén de datos
de relación, así como almacenes de datos NoSQL y hablar sobre sus pros y contras. Para una base de
datos relacional, podríamos comenzar con un modelo de datos que se muestra en la Figura 2.1.
Figura 2.1. Modelo de datos orientado en torno a una base de datos relacional (utilizando
notación UML [Fowler UML])
En la figura 2.2 se presentan algunos datos de muestra para este modelo.
Figura 2.2. Datos típicos que utilizan el modelo de datos RDBMS
Como somos buenos soldados relacionales, todo está correctamente normalizado, de modo que
ningún dato se repita en varias tablas. También tenemos integridad referencial. Un sistema de orden
realista sería naturalmente más complicado que esto, pero este es el beneficio del aire enrarecido de
un libro.
Veamos ahora cómo se vería este modelo cuando pensamos en términos más orientados a los
agregados (Figura 2.3).
en los clientes
{
"id":1,
"name":"Martin",
"billingAddress":[{"city":"Chicago"}]
}
En los pedidos
{
"id":99,
"customerId":1,
"orderItems":[
{
"productId":27,
"price": 32.45,
"productName": "NoSQL destilado"
}
],
"shippingAddress":[{"city":"Chicago"}]
"orderPayment":[
{
"ccinfo":"1000-1000-1000-1000",
"txnId":"abelif879rft",
"billingAddress": {"city": "Chicago"}
}
],
}
En este modelo, tenemos dos agregados principales: cliente y pedido. Hemos usado el diamante
negro
composition en UML para mostrar cómo encajan los datos en la estructura de agregación. El
cliente contiene una lista de direcciones de facturación; El pedido contiene una lista de los
artículos del pedido, una dirección de envío y pagos. El pago en sí contiene una dirección de
facturación para ese pago.
Un único registro de dirección lógica aparece tres veces en los datos de ejemplo, pero en lugar de
usar identificadores, se trata como un valor y se copia cada vez. Esto se ajusta al dominio en el que
no queremos que cambie la dirección de envío, ni la dirección de facturación del pago. En una base
de datos relacional, nos aseguraríamos de que las filas de direcciones no se actualicen para este
caso, creando una nueva fila en su lugar. Con los agregados, podemos copiar toda la estructura de
direcciones en el agregado según lo necesitemos.
El vínculo entre el cliente y el pedido no se encuentra dentro de ninguno de los agregados, sino
que es una relación entre agregados. Del mismo modo, el enlace de un artículo de pedido se cruzaría
a una estructura agregada separada para los productos, en la que no hemos entrado. Aquí hemos
mostrado el nombre del producto como parte del artículo del pedido: este tipo de desnormalización
es similar a las compensaciones con las bases de datos relacionales, pero es más común con los
agregados porque queremos minimizar el número de agregados a los que accedemos durante una
interacción de datos.
Lo importante que hay que tener en cuenta aquí no es la forma concreta en que hemos dibujado el
límite agregado, sino el hecho de que tiene que pensar en acceder a esos datos, y hacer que eso
forme parte de su pensamiento al desarrollar el modelo de datos de la aplicación. De hecho,
podríamos trazar nuestros límites agregados de manera diferente, colocando todos los pedidos de un
cliente en el agregado de clientes (Figura 2.4).
Figura 2.4. Incrustar todos los objetos para el cliente y los pedidos del cliente
Usando el modelo de datos anterior, un ejemplo de Cliente y Pedido sería el siguiente:
Haga clic aquí para ver la imagen del código
en los clientes
{
"cliente": {
"id": 1,
"name": "Martín",
"billingAddress": [{"city":
"Chicago"}], "orders": [
{
"ídem":99,
"customerId":1,
"orderItems":[
{
"productId":27,
"price": 32.45,
"productName": "NoSQL destilado"
}
],
"shippingAddress":[{"city":"Chicago"}
] "orderPayment":[
{
"ccinfo":"1000-1000-1000-1000",
"txnId":"abelif879rft",
"billingAddress": {"city": "Chicago"}
}],
}]
}
}
Como la mayoría de las cosas en el modelado, no hay una respuesta universal sobre cómo dibujar
el agregado
Límites. Depende completamente de cómo tiendas a manipular tus datos. Si tiende a acceder a un
cliente junto con todos los pedidos de ese cliente a la vez, entonces preferiría un solo agregado. Sin
embargo, si tiende a centrarse en acceder a un solo pedido a la vez, entonces debe preferir tener
agregados separados para cada pedido. Naturalmente, esto es muy específico del contexto; Algunas
aplicaciones preferirán uno u otro, incluso dentro de un mismo sistema, que es exactamente la razón
por la que muchas personas prefieren la ignorancia agregada.
2.1.2.Consecuencias de la orientación agregada
Si bien el mapeo relacional captura razonablemente bien los diversos elementos de datos y sus
relaciones, lo hace sin ninguna noción de una entidad agregada. En nuestro idioma de dominio,
podríamos decir que un pedido consta de artículos de pedido, una dirección de envío y un pago.
Esto se puede expresar en el modelo relacional en términos de relaciones de clave externa, pero
no hay nada que distinga las relaciones que representan agregaciones de las que no lo hacen.
Como resultado, la base de datos no puede usar un conocimiento de la estructura agregada para
ayudar a almacenar y distribuir los datos.
Varias técnicas de modelado de datos han proporcionado formas de marcar estructuras agregadas
o compuestas. El problema, sin embargo, es que los modeladores rara vez proporcionan una
semántica para lo que hace que una relación agregada sea diferente de cualquier otra; Donde hay
semántica, varían. Cuando se trabaja con bases de datos orientadas a agregados, tenemos una
semántica más clara a tener en cuenta centrándonos en la unidad de interacción con el
almacenamiento de datos. Sin embargo, no se trata de una propiedad de datos lógica: se trata de
cómo las aplicaciones utilizan los datos, una preocupación que a menudo está fuera de los límites
del modelado de datos.
Las bases de datos relacionales no tienen el concepto de agregado dentro de su modelo de
datos, por lo que las llamamos ignorantes de agregados. En el mundo NoSQL, las bases de
datos de grafos también ignoran los agregados. Ser ignorante de los agregados no es algo
malo. A menudo es difícil trazar bien los límites agregados, especialmente si se utilizan los
mismos datos en muchos contextos diferentes. Un pedido se agrega bien cuando un cliente
está haciendo y revisando pedidos, y cuando el minorista está procesando pedidos.
Sin embargo, si un minorista quiere analizar las ventas de sus productos en los últimos meses,
entonces un agregado de pedidos se convierte en un problema. Para acceder al historial de ventas de
productos, tendrás que indagar en todos los agregados de la base de datos. Por lo tanto, una
estructura agregada puede ayudar con algunas interacciones de datos, pero ser un obstáculo para
otras. Un modelo de ignorante agregado le permite examinar fácilmente los datos de diferentes
maneras, por lo que es una mejor opción cuando no tiene una estructura principal para manipular
los datos.
La razón clave para la orientación agregada es que ayuda en gran medida con la ejecución en un
clúster, que, como recordará, es el argumento principal para el auge de NoSQL. Si se ejecuta en un
clúster, debemos minimizar la cantidad de nodos que debemos consultar cuando recopilamos datos.
Al incluir explícitamente agregados, le damos a la base de datos información importante sobre qué
bits de datos se manipularán juntos y, por lo tanto, deben residir en el mismo nodo.
Los agregados tienen una consecuencia importante para las transacciones. Las bases de datos
relacionales permiten manipular cualquier combinación de filas de cualquier tabla en una sola
transacción. Estas transacciones se denominan transacciones ACID: atómicas, consistentes,
aisladas y duraderas. ACID es un acrónimo bastante artificioso; el punto real es la atomicidad:
muchas filas que abarcan muchas tablas se actualizan como una sola operación. Esta operación se
realiza correctamente o se produce un error en su totalidad, y las operaciones simultáneas son
aislados entre sí para que no puedan ver una actualización parcial.
A menudo se dice que las bases de datos NoSQL no admiten transacciones ACID y, por lo
tanto, sacrifican la consistencia. Esta es una simplificación bastante radical. En general, es cierto
que las bases de datos orientadas a agregados no tienen transacciones ACID que abarquen varios
agregados. En cambio, apoyan la manipulación atómica de un solo agregado a la vez. Esto
significa que si necesitamos manipular múltiples agregados de forma atómica, tenemos que
gestionarlo nosotros mismos en el código de la aplicación. En la práctica, encontramos que la
mayoría de las veces somos capaces de mantener nuestras necesidades de atomicidad dentro de un
solo agregado; De hecho, eso es parte de la consideración para decidir cómo dividir nuestros datos
en agregados. También debemos recordar que las bases de datos de grafos y otras bases de datos
que ignoran los agregados suelen admitir transacciones ACID similares a las bases de datos
relacionales. Sobre todo, el tema de la consistencia es mucho más complicado que si una base de
datos es ACID o no, como exploraremos en el Capítulo 5.
2.2.Modelos de datos clave-valor y de documentos
Dijimos anteriormente que las bases de datos clave-valor y de documentos estaban fuertemente
orientadas a la agregación. Lo que queremos decir con esto es que pensamos que estas bases de
datos se construyen principalmente a través de agregados. Ambos tipos de bases de datos constan de
muchos agregados, cada uno de los cuales tiene una clave o identificador que se usa para acceder a
los datos.
Los dos modelos difieren en que en una base de datos de clave-valor, el agregado es opaco a la
base de datos, es decir, solo una gran masa de bits en su mayoría sin sentido. Por el contrario, una
base de datos de documentos es capaz de ver una estructura en conjunto. La ventaja de la opacidad
es que podemos almacenar lo que queramos en conjunto. La base de datos puede imponer algún
límite general de tamaño, pero aparte de eso, tenemos total libertad. Una base de datos de
documentos impone límites a lo que podemos colocar en ella, definiendo estructuras y tipos
permitidos. A cambio, sin embargo, obtenemos más flexibilidad en el acceso.
Con un almacén de clave-valor, solo podemos acceder a un agregado mediante una búsqueda
basada en su clave. Con una base de datos de documentos, podemos enviar consultas a la base de
datos en función de los campos del agregado, podemos recuperar parte del agregado en lugar de
todo y la base de datos puede crear índices basados en el contenido del agregado.
En la práctica, la línea entre clave-valor y documento se vuelve un poco borrosa. A menudo, las
personas colocan un campo de ID en una base de datos de documentos para realizar una búsqueda
de estilo clave-valor. Las bases de datos clasificadas como bases de datos de clave-valor pueden
permitir estructuras para datos más allá de un agregado opaco. Por ejemplo, Riak le permite agregar
metadatos a los agregados para indexar y vincular enlaces interagregados, Redis le permite dividir
el agregado en listas o conjuntos. Puede admitir consultas mediante la integración de herramientas
de búsqueda como Solr. Por ejemplo, Riak incluye un servicio de búsqueda que utiliza una
búsqueda similar a la de Solr en cualquier agregado que se almacene como estructuras JSON o
XML.
A pesar de esta borrosidad, la distinción general sigue siendo válida. Con las bases de datos de
clave-valor, esperamos buscar principalmente agregados mediante una clave. Con las bases de datos
de documentos, la mayoría de las veces esperamos enviar algún tipo de consulta basada en la
estructura interna del documento; Esto podría ser una clave, pero es más probable que sea otra cosa.
2.3.Tiendas Column-Family
Una de las primeras e influyentes bases de datos NoSQL fue BigTable de Google [Chang, etc.]. Su
nombre evocaba una estructura tabular que realizó con columnas dispersas y sin esquema. Como
pronto verás, no ayuda pensar en esta estructura como una mesa; más bien, es un mapa de dos
niveles. Pero, independientemente de cómo pienses en la estructura, ha sido un modelo que influyó
en bases de datos posteriores como HBase y
Casandra.
Estas bases de datos con un modelo de datos de estilo bigtable a menudo se denominan
almacenes de columnas, pero ese nombre ha existido durante un tiempo para describir a un animal
diferente. Los almacenes de columnas anteriores a NoSQL, como C-Store [C-Store], estaban
satisfechos con SQL y el modelo relacional. Lo que los hacía diferentes era la forma en que
almacenaban físicamente los datos. La mayoría de las bases de datos tienen una fila como unidad
de almacenamiento que, en particular, ayuda a escribir el rendimiento. Sin embargo, hay muchos
escenarios en los que las escrituras son poco frecuentes, pero a menudo es necesario leer algunas
columnas de muchas filas a la vez. En esta situación, es mejor almacenar grupos de columnas para
todas las filas como unidad de almacenamiento básica, por lo que estas bases de datos se
denominan almacenes de columnas.
Bigtable y sus descendientes siguen esta noción de almacenar grupos de columnas (familias de
columnas) juntos, pero se separan de C-Store y sus amigos al abandonar el modelo relacional y SQL.
En este libro, nos referimos a esta clase de bases de datos como bases de datos de familias de
columnas.
Quizás la mejor manera de pensar en el modelo de familia de columnas es como una estructura
agregada de dos niveles. Al igual que con los almacenes de clave-valor, la primera clave a menudo
se describe como un identificador de fila, que recoge el agregado de interés. La diferencia con las
estructuras de familias de columnas es que este agregado de filas está formado por un mapa de
valores más detallados. Estos valores de segundo nivel se denominan columnas. Además de acceder
a la fila en su conjunto, las operaciones también permiten seleccionar una columna en particular, por
lo que para obtener el nombre de un cliente en particular de la Figura 2.5 , puede hacer algo como
get('1234', 'name').
Hasta ahora hemos cubierto la característica clave en la mayoría de las bases de datos NoSQL: su
uso de agregados y cómo las bases de datos orientadas a agregados modelan agregados de
diferentes maneras. Si bien los agregados son una parte central de la historia de NoSQL, hay más
en el lado del modelado de datos que eso, y exploraremos estos conceptos adicionales en este
capítulo.
3.1.Relaciones
Los agregados son útiles en el sentido de que reúnen datos a los que normalmente se accede juntos.
Pero todavía hay muchos casos en los que se accede a los datos relacionados de manera diferente.
Considere la relación entre un cliente y todos sus pedidos. Algunas aplicaciones querrán acceder al
historial de pedidos cada vez que accedan al cliente; Esto encaja bien con la combinación del
cliente con su historial de pedidos en un solo agregado. Otras aplicaciones, sin embargo, desean
procesar pedidos individualmente y, por lo tanto, modelar pedidos como agregados independientes.
En este caso, querrá agregados de pedidos y clientes separados, pero con algún tipo de relación
entre ellos, de modo que cualquier trabajo en un pedido pueda buscar datos de clientes. La forma
más sencilla de proporcionar un enlace de este tipo es incrustar el ID del cliente en los datos
agregados del pedido. De este modo, si necesita datos del registro de cliente, lea el pedido, averigüe
el ID de cliente y realice otra llamada a la base de datos para leer los datos del cliente. Esto
funcionará, y estará bien en muchos escenarios, pero la base de datos ignorará la relación en los
datos. Esto puede ser importante porque hay ocasiones en las que es útil que la base de datos
conozca estos enlaces.
Como resultado, muchas bases de datos, incluso los almacenes de clave-valor, proporcionan
formas de hacer que estas relaciones sean visibles para la base de datos. Los almacenes de
documentos hacen que el contenido del agregado esté disponible para la base de datos para formar
índices y consultas. Riak, un almacén de clave-valor, le permite colocar información de enlaces en
metadatos, lo que admite la recuperación parcial y la capacidad de recorrido por enlaces.
Un aspecto importante de las relaciones entre agregados es cómo manejan las actualizaciones.
Las bases de datos orientadas a agregados tratan el agregado como la unidad de recuperación de
datos. En consecuencia, la atomicidad solo se admite dentro del contenido de un solo agregado. Si
actualiza varios agregados a la vez, tendrá que lidiar con un error a mitad de camino. Las bases de
datos relacionales le ayudan con esto al permitirle modificar varios registros en una sola
transacción, proporcionando garantías ACID mientras altera muchas filas.
Todo esto significa que las bases de datos orientadas a agregados se vuelven más incómodas a
medida que necesita operar en varios agregados. Hay varias formas de lidiar con esto, que
exploraremos más adelante en este capítulo, pero la incomodidad fundamental permanece.
Esto puede implicar que si tiene datos basados en muchas relaciones, debe preferir una base de
datos relacional en lugar de un almacén NoSQL. Si bien eso es cierto para las bases de datos
orientadas a agregados, vale la pena recordar que las bases de datos relacionales tampoco son tan
estelares con relaciones complejas. Si bien puede expresar consultas que involucran combinaciones
en SQL, las cosas rápidamente se vuelven muy peliagudas, tanto con la escritura SQL como con el
rendimiento resultante, a medida que aumenta el número de combinaciones.
Esto hace que sea un buen momento para introducir otra categoría de bases de datos que a
menudo se agrupan en la pila NoSQL.
3.2.Bases de datos de grafos
Las bases de datos de grafos son un pez extraño en el estanque de NoSQL. La mayoría de las
bases de datos NoSQL se inspiraron en la necesidad de ejecutarse en clústeres, lo que condujo a
modelos de datos orientados a la agregación de grandes registros con conexiones simples. Las
bases de datos de grafos están motivadas por una frustración diferente con las bases de datos
relacionales y , por lo tanto, tienen un modelo opuesto: registros pequeños con interconexiones
complejas, algo así como la Figura 3.1.
Pseudo código
foreach (Registrar r en
registros) { foreach (Campo f
en [Link]) {
Imprimir ([Link], [Link])
}
}
Supondrá que ciertos nombres de campo están presentes y llevan datos con un significado
determinado, y asumirá algo sobre el tipo de datos almacenados dentro de ese campo. Los
programas no son humanos; No pueden leer "cantidad" e inferir que eso debe ser lo mismo que
"cantidad", al menos no a menos que los programemos específicamente para que lo hagan. Por lo
tanto, por muy esquemática que sea nuestra base de datos, suele haber un esquema implícito
presente. Este esquema implícito es un conjunto de suposiciones sobre la estructura de los datos en
el código que manipula los datos.
Tener el esquema implícito en el código de la aplicación da lugar a algunos problemas. Esto
significa que para comprender qué datos están presentes, debe profundizar en el código de la
aplicación. Si ese código está bien estructurado, debería ser capaz de encontrar un lugar claro desde
el que deducir el esquema. Pero no hay garantías; Todo depende de lo claro que sea el código de la
aplicación. Además, la base de datos permanece ignorante del esquema, es decir, no puede usar el
esquema para decidir cómo almacenar y recuperar datos de manera eficiente. No puede aplicar sus
propias validaciones a esos datos para garantizar que las diferentes aplicaciones no manipulen los
datos de forma incoherente.
Estas son las razones por las que las bases de datos relacionales tienen un esquema fijo y, de
hecho, las razones por las que la mayoría de las bases de datos han tenido esquemas fijos en el
pasado. Los esquemas tienen valor, y el rechazo de los esquemas por parte de las bases de datos
NoSQL es realmente sorprendente.
Básicamente, una base de datos sin esquema cambia el esquema al código de la aplicación que
accede a él. Esto se vuelve problemático si varias aplicaciones, desarrolladas por diferentes
personas, acceden a la misma base de datos. Estos problemas se pueden reducir con un par de
enfoques. Una es encapsular toda la interacción de la base de datos dentro de una sola aplicación e
integrarla con otras aplicaciones mediante servicios web. Esto encaja bien con la preferencia actual
de muchas personas por el uso de servicios web para la integración. Otro enfoque consiste en
delinear claramente las diferentes áreas de un agregado para el acceso de diferentes aplicaciones.
Pueden ser diferentes secciones de una base de datos de documentos o diferentes familias de
columnas en una base de datos de familias de columnas.
Aunque los fanáticos de NoSQL a menudo critican los esquemas relacionales por tener que
definirse por adelantado y ser inflexibles, eso no es realmente cierto. Los esquemas relacionales se
pueden cambiar en cualquier momento con comandos SQL estándar. Si es necesario, puede crear
nuevas columnas de forma ad hoc para almacenar datos no uniformes. Rara vez lo hemos visto, pero
ha funcionado razonablemente bien donde lo hemos hecho. La mayoría de las veces,
Sin embargo, la falta de uniformidad en los datos es una buena razón para favorecer una base de
datos sin esquema.
La ausencia de esquemas tiene un gran impacto en los cambios de la estructura de una base de
datos a lo largo del tiempo, especialmente para datos más uniformes. Aunque no se practica tan
ampliamente como debería, cambiar el esquema de una base de datos relacional se puede hacer de
forma controlada. Del mismo modo, debe ejercer control al cambiar la forma en que almacena los
datos en una base de datos sin esquemas para que pueda acceder fácilmente a los datos antiguos y
nuevos. Además, la flexibilidad que proporciona la ausencia de esquemas solo se aplica dentro de un
agregado: si necesita cambiar los límites del agregado, la migración es tan compleja como lo es en el
caso relacional. Hablaremos más sobre la migración de bases de datos más adelante ("Schema
Migrations", p. 123).
3.4.Vistas materializadas
Cuando hablamos de modelos de datos orientados a agregados, destacamos sus ventajas. Si desea
acceder a los pedidos, es útil tener todos los datos de un pedido contenidos en un solo agregado que
se pueda almacenar y acceder como una unidad. Pero la orientación agregada tiene una desventaja
correspondiente: ¿qué sucede si un gerente de producto quiere saber cuánto se ha vendido un
artículo en particular en las últimas dos semanas? Ahora, la orientación agregada juega en su contra,
obligándolo a leer potencialmente todos los pedidos de la base de datos para responder a la pregunta.
Puede reducir esta carga mediante la creación de un índice en el producto, pero sigue trabajando en
contra de la estructura agregada.
Las bases de datos relacionales tienen una ventaja aquí porque su falta de estructura agregada les
permite admitir el acceso a los datos de diferentes maneras. Además, proporcionan un mecanismo
conveniente que le permite ver los datos de manera diferente a la forma en que se almacenan: las
vistas. Una vista es como una tabla relacional (es una relación) pero se define mediante el cálculo
sobre las tablas base. Cuando se accede a una vista, la base de datos calcula los datos de la vista,
una forma práctica de encapsulación.
Las vistas proporcionan un mecanismo para ocultar al cliente si los datos son datos derivados o
datos de base, pero no pueden evitar el hecho de que algunas vistas son costosas de calcular. Para
hacer frente a esto, se inventaron las vistas materializadas, que son vistas que se calculan de
antemano y se almacenan en caché en el disco. Las vistas materializadas son efectivas para los
datos que se leen mucho, pero que pueden soportar ser algo obsoletos.
Aunque las bases de datos NoSQL no tienen vistas, es posible que tengan consultas
precalculadas y almacenadas en caché, y reutilizan el término "vista materializada" para
describirlas. También es un aspecto mucho más central para las bases de datos orientadas a
agregados que para los sistemas relacionales, ya que la mayoría de las aplicaciones tendrán que
lidiar con algunas consultas que no encajan bien con la estructura agregada. (A menudo, las bases
de datos NoSQL crean vistas materializadas utilizando un cálculo de map-reduce, del que
hablaremos en Capítulo 7.)
Hay dos estrategias aproximadas para construir una visión materializada. El primero es el
enfoque diligente en el que se actualiza la vista materializada al mismo tiempo que se actualizan los
datos base para ella. En este caso, agregar un pedido también actualizaría los agregados del
historial de compras para cada producto. Este enfoque es bueno cuando hay más lecturas frecuentes
de la vista materializada que escrituras y se desea que las vistas materializadas sean lo más frescas
posible. La base de datos de la aplicación (p. 7) es valioso aquí, ya que facilita garantizar que
cualquier actualización de los datos base también actualice las vistas materializadas.
Si no desea pagar esa sobrecarga en cada actualización, puede ejecutar trabajos por lotes para
actualizar las vistas materializadas a intervalos regulares. Deberá comprender los requisitos de su
negocio para evaluar qué tan obsoletas pueden ser sus vistas materializadas.
Puede crear vistas materializadas fuera de la base de datos leyendo los datos, calculando la vista y
guardándola de nuevo en la base de datos. Más a menudo, las bases de datos admitirán la creación de
vistas materializadas
ellos mismos. En este caso, se proporciona el cálculo que se debe realizar y la base de datos ejecuta
el cálculo cuando es necesario de acuerdo con algunos parámetros que configure. Esto es
particularmente útil para las actualizaciones ansiosas de vistas con map-reduce incremental
("Incremental Map-Reduce", p. 76 de la Constitución).
Las vistas materializadas se pueden utilizar dentro del mismo agregado. Un documento de pedido
puede incluir un elemento de resumen del pedido que proporciona información de resumen sobre el
pedido para que una consulta de resumen de pedido no tenga que transferir todo el documento de
pedido. El uso de diferentes familias de columnas para vistas materializadas es una característica
común de las bases de datos de familias de columnas. Una ventaja de hacer esto es que le permite
actualizar la vista materializada dentro de la misma operación atómica.
3.5.Modelado para el acceso a datos
Como se mencionó anteriormente, al modelar agregados de datos, debemos considerar cómo se
leerán los datos, así como cuáles son los efectos secundarios sobre los datos relacionados con esos
agregados.
Comencemos con el modelo en el que todos los datos del cliente se incrustan mediante un
almacén de clave-valor (consulte la figura 3.2).
Figura 3.2. Incruste todos los objetos para el cliente y sus pedidos.
En este escenario, la aplicación puede leer la información del cliente y todos los datos
relacionados mediante la clave. Si los requisitos son leer los pedidos o los productos vendidos en
cada pedido, se debe leer todo el objeto y luego analizarlo en el lado del cliente para construir los
resultados. Cuando se necesitan referencias, podríamos cambiar a almacenes de documentos y, a
continuación, consultar dentro de los documentos, o incluso cambiar los datos del almacén de
clave-valor para dividir el objeto de valor en objetos Customer y Order y, a continuación,
mantener las referencias de estos objetos entre sí.
Con las referencias (ver Figura 3.3), ahora podemos encontrar los pedidos independientemente
del Cliente, y con la referencia orderId en el Cliente podemos encontrar todos los Pedidos
para el Cliente. El uso de agregados de esta manera permite la optimización de la lectura, pero
tenemos que empujar el orderId
referencia al Cliente cada vez que se realiza un nuevo Pedido.
Haga clic aquí para ver la imagen del código
# Objeto de pedido
{
"customerId": 1,
"orderId": 99,
"orden":{
"orderDate":"20-nov-2011",
"orderItems":[{"productId":27, "price": 32.45}],
"orderPayment":[{"ccinfo":"1000-1000-1000-1000",
"txnId":"abelif879rft"}],
"shippingAddress":{"city":"Chicago"}
}
}
{
"itemid":27,
"pedidos":{99,545,897,678}
}
{
"itemid":29,
"pedidos":{199,545,704,819
}
}
En los almacenes de documentos, dado que podemos consultar dentro de los documentos, es
posible eliminar las referencias a los pedidos del objeto Cliente. Este cambio nos permite no
actualizar el objeto del Cliente cuando el Cliente realiza nuevos pedidos.
Haga clic aquí para ver la imagen del código
Dado que los almacenes de datos de documentos permiten consultar por atributos dentro del
documento, son posibles búsquedas como "buscar todos los pedidos que incluyen el producto Bases
de datos de refactorización", pero la decisión de crear un agregado de elementos y pedidos a los que
pertenecen no se basa en la capacidad de consulta de la base de datos, sino en la optimización de
lectura deseada por la aplicación.
Al modelar para almacenes de familias de columnas, tenemos la ventaja de que las columnas se
ordenan, lo que nos permite nombrar las columnas que se usan con frecuencia para que se
recuperen primero. Al usar las familias de columnas para modelar los datos, es importante
recordar hacerlo según los requisitos de la consulta y no con el propósito de escribir; La regla
general es facilitar la consulta y la desnormalización de los datos durante la escritura.
Como puede imaginar, hay varias formas de modelar los datos; una forma es almacenar el
Cliente y el Pedido en diferentes familias de columnas (consulte la Figura 3.4). Aquí, es
importante tener en cuenta que la referencia a todos los pedidos realizados por el cliente se
encuentra en la familia de columnas Cliente. Por lo general, se realizan otras
desnormalizaciones similares para mejorar el rendimiento de las consultas (lectura).
Figura 3.4. Vista conceptual de un almacén de datos de columna
Cuando usamos bases de datos de grafos para modelar los mismos datos, modelamos todos los
objetos como nodos y las relaciones dentro de ellos como relaciones; Estas relaciones tienen tipos
y significados direccionales.
Cada nodo tiene relaciones independientes con otros nodos. Estas relaciones tienen nombres
como PURCHASED, PAID_WITH o BELONGS_TO (véase la figura 3.5); estos nombres de
relación permiten recorrer el gráfico. Supongamos que desea encontrar a todos los clientes que
COMPRARON un producto con el nombre Base de datos de refactorización. Todo lo que tenemos
que hacer es consultar el nodo de producto Bases de datos de refactorización y buscar todos
los clientes con la relación COMPRADO entrante.
Figura 3.5. Modelo gráfico de datos de comercio electrónico
Este tipo de recorrido de relaciones es muy fácil con las bases de datos de grafos. Es
especialmente conveniente cuando necesita utilizar los datos para recomendar productos a los
usuarios o para encontrar patrones en las acciones realizadas por los usuarios.
3.6.Puntos clave
•Las bases de datos orientadas a agregados hacen que las relaciones entre agregados sean
más difíciles de manejar que las relaciones dentro de los agregados.
•Las bases de datos de grafos organizan los datos en gráficos de nodo y de borde;
Funcionan mejor para datos que tienen estructuras de relación complejas.
•Las bases de datos sin esquema le permiten agregar campos libremente a los registros, pero
generalmente hay un esquema implícito esperado por los usuarios de los datos.
•Las bases de datos orientadas a agregados a menudo calculan vistas materializadas para
proporcionar datos organizados de manera diferente a sus agregados primarios. Esto se
hace a menudo con cálculos de map-reduce.
Capítulo 4. Modelos de distribución
El principal impulsor del interés en NoSQL ha sido su capacidad para ejecutar bases de datos en un
clúster grande. A medida que aumentan los volúmenes de datos, se vuelve más difícil y costoso
escalar verticalmente: compre un servidor más grande para ejecutar la base de datos. Una opción
más atractiva es escalar horizontalmente, es decir, ejecutar la base de datos en un clúster de
servidores. La orientación del agregado se ajusta bien al escalado horizontal, ya que el agregado es
una unidad natural que se utiliza para la distribución.
En función de su modelo de distribución, puede obtener un almacén de datos que le proporcione
la capacidad de manejar mayores cantidades de datos, la capacidad de procesar un mayor tráfico de
lectura o escritura, o más disponibilidad frente a ralentizaciones o interrupciones de la red. Estos
suelen ser beneficios importantes, pero tienen un costo. La ejecución de un clúster introduce
complejidad, por lo que no es algo que deba hacerse a menos que los beneficios sean convincentes.
En términos generales, hay dos caminos para la distribución de datos: replicación y
fragmentación. La replicación toma los mismos datos y los copia en varios nodos. La
fragmentación coloca diferentes datos en diferentes nodos.
La replicación y el particionamiento son técnicas ortogonales: puede utilizar una de ellas o ambas.
La replicación se presenta en dos formas: maestro-esclavo y peer-to-peer. Ahora discutiremos estas
técnicas comenzando por las más simples y avanzando hasta las más complejas: primero la
replicación de un solo servidor, luego la replicación maestro-esclavo, luego la fragmentación y
finalmente la replicación peer-to-peer.
4.1.Servidor único
La primera opción de distribución, y la más sencilla, es la que recomendaríamos con más
frecuencia: ninguna distribución en absoluto. Ejecute la base de datos en un único equipo que
controle todas las lecturas y escrituras en el almacén de datos. Preferimos esta opción porque
elimina todas las complejidades que introducen las otras opciones; Es fácil de gestionar para el
personal de operaciones y fácil de razonar para los desarrolladores de aplicaciones.
Aunque muchas bases de datos NoSQL están diseñadas en torno a la idea de ejecutarse en un
clúster, puede tener sentido usar NoSQL con un modelo de distribución de un solo servidor si el
modelo de datos del almacén NoSQL es más adecuado para la aplicación. Las bases de datos de
gráficos son la categoría obvia aquí, ya que funcionan mejor en una configuración de un solo
servidor. Si el uso de datos se centra principalmente en el procesamiento de agregados, puede valer
la pena utilizar un documento de un solo servidor o un almacén de clave-valor porque es más fácil
para los desarrolladores de aplicaciones.
Durante el resto de este capítulo, analizaremos las ventajas y complicaciones de los esquemas
de distribución más sofisticados. No dejes que el volumen de las palabras te engañe y pienses que
preferiríamos estas opciones. Si podemos salirnos con la nuestra sin distribuir nuestros datos,
siempre elegiremos un enfoque de un solo servidor.
4.2.Partición
A menudo, un almacén de datos ocupado está ocupado porque diferentes personas acceden a
diferentes partes del conjunto de datos. En estas circunstancias, podemos respaldar la escalabilidad
horizontal colocando diferentes partes de los datos en diferentes servidores, una técnica que se
denomina fragmentación (consulte la figura 4.1).
Figura 4.1. La partición coloca diferentes datos en nodos separados, cada uno de los cuales
realiza sus propias lecturas y escrituras.
En el caso ideal, tenemos diferentes usuarios, todos hablando con diferentes nodos de servidor.
Cada usuario solo tiene que hablar con un servidor, por lo que obtiene respuestas rápidas de ese
servidor. La carga se equilibra muy bien entre los servidores, por ejemplo, si tenemos diez
servidores, cada uno solo tiene que manejar el 10% de la carga.
Por supuesto, el caso ideal es una bestia bastante rara. Para acercarnos a él, tenemos que
asegurarnos de que los datos a los que se accede juntos estén agrupados en el mismo nodo y que
estos grupos estén dispuestos en los nodos para proporcionar el mejor acceso a los datos.
La primera parte de esta pregunta es cómo agrupar los datos para que un usuario obtenga sus
datos principalmente de un solo servidor. Aquí es donde la orientación agregada resulta realmente
útil. El objetivo de los agregados es que los diseñamos para combinar datos a los que comúnmente
se accede juntos, por lo que los agregados saltan como una unidad obvia de distribución.
Cuando se trata de organizar los datos en los nodos, hay varios factores que pueden ayudar a
mejorar el rendimiento. Si sabe que la mayoría de los accesos a determinados agregados se basan
en una ubicación física, puede colocar los datos cerca de donde se está accediendo. Si tiene
pedidos para alguien que vive en Boston, puede colocar esos datos en su centro de datos del este
de EE. UU.
Otro factor es tratar de mantener la carga uniforme. Esto significa que debe intentar organizar los
agregados para que se distribuyan uniformemente entre los nodos, que reciben cantidades iguales de
la carga. Esto puede variar con el tiempo, por ejemplo, si se tiende a acceder a algunos datos en
determinados días de la semana, por lo que es posible que haya reglas específicas del dominio que
desee usar.
En algunos casos, es útil juntar agregados si cree que se pueden leer en secuencia. El periódico de
Bigtable [Chang, etc.] Descrito manteniendo sus filas en orden lexicográfico y ordenando las
direcciones web en función de nombres de dominio invertidos (por ejemplo, [Link]). De
esta manera, se podría acceder a los datos de varias páginas juntas para mejorar la eficiencia del
procesamiento.
Históricamente, la mayoría de las personas han realizado el particionamiento como parte de la
lógica de la aplicación. Podrías poner todo
clientes con apellidos que empiezan de la A a la D en un fragmento y de la E a la G en otro. Esto
complica el modelo de programación, ya que el código de la aplicación debe asegurarse de que las
consultas se distribuyen entre las distintas particiones. Además, reequilibrar el particionamiento
significa cambiar el código de la aplicación y migrar los datos. Muchas bases de datos NoSQL
ofrecen fragmentación automática, en la que la base de datos asume la responsabilidad de
asignar datos a las particiones y garantizar que el acceso a los datos vaya a la partición correcta.
Esto puede hacer que sea mucho más fácil usar el particionamiento en una aplicación.
El particionamiento es especialmente valioso para el rendimiento, ya que puede mejorar el
rendimiento de lectura y escritura. El uso de la replicación, especialmente con el almacenamiento
en caché, puede mejorar en gran medida el rendimiento de lectura, pero hace poco para las
aplicaciones que tienen muchas escrituras. La partición proporciona una forma de escalar
horizontalmente las escrituras.
La fragmentación hace poco para mejorar la resiliencia cuando se usa sola. Aunque los datos
están en diferentes nodos, un error de nodo hace que los datos de esa partición no estén disponibles
con la misma seguridad que para una solución de un solo servidor. El beneficio de resiliencia que
proporciona es que solo los usuarios de los datos de ese fragmento se verán afectados; Sin embargo,
no es bueno tener una base de datos a la que le faltan parte de sus datos. Con un solo servidor es
más fácil pagar el esfuerzo y el costo de mantener ese servidor en funcionamiento; Por lo general,
los clústeres intentan usar máquinas menos confiables y es más probable que se produzca un error
de nodo. Por lo tanto, en la práctica, es probable que la fragmentación por sí sola disminuya la
resistencia.
A pesar de que la fragmentación se hace mucho más fácil con los agregados, todavía no es un
paso que deba tomarse a la ligera. Algunas bases de datos están pensadas desde el principio para
usar el particionamiento, en cuyo caso es aconsejable ejecutarlas en un clúster desde el principio
del desarrollo y, desde luego, en la producción. Otras bases de datos utilizan la partición como un
paso deliberado desde una configuración de un solo servidor, en cuyo caso es mejor iniciar la
partición de un solo servidor y utilizar la partición sólo una vez que las proyecciones de carga
indiquen claramente que se está quedando sin espacio libre.
En cualquier caso, el paso de un solo nodo a la fragmentación va a ser complicado. Hemos
escuchado historias de equipos que se meten en problemas porque dejaron la partición demasiado
tarde, por lo que cuando la activaron en producción, su base de datos prácticamente no estaba
disponible porque el soporte de particionamiento consumió todos los recursos de la base de datos
para mover los datos a nuevas particiones. La lección aquí es usar el particionamiento mucho antes
de que lo necesite, cuando tenga suficiente espacio libre para llevar a cabo el particionamiento.
4.3.Replicación maestro-esclavo
Con la distribución maestro-esclavo, se replican los datos en varios nodos. Un nodo se designa
como maestro o primario. Este maestro es la fuente autorizada de los datos y, por lo general, es
responsable de procesar cualquier actualización de esos datos. Los otros nodos son esclavos o
secundarios. Un proceso de replicación sincroniza los esclavos con el maestro (ver Figura 4.2).
Figura 4.2. Los datos se replican del maestro a los esclavos. Los servicios maestros todas las
escrituras; Las lecturas pueden provenir de maestros o esclavos.
La replicación maestro-esclavo es más útil para el escalado cuando se tiene un conjunto de datos
de lectura intensiva. Puede escalar horizontalmente para manejar más solicitudes de lectura
agregando más nodos esclavos y asegurándose de que todas las solicitudes de lectura se enruten a los
esclavos. Sin embargo, todavía está limitado por la capacidad del maestro para procesar
actualizaciones y su capacidad para transmitir esas actualizaciones. Por lo tanto, no es un buen
esquema para conjuntos de datos con mucho tráfico de escritura, aunque descargar el tráfico de
lectura ayudará un poco a controlar la carga de escritura.
Una segunda ventaja de la replicación maestro-esclavo es la resistencia de lectura: si el maestro
falla, los esclavos aún pueden manejar solicitudes de lectura. De nuevo, esto es útil si la mayor parte
del acceso a los datos es de lectura. El error del maestro elimina la capacidad de manejar escrituras
hasta que se restaure el maestro o se designe un nuevo maestro. Sin embargo, tener esclavos como
réplicas del maestro acelera la recuperación después de un fracaso del maestro, ya que un esclavo
puede ser nombrado un nuevo maestro muy rápidamente.
La capacidad de designar un esclavo para reemplazar a un maestro con errores significa que la
replicación maestro-esclavo es útil incluso si no es necesario escalar horizontalmente. Todo el
tráfico de lectura y escritura puede ir al maestro mientras que el esclavo actúa como una copia de
seguridad en caliente. En este caso, es más fácil pensar en el sistema como una tienda de un solo
servidor con una copia de seguridad en caliente. Obtiene la comodidad de la configuración de un
solo servidor, pero con una mayor resistencia, lo cual es particularmente útil si desea poder manejar
los errores del servidor con gracia.
Los maestros pueden ser nombrados de forma manual o automática. La asignación manual suele
significar que, cuando se configura el clúster, se configura un nodo como maestro. Con la cita
automática, usted
Crea un grupo de nodos y eligen a uno de ellos para que sea el maestro. Además de una
configuración más sencilla, la designación automática significa que el clúster puede designar
automáticamente un nuevo maestro cuando falla un maestro, lo que reduce el tiempo de
inactividad.
Para obtener resistencia de lectura, debe asegurarse de que las rutas de lectura y escritura en la
aplicación sean diferentes, de modo que pueda controlar un error en la ruta de acceso de escritura y
seguir leyendo. Esto incluye cosas como poner las lecturas y escrituras a través de conexiones de
base de datos separadas, una función que a menudo no es compatible con las bibliotecas de
interacción de bases de datos. Al igual que con cualquier característica, no puede estar seguro de
que tiene resistencia de lectura sin buenas pruebas que deshabiliten las escrituras y comprueben que
las lecturas se siguen produciendo.
La replicación viene con algunos beneficios atractivos, pero también viene con un lado oscuro
inevitable: la inconsistencia. Existe el peligro de que diferentes clientes, que leen diferentes
esclavos, vean valores diferentes porque los cambios no se han propagado todos a los esclavos. En
el peor de los casos, eso puede significar que un cliente no puede leer lo que acaba de hacer.
Incluso si utiliza la replicación maestro-esclavo solo para la copia de seguridad en caliente, esto
puede ser un problema, ya que si el maestro falla, se pierden las actualizaciones que no se pasen a
la copia de seguridad.
Hablaremos de cómo lidiar con estos problemas más adelante ("Consistencia", p. 47).
4.4.Replicación punto a punto
La replicación maestro-esclavo ayuda con la escalabilidad de lectura, pero no ayuda con la
escalabilidad de las escrituras. Proporciona resistencia contra el fracaso de un esclavo, pero no de
un amo. Esencialmente, el maestro sigue siendo un cuello de botella y un único punto de fallo. La
replicación peer-to-peer (ver Figura 4.3) ataca estos problemas al no tener un maestro. Todas las
réplicas tienen el mismo peso, todas pueden aceptar escrituras y la pérdida de cualquiera de ellas
no impide el acceso al almacén de datos.
Figura 4.3. La replicación punto a punto hace que todos los nodos apliquen lecturas y
escrituras a todos los datos.
La perspectiva aquí se ve muy bien. Con un clúster de replicación punto a punto, puede pasar
por alto los errores de nodo sin perder el acceso a los datos. Además, puede agregar fácilmente
nodos para mejorar su rendimiento. Hay muchas cosas que me gustan aquí, pero hay
complicaciones.
La mayor complicación es, de nuevo, la consistencia. Cuando puede escribir en dos lugares
diferentes, corre el riesgo de que dos personas intenten actualizar el mismo registro al mismo
tiempo, lo que supone un conflicto de escritura y escritura. Las inconsistencias en la lectura
conducen a problemas, pero al menos son relativamente transitorias.
Las escrituras incoherentes son para siempre.
Hablaremos más sobre cómo lidiar con las inconsistencias de escritura más adelante, pero por
el momento señalaremos un par de opciones amplias. Por un lado, podemos asegurarnos de que
cada vez que escribimos datos, las réplicas se coordinan para asegurarnos de que evitamos un
conflicto. Esto puede darnos una garantía tan fuerte como un maestro, aunque a costa del tráfico
de red para coordinar las escrituras. No necesitamos que todas las réplicas estén de acuerdo en la
escritura, solo una mayoría, por lo que aún podemos sobrevivir a la pérdida de una minoría de los
nodos de réplica.
En el otro extremo, podemos decidir hacer frente a una escritura inconsistente. Hay contextos en
los que podemos idear una política para fusionar escrituras incoherentes. En este caso, podemos
obtener el beneficio de rendimiento completo de escribir en cualquier réplica.
Estos puntos se encuentran en los extremos de un espectro en el que cambiamos la coherencia por
la disponibilidad.
4.5.Combinación de particionamiento y replicación
La replicación y el particionamiento son estrategias que se pueden combinar. Si usamos tanto la
replicación maestro-esclavo como la fragmentación (ver Figura 4.4), esto significa que tenemos
varios maestros, pero cada elemento de datos solo tiene un único maestro. Dependiendo de su
configuración, puede elegir un nodo para que sea maestro para algunos datos y esclavos para otros, o
puede dedicar nodos para tareas de maestro o esclavo.
Uno de los mayores cambios de una base de datos relacional centralizada a una base de datos
NoSQL orientada a clústeres está en cómo se piensa sobre la coherencia. Las bases de datos
relacionales tratan de exhibir una fuerte consistencia evitando todas las diversas inconsistencias
que discutiremos en breve. Una vez que comienzas a mirar el mundo NoSQL, aparecen frases
como "teorema CAP" y "consistencia eventual", y tan pronto como comienzas a construir algo,
tienes que pensar en qué tipo de consistencia necesitas para tu sistema.
La consistencia viene en varias formas, y esa palabra cubre una miríada de formas en que los
errores pueden introducirse en tu vida. Así que vamos a empezar hablando de las distintas formas
que puede adoptar la consistencia. Después de eso, discutiremos por qué es posible que desee relajar
la consistencia (y su hermana mayor, la durabilidad).
5.1.Coherencia de la actualización
Comenzaremos considerando la posibilidad de actualizar un número de teléfono. Casualmente,
Martin y Pramod están mirando el sitio web de la empresa y notan que el número de teléfono está
desactualizado. De manera inverosímil, ambos tienen acceso a la actualización, por lo que ambos
entran al mismo tiempo para actualizar el número. Para hacer el ejemplo interesante, supondremos
que lo actualizan de manera ligeramente diferente, porque cada uno usa un formato ligeramente
diferente. Este problema se denomina conflicto de escritura-escritura: dos personas actualizando el
mismo elemento de datos al mismo tiempo.
Cuando las escrituras lleguen al servidor, el servidor las serializará: decidirá aplicar una y luego
la otra. Supongamos que usa el orden alfabético y elige primero la actualización de Martin y luego la
de Pramod. Sin ningún control de concurrencia, la actualización de Martin sería aplicada e
inmediatamente sobrescrita por Pramod. En este caso, la de Martin es una actualización perdida.
Aquí la actualización perdida no es un gran problema, pero a menudo lo es. Vemos esto como una
falla de coherencia porque la actualización de Pramod se basó en el estado anterior a la actualización
de Martin, pero se aplicó después de ella.
Los enfoques para mantener la coherencia frente a la simultaneidad a menudo se describen como
pesimistas u optimistas. Un enfoque pesimista funciona evitando que ocurran conflictos; un
enfoque optimista permite que ocurran conflictos, pero los detecta y toma medidas para resolverlos.
Para los conflictos de actualización, el enfoque pesimista más común es tener bloqueos de escritura,
de modo que para cambiar un valor es necesario adquirir un bloqueo, y el sistema garantiza que solo
un cliente pueda obtener un bloqueo a la vez.
Por lo tanto, Martin y Pramod intentarían adquirir el bloqueo de escritura, pero solo Martin (el
primero) tendría éxito. Pramod vería entonces el resultado de la escritura de Martin antes de decidir
si hacer su propia actualización.
Un enfoque optimista común es una actualización condicional en la que cualquier cliente que
realice una actualización prueba el valor justo antes de actualizarlo para ver si ha cambiado desde su
última lectura. En este caso, la actualización de Martin tendría éxito, pero la de Pramod fracasaría.
El error le haría saber a Pramod que debería volver a mirar el valor y decidir si intentar una
actualización adicional.
Tanto los enfoques pesimistas como los optimistas que acabamos de describir se basan en una
serialización coherente de las actualizaciones. Con un solo servidor, esto es obvio: tiene que elegir
uno y luego el otro. Pero si hay más de un servidor, como con la replicación punto a punto, dos
nodos podrían aplicar las actualizaciones en un orden diferente, lo que da como resultado un valor
diferente para el número de teléfono de cada par.
A menudo, cuando se habla de simultaneidad en sistemas distribuidos, se habla de coherencia
secuencial, es decir, de garantizar que todos los nodos apliquen operaciones en el mismo orden.
Hay otra forma optimista de controlar un conflicto de escritura-escritura: guardar ambas
actualizaciones y registrar que están en conflicto. Este enfoque es familiar para muchos
programadores de sistemas de control de versiones, particularmente sistemas de control de
versiones distribuidos que, por su naturaleza, a menudo tendrán confirmaciones conflictivas. El
siguiente paso se deriva nuevamente del control de versiones: debe fusionar las dos actualizaciones
de alguna manera. Tal vez muestres ambos valores al usuario y le pidas que lo resuelva, esto es lo
que sucede si actualizas el mismo contacto en tu teléfono y en tu computadora. Alternativamente, el
equipo puede ser capaz de realizar la fusión por sí mismo; Si se trata de un problema de formateo
del teléfono, es posible que pueda darse cuenta de eso y aplicar el nuevo número con el formato
estándar. Cualquier combinación automatizada de conflictos de escritura-escritura es muy específica
del dominio y debe programarse para cada caso particular.
A menudo, cuando las personas se encuentran por primera vez con estos problemas, su reacción
es preferir la simultaneidad pesimista porque están decididos a evitar conflictos. Si bien en algunos
casos esta es la respuesta correcta, siempre hay una compensación. La programación simultánea
implica un equilibrio fundamental entre la seguridad (evitar errores como los conflictos de
actualización) y la ejecución (responder rápidamente a los clientes). Los enfoques pesimistas a
menudo degradan gravemente la capacidad de respuesta de un sistema hasta el punto de que se
vuelve inadecuado para su propósito. Este problema se ve agravado por el peligro de errores: la
simultaneidad pesimista a menudo conduce a interbloqueos, que son difíciles de prevenir y depurar.
La replicación hace que sea mucho más probable que se encuentre con conflictos de
escritura-escritura. Si diferentes nodos tienen diferentes copias de algunos datos que se pueden
actualizar de forma independiente, se producirán conflictos a menos que se tomen medidas
específicas para evitarlos. El uso de un solo nodo como destino para todas las escrituras de algunos
datos hace que sea mucho más fácil mantener la coherencia de las actualizaciones. De los modelos
de distribución que hemos analizado anteriormente, todos los modelos de replicación, excepto la
replicación punto a punto, hacen esto.
5.2.Coherencia de lectura
Tener un almacén de datos que mantenga la coherencia de las actualizaciones es una cosa, pero no
garantiza que los lectores de ese almacén de datos siempre obtengan respuestas coherentes a sus
solicitudes. Imaginemos que tenemos un pedido con líneas de pedido y un cargo de envío. Los
gastos de envío se calculan en función de las líneas de pedido del pedido. Por lo tanto, si añadimos
una partida, también tenemos que recalcular y actualizar los gastos de envío. En una base de datos
relacional, el costo de envío y las partidas estarán en tablas separadas. El peligro de incoherencia es
que Martin agrega una línea de pedido a su pedido, Pramod luego lee las líneas de pedido y los
gastos de envío, y luego Martin actualiza los gastos de envío. Se trata de un conflicto de lectura o
de lectura y escritura inconsistente: en la figura 5.1, Pramod ha realizado una lectura en medio de
la escritura de Martin.
Figura 5.1. Un conflicto de lectura y escritura en coherencia lógica
Nos referimos a este tipo de consistencia como consistencia lógica: garantizar que los
diferentes elementos de datos tengan sentido juntos. Para evitar un conflicto de lectura y escritura
lógicamente incoherente, las bases de datos relacionales admiten la noción de transacciones.
Siempre que Martin envuelva sus dos escrituras en una transacción, el sistema garantiza que
Pramod leerá ambos elementos de datos antes de la actualización o ambos después de la
actualización.
Una afirmación común que escuchamos es que las bases de datos NoSQL no admiten
transacciones y, por lo tanto, no pueden ser consistentes. Tal afirmación es en su mayoría errónea
porque pasa por alto muchos detalles importantes. Nuestra primera aclaración es que cualquier
afirmación sobre la falta de transacciones generalmente solo se aplica a algunas bases de datos
NoSQL, en particular las orientadas a agregados. Por el contrario, las bases de datos de grafos
tienden a admitir transacciones ACID de la misma manera que las bases de datos relacionales.
En segundo lugar, las bases de datos orientadas a agregados admiten actualizaciones atómicas,
pero solo dentro de un único agregado. Esto significa que tendrá coherencia lógica dentro de un
agregado, pero no entre agregados. Por lo tanto, en el ejemplo, podría evitar encontrarse con esa
incoherencia si el pedido, el cargo de entrega y los artículos de línea forman parte de un solo
agregado de pedido.
Por supuesto, no todos los datos se pueden colocar en el mismo agregado, por lo que cualquier
actualización que afecte a varios agregados deja abierto un momento en el que los clientes podrían
realizar una lectura incoherente. La cantidad de tiempo que una incoherencia está presente se
denomina ventana de inconsistencia. Un sistema NoSQL puede tener una ventana de
inconsistencia bastante corta: Como dato, la documentación de Amazon dice que la ventana de
inconsistencia para su servicio SimpleDB suele ser inferior a un segundo.
Este ejemplo de una lectura lógicamente inconsistente es el ejemplo clásico que verá en cualquier
libro que toque la programación de bases de datos. Sin embargo, una vez que se introduce la
replicación, se obtiene un tipo completamente nuevo de inconsistencia. Imaginemos que hay una
última habitación de hotel para un evento deseable. El sistema de reservas de hoteles se ejecuta en
muchos nodos. Martin y Cindy son una pareja que está considerando esta habitación, pero están
discutiendo esto por teléfono porque Martin está en Londres y Cindy está en Boston. Mientras tanto,
Pramod, que está en Mumbai, va y reserva esa última habitación. Eso actualiza la disponibilidad de
habitaciones replicadas, pero la actualización llega a Boston más rápido de lo que llega a Londres.
Cuando Martin y Cindy encienden sus navegadores para ver si la habitación está disponible, Cindy
la ve reservada y Martin la ve gratis. Esta es otra lectura inconsistente, pero es una violación de una
forma diferente de consistencia que llamamos consistencia de replicación: garantizar que el mismo
elemento de datos tenga el mismo valor cuando se lee desde diferentes réplicas (consulte la figura
5.2).
Muchos críticos de las bases de datos NoSQL se centran en la falta de soporte para las
transacciones. Las transacciones son una herramienta útil que ayuda a los programadores a
mantener la coherencia. Una razón por la que muchos defensores de NoSQL se preocupan menos
por la falta de transacciones es que las bases de datos NoSQL orientadas a agregados admiten
actualizaciones atómicas dentro de un agregado, y los agregados están diseñados para que sus datos
formen una unidad natural de actualización. Dicho esto, es cierto que las necesidades
transaccionales son algo a tener en cuenta a la hora de decidir qué base de datos utilizar.
Como parte de esto, es importante recordar que las transacciones tienen limitaciones. Incluso
dentro de un sistema transaccional, todavía tenemos que lidiar con actualizaciones que requieren
intervención humana y, por lo general, no se pueden ejecutar dentro de las transacciones porque
implicarían mantener una transacción abierta durante demasiado tiempo. Podemos hacer frente a
estos usando sellos de versión, que también resultan útiles en otras situaciones, particularmente a
medida que nos alejamos del modelo de distribución de un solo servidor.
6.1.Transacciones comerciales y del sistema
La necesidad de admitir la coherencia de las actualizaciones sin transacciones es en realidad una
característica común de los sistemas, incluso cuando se basan en bases de datos transaccionales.
Cuando los usuarios piensan en transacciones, generalmente se refieren a transacciones
comerciales. Una transacción comercial puede ser algo como navegar por un catálogo de
productos, elegir una botella de Talisker a buen precio, completar la información de la tarjeta de
crédito y confirmar el pedido. Sin embargo, todo esto generalmente no ocurrirá dentro de la
transacción del sistema proporcionada por la base de datos porque esto significaría bloquear los
elementos de la base de datos mientras el usuario está tratando de encontrar su tarjeta de crédito y
sus colegas lo llaman para almorzar.
Por lo general, las aplicaciones solo comienzan una transacción del sistema al final de la
interacción con el usuario, por lo que los bloqueos solo se mantienen durante un corto período de
tiempo. El problema, sin embargo, es que los cálculos y las decisiones pueden haberse tomado en
función de datos que han cambiado. La lista de precios puede haber actualizado el precio del
Talisker, o alguien puede haber actualizado la dirección del cliente, cambiando los gastos de envío.
Las técnicas generales para manejar esto son la simultaneidad fuera de línea [Fowler PoEAA],
útil también en situaciones NoSQL. Un enfoque particularmente útil es el bloqueo optimista sin
conexión [Fowler PoEAA], una forma de actualización condicional en la que una operación de
cliente vuelve a leer cualquier información en la que se basa la transacción comercial y verifica que
no haya cambiado desde que se leyó y se mostró originalmente al usuario. Una buena manera de
hacerlo es asegurarse de que los registros de la base de datos contengan algún tipo de sello de
versión: un campo que cambia cada vez que cambian los datos subyacentes del registro. Cuando
lees los datos, mantienes una nota del sello de versión, de modo que cuando escribes datos puedes
comprobar si la versión ha cambiado.
Es posible que te hayas encontrado con esta técnica con la actualización de recursos con HTTP
[HTTP]. Una forma de hacer esto es usar etags. Cada vez que obtiene un recurso, el servidor
responde con una etiqueta electrónica en el encabezado. Esta etag es una cadena opaca que indica la
versión del recurso. Si, a continuación, actualiza ese recurso, puede usar una actualización
condicional proporcionando la etiqueta electrónica que obtuvo de su último GET. Si el recurso ha
cambiado en el servidor, las etiquetas etag no coincidirán y el servidor rechazará la actualización,
devolviendo una respuesta 412 (error de condición previa).
Algunas bases de datos proporcionan un mecanismo similar de actualización condicional que le
permite asegurarse de que las actualizaciones no se basarán en datos obsoletos. Puede hacer esta
comprobación usted mismo, aunque luego tiene que hacerlo
Asegúrese de que ningún otro subproceso pueda ejecutarse en el recurso entre la lectura y la
actualización. (A veces, esto se denomina operación de comparación y conjunto (CAS), cuyo
nombre proviene de las operaciones CAS realizadas en los procesadores. La diferencia es que un
CAS de procesador compara un valor antes de establecerlo, mientras que una actualización
condicional de base de datos compara una marca de versión del valor).
Hay varias formas de construir los sellos de versión. Puede usar un contador, siempre
incrementándolo al actualizar el recurso. Los contadores son útiles, ya que facilitan saber si una
versión es más reciente que otra. Por otro lado, requieren que el servidor genere el valor del contador
y también necesitan un único maestro para garantizar que los contadores no se dupliquen.
Otro enfoque consiste en crear un GUID, un número aleatorio grande que se garantiza que es
único.
Estos utilizan alguna combinación de fechas, información de hardware y cualquier otra fuente de
aleatoriedad que puedan recoger. Lo bueno de los GUID es que cualquiera puede generarlos y nunca
obtendrá un duplicado; Una desventaja es que son grandes y no se pueden comparar directamente
para determinar la antigüedad.
Un tercer enfoque es hacer un hash del contenido del recurso. Con un tamaño de clave hash lo
suficientemente grande, un hash de contenido puede ser globalmente único como un GUID y
también puede ser generado por cualquier persona; La ventaja es que son deterministas: cualquier
nodo generará el mismo hash de contenido para los mismos datos de recursos.
Sin embargo, al igual que los GUID, no se pueden comparar directamente en cuanto a la antigüedad
y pueden ser largos.
Un cuarto enfoque es usar la marca de tiempo de la última actualización. Al igual que los
contadores, son razonablemente cortos y se pueden comparar directamente por su antigüedad, pero
tienen la ventaja de no necesitar un solo maestro. Varias máquinas pueden generar marcas de tiempo,
pero para funcionar correctamente, sus relojes deben mantenerse sincronizados. Un nodo con un
reloj defectuoso puede causar todo tipo de daños en los datos. También existe el peligro de que, si la
marca de tiempo es demasiado granular, pueda obtener duplicados: no es bueno usar marcas de
tiempo con una precisión de milisegundos si obtiene muchas actualizaciones por milisegundo.
Puede combinar las ventajas de estos esquemas de sellos de diferentes versiones utilizando más
de uno de ellos para crear un sello compuesto. Por ejemplo, CouchDB utiliza una combinación de
contador y hash de contenido. La mayoría de las veces, esto permite comparar las marcas de versión
para determinar si son recientes, incluso cuando se utiliza la replicación punto a punto. En caso de
que dos pares se actualicen al mismo tiempo, la combinación del mismo recuento y diferentes
hashes de contenido facilita la detección del conflicto.
Además de ayudar a evitar conflictos de actualización, las marcas de versión también son útiles
para proporcionar coherencia de sesión (p. 52).
6.2.Sellos de versión en varios nodos
El sello de versión básica funciona bien cuando se tiene una única fuente autorizada de datos,
como un único servidor o una replicación maestro-esclavo. En ese caso, el sello de versión es
controlado por el maestro. Todos los esclavos siguen los sellos del amo. Pero este sistema debe
mejorarse en un modelo de distribución punto a punto porque ya no hay un solo lugar para
establecer los sellos de versión.
Si le pides a dos nodos algunos datos, te encuentras con la posibilidad de que te den respuestas
diferentes. Si esto sucede, su reacción puede variar dependiendo de la causa de esa diferencia. Puede
ser que una actualización solo haya llegado a un nodo pero no al otro, en cuyo caso puede aceptar la
última (suponiendo que pueda decir cuál es). Alternativamente, es posible que se haya encontrado
con una actualización inconsistente, en cuyo caso debe decidir cómo lidiar con eso. En esta
situación, un simple GUID o etag no será suficiente, ya que no le informan lo suficiente sobre las
relaciones.
La forma más simple de sello de versión es un contador. Cada vez que un nodo actualiza los datos,
se incrementa
el contador y coloca el valor del contador en el sello de versión. Si tienes réplicas esclavas azules
y verdes de un solo maestro, y el nodo azul responde con un sello de versión de 4 y el nodo verde
con 6, sabes que la respuesta del verde es más reciente.
En casos de maestros múltiples, necesitamos algo más elegante. Un enfoque, utilizado por los
sistemas de control de versiones distribuidos, es asegurarse de que todos los nodos contengan un
historial de sellos de versión. De esa manera, puedes ver si la respuesta del nodo azul es un
antecesor de la respuesta del verde. Esto requeriría que los clientes conserven los historiales de
sellos de versión, o que los nodos del servidor conserven los historiales de sellos de versión y los
incluyan cuando se les soliciten datos. Esto también detecta una inconsistencia, que veríamos si
obtenemos dos sellos de versión y ninguno de ellos tiene el otro en sus historiales. Aunque los
sistemas de control de versiones mantienen este tipo de historiales, no se encuentran en las bases de
datos NoSQL.
Un enfoque simple pero problemático es usar marcas de tiempo. El principal problema aquí es
que suele ser difícil garantizar que todos los nodos tengan una noción coherente del tiempo,
especialmente si las actualizaciones pueden ocurrir rápidamente. Si el reloj de un nodo no está
sincronizado, puede causar todo tipo de problemas. Además, no puede detectar conflictos de
escritura y escritura con marcas de tiempo, por lo que solo funcionaría bien para el caso maestro
único, y entonces un contador suele ser mejor.
El enfoque más común utilizado por los sistemas NoSQL peer-to-peer es una forma especial de
sello de versión que llamamos sello vectorial. En esencia, un stamp vectorial es un conjunto de
contadores, uno para cada nodo. Un sello vectorial para tres nodos (azul, verde, negro) tendría un
aspecto similar a [azul: 43, verde: 54, negro: 12]. Cada vez que un nodo tiene una
actualización interna, actualiza su propio contador, por lo que una actualización en el nodo verde
cambiaría el vector a [azul: 43, verde: 55, negro: 12].
Cada vez que dos nodos se comunican, sincronizan sus sellos vectoriales. Hay varias variaciones de
cómo se realiza exactamente esta sincronización. Estamos acuñando el término "sello vectorial"
como un término general en este libro; También te encontrarás con relojes vectoriales y vectores de
versiones, que son formas específicas de sellos vectoriales que difieren en la forma en que se
sincronizan.
Al usar este esquema, puede saber si un sello de versión es más reciente que otro porque el sello
más nuevo tendrá todos sus contadores mayores o iguales a los del sello anterior. Por lo tanto,
[azul: 1, verde: 2, negro: 5] es más nuevo que [azul: 1, verde: 1, negro 5] ya que uno
de sus contadores es mayor. Si ambos sellos tienen un contador mayor que el otro, por ejemplo,
[azul: 1, verde: 2, negro: 5] y [azul: 2, verde: 1, negro: 5], entonces tiene un
conflicto de escritura-escritura.
Es posible que falten valores en el vector, en cuyo caso usamos tratar el valor que falta como 0.
Por lo tanto , [azul: 6, negro: 2] se trataría como [azul: 6, verde: 0, negro: 2]. Esto
le permite agregar fácilmente nuevos nodos sin invalidar los sellos vectoriales existentes.
Los sellos vectoriales son una herramienta valiosa que detecta inconsistencias, pero no las
resuelve. Cualquier resolución de conflictos dependerá del dominio en el que esté trabajando. Esto
es parte del equilibrio entre coherencia y latencia. O bien tiene que vivir con el hecho de que las
particiones de red pueden hacer que su sistema no esté disponible, o tiene que detectar y lidiar con
las inconsistencias.
6.3.Puntos clave
•Las marcas de versión ayudan a detectar conflictos de simultaneidad. Cuando lea datos y, a
continuación, los actualice, puede comprobar la marca de versión para asegurarse de que
nadie actualizó los datos entre la lectura y la escritura.
•Las marcas de versión se pueden implementar mediante contadores, GUID, hashes de
contenido, marcas de tiempo o una combinación de estos.
•Con los sistemas distribuidos, un vector de sellos de versión le permite detectar cuando
diferentes nodos
tienen actualizaciones contradictorias.
Capítulo 7. Mapear-Reducir
El auge de las bases de datos orientadas a agregados se debe en gran parte al crecimiento de los
clústeres. La ejecución en un clúster significa que tiene que hacer concesiones en el
almacenamiento de datos de forma diferente a cuando se ejecuta en una sola máquina. Los
clústeres no solo cambian las reglas para el almacenamiento de datos, sino que también cambian
las reglas para el cálculo. Si almacena muchos datos en un clúster, el procesamiento eficiente de
esos datos significa que tiene que pensar de manera diferente sobre cómo organizar su
procesamiento.
Con una base de datos centralizada, generalmente hay dos formas de ejecutar la lógica de
procesamiento en ella: en el propio servidor de base de datos o en un equipo cliente. Ejecutarlo en
un equipo cliente le da más flexibilidad a la hora de elegir un entorno de programación, lo que suele
hacer que los programas sean más fáciles de crear o ampliar. Esto se produce a costa de tener que
extraer una gran cantidad de datos del servidor de base de datos. Si necesita acceder a una gran
cantidad de datos, entonces tiene sentido realizar el procesamiento en el servidor, pagando el precio
en conveniencia de programación y aumentando la carga en el servidor de base de datos.
Cuando se tiene un clúster, hay buenas noticias de inmediato: tiene muchas máquinas para
distribuir el cálculo. Sin embargo, también debe intentar reducir la cantidad de datos que deben
transferirse a través de la red realizando la mayor cantidad de procesamiento posible en el mismo
nodo que los datos que necesita.
El patrón map-reduce (una forma de Scatter-Gather [Hohpe y Woolf]) es una forma de organizar
el procesamiento de tal manera que se aprovechen varias máquinas en un clúster mientras se
mantiene la mayor cantidad de procesamiento y los datos que necesita juntos en la misma máquina.
Ganó prominencia por primera vez con el marco MapReduce de Google [Dean y Ghemawat]. Una
implementación de código abierto ampliamente utilizada es parte del proyecto Hadoop, aunque
varias bases de datos incluyen sus propias implementaciones. Al igual que con la mayoría de los
patrones, existen diferencias en los detalles entre estas implementaciones, por lo que nos
concentraremos en el concepto general. El nombre "map-reduce" revela su inspiración en las
operaciones de mapear y reducir en colecciones en lenguajes de programación funcionales.
7.1.Mapa Básico-Reducir
Para explicar la idea básica, partiremos de un ejemplo que ya hemos azotado hasta la saciedad: el de
los clientes y los pedidos. Supongamos que hemos elegido pedidos como nuestro agregado, y que
cada pedido tiene líneas de pedido. Cada línea de pedido tiene un ID de producto, una cantidad y el
precio cobrado. Este agregado tiene mucho sentido, ya que normalmente la gente quiere ver todo el
orden en un solo acceso. Tenemos muchos pedidos, por lo que hemos fragmentado el conjunto de
datos en muchas máquinas.
Sin embargo, la gente de análisis de ventas quiere ver un producto y sus ingresos totales de los
últimos siete días.
Este informe no se ajusta a la estructura de agregados que tenemos, que es la desventaja de usar
agregados. Para obtener el informe de ingresos del producto, tendrá que visitar todas las máquinas
del clúster y examinar muchos registros de cada máquina.
Este es exactamente el tipo de situación que requiere una reducción de mapas. La primera etapa
de un trabajo de map-reduce es el map. Un mapa es una función cuya entrada es un solo agregado y
cuya salida es un grupo de pares clave-valor. En este caso, la entrada sería un pedido. La salida
serían pares clave-valor correspondientes a las partidas. Cada uno tendría el ID del producto como
clave y un mapa incrustado con la cantidad y el precio como valores (ver Figura 7.1).
Figura 7.1. Una función de mapa lee los registros de la base de datos y emite pares
clave-valor.
Cada aplicación de la función de mapa es independiente de todas las demás. Esto permite que se
puedan paralelizar de forma segura, de modo que un marco de map-reduce pueda crear tareas de
mapeo eficientes en cada nodo y asignar libremente cada pedido a una tarea de mapeo. Esto produce
una gran cantidad de paralelismo y localidad de acceso a los datos.
Para este ejemplo, solo estamos seleccionando un valor del registro, pero no hay ninguna razón
por la que no podamos llevar a cabo alguna función arbitrariamente compleja como parte del
mapa, siempre que solo dependa del valor de los datos de un agregado.
Una operación de mapa solo opera en un único registro; la función de reducción toma varias
salidas de mapa con la misma clave y combina sus valores. Por lo tanto, una función de mapa podría
producir 1000 elementos de línea de pedidos para "Refactorización de base de datos"; La función de
reducción se reduciría a uno, con los totales de la cantidad y los ingresos. Mientras que la función de
mapa se limita a trabajar solo con datos de un solo agregado, la función de reducción puede usar
todos los valores emitidos para una sola clave (consulte la Figura 7.2).
Figura 7.2. Una función reduce toma varios pares clave-valor con la misma clave y los agrega
en uno.
El marco de map-reduce organiza que las tareas de mapeo se ejecuten en los nodos correctos para
procesar todos los documentos y que los datos se muevan a la función de reducción. Para facilitar la
escritura de la función reduce, el marco recopila todos los valores de un solo par y llama a la función
reduce una vez
con la clave y la colección de todos los valores de esa clave. Por lo tanto, para ejecutar un trabajo de
map-reduce, solo necesita escribir estas dos funciones.
7.2.Particionamiento y combinación
En la forma más simple, pensamos en un trabajo de map-reduce como si tuviera una sola función de
reducción. Las salidas de todas las tareas de asignación que se ejecutan en los distintos nodos se
concatenan juntas y se envían a la reducción. Si bien esto funcionará, hay cosas que podemos hacer
para aumentar el paralelismo y reducir la transferencia de datos (consulte la figura 7.3).
Figura 7.4. La combinación reduce los datos antes de enviarlos a través de la red.
No todas las funciones de reducción son combinables. Considere una función que cuenta el
número de clientes únicos para un producto en particular. La función de mapa para una operación de
este tipo tendría que emitir el producto y el cliente. Luego, el reductor puede combinarlos y contar
cuántas veces aparece cada cliente para un producto en particular, emitiendo el producto y el conteo
(ver Figura 7.5). Pero la salida de este reductor es diferente de su entrada, por lo que no se puede
usar como combinador. Todavía puede ejecutar una función de combinación aquí: una que
simplemente elimine los pares de producto y cliente duplicados, pero será diferente del reductor
final.
Figura 7.5. Esta función de reducción, que cuenta cuántos clientes únicos piden un té en
particular, no es combinable.
Cuando tiene reductores de combinación, el marco de map-reduce puede ejecutarse de forma
segura no solo en paralelo (para reducir diferentes particiones), sino también en serie para reducir
la misma partición en diferentes momentos y lugares. Además de permitir que la combinación se
produzca en un nodo antes de la transmisión de datos, también puede comenzar a combinar antes
de que los mapeadores hayan terminado. Esto proporciona una buena cantidad de flexibilidad
adicional al procesamiento de map-reduce. Algunos marcos de map-reduce requieren que todos
los reductores sean reductores combinados, lo que maximiza esta flexibilidad. Si necesita hacer un
reductor no combinado con uno de los
En estos marcos, deberá separar el procesamiento en pasos de map-reduce canalizados.
7.3.Composición de cálculos de map-reduce
El enfoque de map-reduce es una forma de pensar en el procesamiento simultáneo que sacrifica la
flexibilidad en la forma de estructurar el cálculo por un modelo relativamente sencillo para
paralelizar el cálculo en un clúster. Dado que se trata de una compensación, existen restricciones
sobre lo que puede hacer en sus cálculos. Dentro de una tarea de mapa, solo puede operar en un
único agregado. Dentro de una tarea de reducción, solo puede operar con una sola tecla. Esto
significa que tiene que pensar de manera diferente sobre la estructuración de sus programas para
que funcionen bien dentro de estas restricciones.
Una limitación simple es que tiene que estructurar sus cálculos en torno a operaciones que
encajen bien con la noción de una operación de reducción. Un buen ejemplo de esto es el cálculo
de promedios. Consideremos el tipo de pedidos que hemos estado viendo hasta ahora;
Supongamos que queremos saber la cantidad media pedida de cada producto. Una propiedad
importante de los promedios es que no son componibles
Es decir, si tomo dos grupos de órdenes, no puedo combinar sus promedios solos. En su lugar,
necesito tomar la cantidad total y el recuento de pedidos de cada grupo, combinarlos y luego
calcular el promedio a partir de la suma y el recuento combinados (consulte la figura 7.6).
Figura 7.8. Un cálculo desglosado en dos pasos de map-reducción, que se ampliarán en las
próximas tres figuras
Una primera etapa (Figura 7.9) leería los registros de pedidos originales y generaría una serie
de pares clave-valor para las ventas de cada producto por mes.
Figura 7.9. Creación de registros para las ventas mensuales de un producto
Esta etapa es similar a los ejemplos de map-reduce que hemos visto hasta ahora. La única
característica nueva es el uso de una clave compuesta para que podamos reducir los registros en
función de los valores de varios campos.
Los mapeadores de la segunda etapa (Figura 7.10) procesan este resultado dependiendo del año.
Un registro de 2011 rellena la cantidad del año actual, mientras que un registro de 2010 rellena una
cantidad del año anterior. Los registros de años anteriores (como 2009) no dan lugar a que se emita
ninguna salida de asignación.
Figura 7.10. El mapeador de la segunda etapa crea registros base para las comparaciones
interanuales.
La reducción en este caso (Figura 7.11) es una combinación de registros, donde la combinación
de los valores mediante la suma permite que las salidas de dos años diferentes se reduzcan a un
solo valor (con un cálculo basado en los valores reducidos arrojados para una buena medida).
Figura 7.11. El paso de reducción es una combinación de registros incompletos.
La descomposición de este informe en varios pasos de asignación y reducción facilita su
escritura. Al igual que muchos ejemplos de transformación, una vez que se ha encontrado un
marco de transformación que facilita la composición de pasos, suele ser más fácil componer
muchos pasos pequeños juntos que intentar meter montones de lógica en un solo paso.
Otra ventaja es que la salida intermedia también puede ser útil para diferentes salidas, por lo que
puede obtener algo de reutilización. Esta reutilización es importante ya que ahorra tiempo tanto en la
programación como en la ejecución. Los registros intermedios se pueden guardar en el almacén de
datos, formando una vista materializada ("Vistas materializadas", p. 30). Las primeras etapas de las
operaciones de map-reduce son particularmente valiosas para ahorrar, ya quea menudo representan
la mayor cantidad de acceso a los datos, por lo que construirlas una vez como base para muchos usos
posteriores ahorra mucho trabajo. Sin embargo, al igual que con cualquier actividad de reutilización,
es importante crearlos a partir de la experiencia con consultas reales, ya que la reutilización
especulativa rara vez cumple su promesa. Por lo tanto, es importante examinar las formas de varias
consultas a medida que se construyen y factorizar las partes comunes de los cálculos en vistas
materializadas.
Map-reduce es un patrón que se puede implementar en cualquier lenguaje de programación. Sin
embargo, las limitaciones del estilo lo convierten en una buena opción para lenguajes diseñados
específicamente para cálculos de map-reduce. Apache Pig [Pig], una rama del proyecto Hadoop
[Hadoop], es un lenguaje construido específicamente para facilitar la escritura de programas de
map-reducción. Sin duda, hace que sea mucho más fácil trabajar con Hadoop que con las
bibliotecas Java subyacentes. De manera similar, si desea especificar programas de map-reduce
utilizando una sintaxis similar a SQL, existe hive [Hive], otra rama de Hadoop.
Es importante conocer el patrón de map-reduce incluso fuera del contexto de las bases de datos
NoSQL. El sistema original de reducción de mapas de Google funcionaba con archivos almacenados
en un sistema de archivos distribuido
—un enfoque que utiliza el proyecto de código abierto Hadoop. Aunque se necesita algo de
reflexión para acostumbrarse a las limitaciones de la estructuración de cálculos en pasos de
map-reduce, el resultado es un cálculo que es inherentemente adecuado para ejecutarse en un clúster.
Cuando se trata de grandes volúmenes de datos, es necesario adoptar un enfoque orientado a
clústeres. Las bases de datos orientadas a agregados encajan bien con este estilo de cálculo. Creemos
que en los próximos años muchas más organizaciones procesarán los volúmenes de datos que exigen
una solución orientada a clústeres, y el patrón de mapeo-reducción se utilizará cada vez más.
7.3.2.Incremental Map-Reduce
Los ejemplos que hemos discutido hasta ahora son cálculos completos de map-reduce, donde
comenzamos con entradas sin procesar y creamos una salida final. Muchos cálculos de
map-reduce tardan un tiempo en realizarse, incluso con hardware agrupado, y siguen llegando
nuevos datos, lo que significa que tenemos que volver a ejecutar el cálculo para mantener la salida
actualizada. Comenzar desde cero cada vez puede llevar demasiado tiempo, por lo que a menudo
es útil estructurar un cálculo de map-reduce para permitir actualizaciones incrementales, de modo
que solo sea necesario realizar el cálculo mínimo.
Las etapas del mapa de una reducción de mapa son fáciles de manejar de forma incremental:
solo si los datos de entrada cambian, es necesario volver a ejecutar el asignador. Dado que los
mapas están aislados entre sí, las actualizaciones incrementales son sencillas.
El caso más complejo es el paso de reducción, ya que reúne las salidas de muchos mapas y
cualquier cambio en las salidas del mapa podría desencadenar una nueva reducción. Este nuevo
cálculo se puede reducir en función de lo paralelo que sea el paso de reducción. Si vamos a
particionar los datos para la reducción, no es necesario volver a reducir ninguna partición que no
haya cambiado. Del mismo modo, si hay un paso del combinador, no es necesario volver a
ejecutarlo si sus datos de origen no han cambiado.
Si nuestro reductor es combinable, hay más oportunidades para evitar el cálculo. Si los cambios
son aditivos, es decir, si solo estamos agregando nuevos registros pero no estamos cambiando ni
eliminando ningún registro antiguo, entonces podemos ejecutar la reducción con el resultado
existente y las nuevas adiciones. Si hay cambios destructivos, es decir, actualizaciones y
eliminaciones, entonces podemos evitar algún recálculo dividiendo la operación de reducción en
pasos y solo recalculando aquellos pasos cuyas entradas han cambiado, esencialmente, usando una
red de dependencias [DSL de Fowler] para organizar el cálculo.
El marco de map-reduce controla gran parte de esto, por lo que debe comprender cómo un
marco específico admite la operación incremental.
7.4.Lecturas adicionales
Si va a utilizar cálculos de map-reduce, su primer puerto de escala será la documentación de la
base de datos concreta que está utilizando. Cada base de datos tiene su propio enfoque,
vocabulario y peculiaridades, y eso es con lo que deberá familiarizarse. Más allá de eso, existe la
necesidad de capturar más información general sobre cómo estructurar los trabajos de
mapeo-reducción para maximizar la capacidad de mantenimiento y el rendimiento. Todavía no
tenemos ningún libro específico para señalar, pero sospechamos que una buena fuente, aunque
fácilmente se pasa por alto, son los libros sobre Hadoop. Aunque Hadoop no es una base de datos,
es una herramienta que utiliza en gran medida la reducción de mapas, por lo que es probable que
escribir una tarea eficaz de reducción de mapas con Hadoop sea útil en otros contextos (sujeto a
los cambios en detalle entre Hadoop y los sistemas que esté utilizando).
7.5.Puntos clave
•Map-reduce es un patrón que permite que los cálculos se paralelicen en un clúster.
•La tarea de asignación lee los datos de un agregado y los reduce a pares clave-valor
relevantes. Los mapas solo leen un único registro a la vez y, por lo tanto, se pueden
paralelizar y ejecutar en el nodo que almacena el registro.
•Las tareas de reducción toman muchos valores para una sola salida clave de las tareas de
asignación y los resumen en una sola salida. Cada reductor funciona con el resultado de una
sola tecla, por lo que se puede paralelizar por tecla.
•Los reductores que tienen la misma forma de entrada y salida se pueden combinar en
tuberías. Esto mejora el paralelismo y reduce la cantidad de datos que se van a transferir.
•Las operaciones de asignación y reducción se pueden componer en canalizaciones en las
que la salida de una reducción es la entrada al mapa de otra operación.
•Si el resultado de un cálculo de map-reduce se utiliza ampliamente, se puede almacenar
como una vista materializada.
•Las vistas materializadas se pueden actualizar a través de operaciones incrementales
de asignación y reducción que solo calculan los cambios en la vista en lugar de volver
a calcular todo desde cero.
Parte II: Implementación
Capítulo 8. Bases de datos de clave-valor
Un almacén de clave-valor es una tabla hash simple, que se usa principalmente cuando todo el
acceso a la base de datos se realiza a través de la clave principal. Piense en una tabla de un RDBMS
tradicional con dos columnas, como ID y NAME, siendo la columna ID la clave y la columna
NAME que almacena el valor. En un RDBMS, la columna NAME se restringe al almacenamiento de
datos de tipo String. La aplicación puede proporcionar un ID y un VALUE y conservar el par; si el
ID ya existe, el valor actual se sobrescribe, de lo contrario, se crea una nueva entrada. Veamos
cómo se compara la terminología en Oracle y Riak.
Figura 8.2. Cambie el diseño de la clave para segmentar los datos en un solo cubo.
También podríamos crear cubos que almacenen datos específicos. En Riak, se conocen como
cubos de dominio, lo que permite que la serialización y la deserialización sean manejadas por el
controlador del cliente.
Haga clic aquí para ver la imagen del código
Si necesitamos que los datos de cada nodo sean coherentes, podemos aumentar el conjunto
numberOfNodesToRespondToWrite en w para que sea el mismo que nVal. Por supuesto, hacer eso
disminuirá el rendimiento de escritura del clúster. Para mejorar los conflictos de escritura o
lectura, podemos cambiar la marca allowSiblings durante la creación del bucket: si se
establece en false, dejamos que la última escritura gane y no cree hermanos.
8.2.2.Transacciones
Los diferentes productos del tipo de almacén de clave-valor tienen diferentes especificaciones de
transacciones. En términos generales, no hay garantías sobre las escrituras. Muchos almacenes de
datos implementan transacciones de diferentes maneras. Riak usa el concepto de quórum
("Quórums", p. 57) implementado mediante el uso del valor W
—factor de replicación— durante la llamada a la API de escritura.
Supongamos que tenemos un clúster Riak con un factor de replicación de 5 y suministramos el
valor W de 3. Al escribir, la escritura se notifica como correcta solo cuando se escribe y se notifica
como correcta en al menos tres de los nodos. Esto le permite a Riak tener tolerancia de escritura; en
nuestro ejemplo, con N igual a 5
y con un valor W de 3, el clúster puede tolerar que N - W = 2 nodos estén inactivos para las
operaciones de escritura, aunque aún habríamos perdido algunos datos en esos nodos para la
lectura.
8.2.3.Características de consulta
Todos los almacenes de clave-valor pueden consultar por clave, y eso es todo. Si tiene requisitos
para consultar mediante algún atributo de la columna de valor, no es posible usar la base de datos: la
aplicación necesita leer el valor para averiguar si el atributo cumple las condiciones.
La consulta por clave también tiene un efecto secundario interesante. ¿Qué sucede si no
conocemos la clave, especialmente durante la consulta ad-hoc durante la depuración? La mayoría
de los almacenes de datos no le darán una lista de todas las claves principales; Incluso si lo
hicieran, recuperar listas de claves y luego consultar el valor sería muy engorroso. Algunas bases
de datos de clave-valor evitan esto al proporcionar la capacidad de buscar dentro del valor, como
Riak Search, que le permite consultar los datos tal como lo haría con los índices de Lucene.
Al usar almacenes de clave-valor, se debe pensar mucho en el diseño de la clave. ¿Se puede
generar la clave utilizando algún algoritmo? ¿La clave puede ser proporcionada por el usuario (ID de
usuario, correo electrónico, etc.)? ¿O se deriva de marcas de tiempo u otros datos que se pueden
derivar fuera de la base de datos?
Estas características de consulta hacen que los almacenes de clave-valor sean candidatos
probables para almacenar datos de sesión (con el ID de sesión como clave), datos del carro de la
compra, perfiles de usuario, etc. La propiedad expiry_secs se puede utilizar para caducar claves
después de un determinado intervalo de tiempo, especialmente para objetos de sesión/carro de la
compra.
Haga clic aquí para ver la imagen del código
Riak proporciona una interfaz basada en HTTP, de modo que todas las operaciones se pueden
realizar desde el navegador web o en la línea de comandos mediante curl. Guardemos estos datos
en Riak:
Haga clic aquí para ver la imagen del código
{
"lastVisit":1324669989288,
"user":{
"customerId":"91cfdf5bcb7c",
"name":"comprador",
"countryCode":"US",
"tzOffset":0
}
}
Utilice el comando curl para POST los datos, almacenando los datos en el bucket de sesión con
la clave de
a7e618d9db25 (tenemos que proporcionar esta clave):
curl -i [Link]
8.2.5.Escalada
Muchos almacenes de clave-valor escalan mediante el particionamiento ("particionamiento", p. 38).
Con el particionamiento, el valor de la clave determina en qué nodo se almacena la clave.
Supongamos que estamos fragmentando por el primer carácter de la clave; Si la clave es
F4B19D79587D, que comienza con una F, se enviará a un nodo diferente al de la clave
AD9C7A396542. Este tipo de configuración de particionamiento puede aumentar el rendimiento a
medida que se agregan más nodos al clúster.
La fragmentación también presenta algunos problemas. Si el nodo utilizado para almacenar f deja
de funcionar, los datos almacenados en ese nodo dejan de estar disponibles, y no se pueden escribir
nuevos datos con claves que comiencen por f.
Los almacenes de datos como Riak le permiten controlar los aspectos del teorema CAP ("El
teorema CAP", p. 53): N (número de nodos para almacenar las réplicas de clave-valor), R
(número de nodos quedeben tener los datos que se obtienen antes de que la lectura se considere
exitosa) y W (el número de nodos en los que se debe escribir la escritura antes de que se considere
exitosa).
Supongamos que tenemos un clúster Riak de 5 nodos. Establecer N en 3 significa que todos los
datos se replican en al menos tres nodos, establecer R en 2 significa que dos nodos deben
responder a una solicitud GET para que se considere correcta, y establecer W en 2 garantiza
que la solicitud PUT se escriba en dos nodos antes de que la escritura se considere correcta.
Esta configuración nos permite ajustar los errores de los nodos para las operaciones de lectura o
escritura. En función de nuestra necesidad, podemos cambiar estos valores para mejorar la
disponibilidad de lectura o escritura. En términos generales, elija un valor W que coincida con sus
necesidades de consistencia; estos valores se pueden establecer como predeterminados durante la
creación del bucket.
8.3.Casos de uso adecuados
Analicemos algunos de los problemas en los que los almacenes de clave-valor son una buena opción.
8.3.1.Almacenamiento de la información de la sesión
Por lo general, cada sesión web es única y se le asigna un valor sessionid único. Las
aplicaciones que almacenan el sessionid en un disco o en un RDBMS se beneficiarán
enormemente de pasar a un almacén de clave-valor, ya que todo lo relacionado con la sesión se
puede almacenar mediante una sola solicitud PUT o recuperarse mediante GET. Éste
La operación de solicitud única lo hace muy rápido, ya que todo lo relacionado con la sesión se
almacena en un solo objeto. Soluciones como Memcached son utilizadas por muchas aplicaciones
web, y Riak se puede utilizar cuando la disponibilidad es importante.
8.3.2.Perfiles de usuario, preferencias
Casi todos los usuarios tienen un ID de usuario, un nombre de usuario o algún otro atributo
único, así como preferencias como el idioma, el color, la zona horaria, los productos a los que
tiene acceso el usuario, etc. Todo esto se puede poner en un objeto, por lo que obtener las
preferencias de un usuario requiere una sola operación GET. Del mismo modo, se pueden
almacenar perfiles de productos.
8.3.3.Datos de la cesta de la compra
Los sitios web de comercio electrónico tienen carritos de compras vinculados al usuario. Como
queremos que los carritos de compras estén disponibles todo el tiempo, en navegadores, máquinas y
sesiones, toda la información de compra se puede poner en el valor donde la clave es el ID de
usuario. Un clúster Riak sería el más adecuado para este tipo de aplicaciones.
8.4.Cuándo no usarlo
Hay espacios problemáticos en los que los almacenes de clave-valor no son la mejor solución.
8.4.1.Relaciones entre los datos
Si necesita tener relaciones entre diferentes conjuntos de datos o correlacionar los datos entre
diferentes conjuntos de claves, los almacenes de clave-valor no son la mejor solución para usar,
aunque algunos almacenes de clave-valor proporcionan características de recorrido por vínculos.
8.4.2.Transacciones multioperación
Si va a guardar varias claves y no se puede guardar ninguna de ellas, y desea revertir o revertir el
resto de las operaciones, los almacenes de clave-valor no son la mejor solución que se puede usar.
8.4.3.Consulta por datos
Si necesita buscar las claves en función de algo que se encuentra en la parte de valor de los pares
clave-valor, los almacenes de clave-valor no le van a funcionar bien. No hay forma de
inspeccionar el valor en el lado de la base de datos, con la excepción de algunos productos como
Riak Search o motores de indexación como Lucene [Lucene] o Solr [Solr].
8.4.4.Operaciones por conjuntos
Dado que las operaciones se limitan a una tecla a la vez, no hay forma de operar con varias teclas al
mismo tiempo. Si necesita operar con varias teclas, debe manejar esto desde el lado del cliente.
Capítulo 9. Bases de datos de documentos
Los documentos son el concepto principal en las bases de datos de documentos. La base de datos
almacena y recupera documentos, que pueden ser XML, JSON, BSON, etc. Estos documentos son
estructuras de datos de árbol jerárquicas autodescriptivas que pueden constar de mapas, colecciones
y valores escalares. Los documentos almacenados son similares entre sí, pero no tienen por qué ser
exactamente iguales. Las bases de datos de documentos almacenan documentos en la parte de valor
del almacén de clave-valor; Piense en las bases de datos de documentos como almacenes de
clave-valor donde el valor es examinable. Veamos cómo se compara la terminología en Oracle y
MongoDB.
El _id es un campo especial que se encuentra en todos los documentos de Mongo, al igual que
ROWID en Oracle. En MongoDB, _id puede ser asignada por el usuario, siempre que sea única.
{ "firstname": "Martin",
"me gusta": [
"Ciclismo",
"Fotografía" ],
"lastcity": "Boston",
"lastVisited":
}
El documento anterior puede considerarse una fila en un RDBMS tradicional. Veamos otro
documento:
Haga clic aquí para ver la imagen del código
{
"firstname": "Pramod",
"citiesvisited": [ "Chicago", "Londres", "Pune", "Bangalore"
], "direcciones": [
{ "estado": "AK",
"ciudad":
"DILLINGHAM",
"tipo": "R"
},
{ "estado": "MH",
"city": "PUNE",
"tipo": "R" }
],
"lastcity": "Chicago"
}
Al observar los documentos, podemos ver que son similares, pero tienen diferencias en los
nombres de los atributos.
Esto está permitido en las bases de datos de documentos. El esquema de los datos puede diferir de
un documento a otro, pero estos documentos pueden pertenecer a la misma colección, a diferencia
de un RDBMS en el que cada fila de una tabla tiene que seguir el mismo esquema. Representamos
una lista de ciudades visitadas como una matriz, o una lista de direcciones como una lista de
documentos incrustados dentro del documento principal. La incrustación de documentos secundarios
como subobjetos dentro de los documentos proporciona un fácil acceso y un mejor rendimiento.
Si te fijas en los documentos, verás que algunos de los atributos son similares, como el nombre o
la ciudad. Al mismo tiempo, hay atributos en el segundo documento que no existen en el primer
documento, como las direcciones, mientras que los "me gusta" están en el primer documento
pero no en el segundo.
Esta representación diferente de los datos no es la misma que en RDBMS, donde cada columna
tiene que ser definida, y si no tiene datos, se marca como vacía o se establece en null. En los
documentos, no hay atributos vacíos; Si no se encuentra un atributo dado, suponemos que no se
estableció o que no es relevante para el documento. Los documentos permiten crear nuevos
atributos sin necesidad de definirlos o cambiar los documentos existentes.
Algunas de las bases de datos de documentos populares que hemos visto son MongoDB
[MongoDB], CouchDB [CouchDB], Terrastore [Terrastore], OrientDB [OrientDB], RavenDB
[RavenDB] y, por supuesto, el conocido y a menudo denostado Lotus Notes [Notes Storage
Facility] que utiliza el almacenamiento de documentos.
9.2.Funciones
Si bien hay muchas bases de datos de documentos especializadas, usaremos MongoDB como
representante del conjunto de características. Tenga en cuenta que cada producto tiene algunas
características que pueden no encontrarse en otras bases de datos de documentos.
Tomemos un tiempo para entender cómo funciona MongoDB. Cada instancia de MongoDB tiene
varias bases de datos, y cada base de datos puede tener varias colecciones. Cuando comparamos esto
con RDBMS, una instancia de RDBMS es lo mismo que una instancia de MongoDB, los esquemas
de RDBMS son similares a las bases de datos de MongoDB y las tablas de RDBMS son colecciones
de MongoDB. Cuando almacenamos un documento, tenemos que elegir a qué base de datos y
colección pertenece este documento, por ejemplo, [Link](document), que
generalmente se representa como [Link](document).
9.2.1.Consistencia
La coherencia en la base de datos de MongoDB se configura utilizando los conjuntos de réplicas
y eligiendo esperar a que las escrituras se repliquen en todos los esclavos o en un número
determinado de esclavos. Cada escritura puede especificar el número de servidores a los que se
debe propagar la escritura antes de que se devuelva como correcta.
Un comando como [Link]({ getlasterror : 1 , w : "majority" }) le dice a la
base de datos qué tan fuerte es la consistencia que desea. Por ejemplo, si tiene un servidor y
especifica w como mayoría, la escritura se devolverá inmediatamente, ya que solo hay un nodo.
Si tiene tres nodos en el conjunto de réplicas y especifica w como mayoría, la escritura tendrá que
completarse en un mínimo de dos nodos antes de que se notifique como correcta. Puede aumentar
el valor w para una mayor consistencia, pero sufrirá en el rendimiento de escritura, ya que ahora
las escrituras deben completarse en más nodos.
Los conjuntos de réplicas también le permiten aumentar el rendimiento de lectura al permitir la
lectura de los esclavos estableciendo slaveOk; este parámetro se puede establecer en la conexión,
o en la base de datos, o en la colección, o
individualmente para cada operación.
Haga clic aquí para ver la imagen del código
Aquí estamos configurando slaveOk por operación, para que podamos decidir qué operaciones
pueden trabajar con datos del nodo esclavo.
Haga clic aquí para ver la imagen del código
De forma similar a las diversas opciones disponibles para la lectura, puede cambiar la
configuración para lograr una fuerte coherencia de escritura, si lo desea. De forma predeterminada,
una escritura se notifica como correcta una vez que la base de datos la recibe; Puede cambiar esto
para esperar a que las escrituras se sincronicen con el disco o para propagarse a dos o más esclavos.
Esto se conoce como WriteConcern: se asegura de que ciertas escrituras se escriban en el maestro y
en algunos esclavos estableciendo WriteConcern en REPLICAS_SAFE. A continuación se muestra el
código en el que se establece WriteConcern para todas las escrituras de una colección:
Haga clic aquí para ver la imagen del código
Hay una compensación en la que debe pensar cuidadosamente, en función de las necesidades de
su aplicación y los requisitos empresariales, para decidir qué configuración tiene sentido para
slaveOk durante la lectura o qué nivel de seguridad desea durante la escritura con WriteConcern.
9.2.2.Transacciones
Las transacciones, en el sentido tradicional de RDBMS, significan que puede comenzar a modificar
la base de datos con comandos de inserción, actualización o eliminación en diferentes tablas
y luego decidir si desea mantener los cambios o no mediante el uso de commit o rollback. Por lo
general, estas construcciones no están disponibles en las soluciones NoSQL: una escritura se realiza
correctamente o se produce un error. Las transacciones a nivel de documento único se conocen como
transacciones atómicas. Las transacciones que involucran más de una operación no son posibles,
aunque hay productos como RavenDB que admiten transacciones en múltiples operaciones.
De forma predeterminada, todas las escrituras se notifican como correctas. Se puede lograr un
control más preciso sobre la escritura mediante el parámetro WriteConcern. Nos aseguramos de
que el pedido se escriba en más de un nodo antes de que se notifique como correcto mediante
WriteConcern.REPLICAS_SAFE. Los diferentes niveles de WriteConcern le permiten elegir el nivel
de seguridad durante las escrituras; por ejemplo, al escribir entradas de registro, puede usar el nivel
más bajo de seguridad, [Link].
Haga clic aquí para ver la imagen del código
9.2.3.Disponibilidad
El teorema CAP ("El teorema CAP", p. 53) dicta que solo podemos tener dos de Coherencia,
Disponibilidad y Tolerancia de partición. Las bases de datos de documentos intentan mejorar la
disponibilidad mediante la replicación de datos mediante la configuración maestro-esclavo. Los
mismos datos están disponibles en varios nodos y los clientes pueden acceder a los datos incluso
cuando el nodo principal está inactivo. Normalmente, el código de la aplicación no tiene que
determinar si el nodo principal está disponible o no. MongoDB implementa la replicación,
proporcionando alta disponibilidad mediante conjuntos de réplicas.
En un conjunto de réplicas, hay dos o más nodos que participan en una replicación
maestro-esclavo asincrónica. Los nodos del conjunto de réplicas eligen el maestro, o principal,
entre ellos. Suponiendo que todos los nodos tienen los mismos derechos de voto, algunos nodos
pueden ser favorecidos por estar más cerca de los otros servidores, por tener más RAM, etc.; Los
usuarios pueden afectar esto asignando una prioridad (un número entre 0 y 1000) a un nodo.
Todas las solicitudes van al nodo maestro y los datos se replican en los nodos esclavos. Si el nodo
principal deja de funcionar, los nodos restantes del conjunto de réplicas votan entre ellos para elegir
un nuevo nodo principal; Todas las solicitudes futuras se enrutan al nuevo maestro y los nodos
esclavos comienzan a obtener datos del nuevo maestro. Cuando el nodo que falló vuelve a estar en
línea, se une como esclavo y se pone al día con el resto de los nodos extrayendo todos los datos que
necesita para ponerse al día.
La figura 9.1 es un ejemplo de configuración de conjuntos de réplicas. Tenemos dos nodos,
mongo A y mongo B, que ejecutan la base de datos MongoDB en el centro de datos primario, y
mongo C en el centro de datos secundario. Si queremos que los nodos del centro de datos principal
se elijan como nodos principales, podemos asignarles una prioridad más alta que a los demás nodos.
Se pueden agregar más nodos a los conjuntos de réplicas sin tener que desconectarlos.
Figura 9.1. Configuración de conjunto de réplicas con mayor prioridad asignada a los
nodos del mismo centro de datos
La aplicación escribe o lee desde el nodo principal (maestro). Cuando se establece la conexión,
la aplicación solo necesita conectarse a un nodo (principal o no, no importa) en el conjunto de
réplicas, y el resto de los nodos se detectan automáticamente. Cuando el nodo principal deja de
funcionar, el controlador se comunica con el nuevo nodo principal elegido por el conjunto de
réplicas. La aplicación no tiene que gestionar ninguno de los errores de comunicación ni los
criterios de selección de nodos. El uso de conjuntos de réplicas ofrece la capacidad de tener un
almacén de datos de documentos de alta disponibilidad.
Los conjuntos de réplicas se usan generalmente para la redundancia de datos, la conmutación
por error automatizada, el escalado de lectura, el mantenimiento del servidor sin tiempo de
inactividad y la recuperación ante desastres. Se pueden lograr configuraciones de disponibilidad
similares con CouchDB, RavenDB, Terrastore y otros productos.
9.2.4.Características de consulta
Las bases de datos de documentos proporcionan diferentes características de consulta. CouchDB le
permite realizar consultas a través de vistas, consultas complejas en documentos que pueden ser
materializadas ("Vistas Materializadas", p. 30) o dinámicos (piense en ellos como vistas RDBMS
que están materializadas o no). Con CouchDB, si necesita agregar el número de reseñas de un
producto, así como la calificación promedio, puede agregar una vista implementada a través de
map-reduce ("Basic Map-Reduce", p. 68) para devolver el recuento de reseñas y el promedio de sus
calificaciones.
Cuando hay muchas solicitudes, no es conveniente calcular el recuento y el promedio de cada
solicitud; En su lugar, puede agregar una vista materializada que calcule previamente los valores y
almacene los resultados en la base de datos. Estas vistas materializadas se actualizan cuando se
consultan, si se ha modificado algún dato desde la última actualización.
Una de las buenas características de las bases de datos de documentos, en comparación con los
almacenes de clave-valor, es que podemos consultar los datos dentro del documento sin tener que
recuperar todo el documento por su clave y luego hacer una introspección del documento. Esta
característica acerca estas bases de datos al modelo de consulta de RDBMS.
MongoDB tiene un lenguaje de consulta que se expresa a través de JSON y tiene
construcciones como $query para la cláusula where, $orderby para ordenar los datos o
$explain para mostrar el plan de ejecución de la consulta. Hay muchas más construcciones
como estas que se pueden combinar para crear una consulta de MongoDB.
Echemos un vistazo a ciertas consultas que podemos hacer contra MongoDB. Supongamos que
queremos devolver todos los documentos de una colección de pedidos (todas las filas de la tabla
de pedidos). El SQL para esto sería:
SELECT * FROM order
La consulta equivalente en Mongo para obtener todos los pedidos de un solo customerId de
883c2c5b4e5b:
Haga clic aquí para ver la imagen del código
[Link]({"customerId":"883c2c5b4e5b"})
Del mismo modo, la selección de orderId y orderDate para un cliente en SQL sería:
Haga clic aquí para ver la imagen del código
[Link]({customerId:"883c2c5b4e5b"},{orderId:1,orderDate:1})
Del mismo modo, están disponibles las consultas para contar, sumar, etc. Dado que los
documentos son objetos agregados, es muy fácil consultar los documentos que deben coincidir
mediante los campos con objetos secundarios. Supongamos que queremos consultar todos los
pedidos en los que uno de los artículos solicitados tiene un nombre como Refactorización. El SQL
para este requisito sería:
Haga clic aquí para ver la imagen del código
[Link]({"[Link]":/Refactorización/})
La consulta de MongoDB es más sencilla porque los objetos están incrustados dentro de un
único documento y se pueden realizar consultas en función de los documentos secundarios
incrustados.
9.2.5.Escalada
La idea del escalado es agregar nodos o cambiar el almacenamiento de datos sin simplemente migrar
la base de datos a una caja más grande. No estamos hablando de hacer cambios en la aplicación para
manejar más carga; En su lugar, estamos interesados en qué características hay en la base de datos
para que pueda manejar más carga.
El escalado para cargas de lectura pesadas se puede lograr agregando más esclavos de lectura, de
modo que todas las lecturas puedan
se dirija a los esclavos. Dada una aplicación de lectura pesada, con nuestro clúster de conjunto de
réplicas de 3 nodos, podemos agregar más capacidad de lectura al clúster a medida que aumenta la
carga de lectura simplemente agregando más nodos esclavos al conjunto de réplicas para ejecutar
lecturas con el indicador slaveOk (Figura 9.2). Se trata de un escalado horizontal para las
lecturas.
Figura 9.2. Adición de un nuevo nodo, mongo D, a un clúster de conjunto de réplicas existente
Una vez que se inicia el nuevo nodo, mongo D, debe agregarse al conjunto de
réplicas.
[Link]("mongod:27017");
Cuando se agrega un nuevo nodo, se sincronizará con los nodos existentes, se unirá al conjunto
de réplicas como nodo secundario y comenzará a atender solicitudes de lectura. Una ventaja de
esta configuración es que no tenemos que reiniciar ningún otro nodo, y tampoco hay tiempo de
inactividad para la aplicación.
Cuando queremos escalar para la escritura, podemos comenzar a particionar ("Sharding", p. 38)
los datos. La partición es similar a las particiones en RDBMS, donde dividimos los datos por valor
en una columna determinada, como el estado o el año. Con RDBMS, las particiones suelen estar en
el mismo nodo, por lo que la aplicación cliente no tiene que consultar una partición específica, pero
puede seguir consultando la tabla base; el RDBMS se encarga de encontrar la partición correcta
para la consulta y devuelve los datos.
En la fragmentación, los datos también se dividen por un campo determinado, pero luego se
mueven a diferentes nodos de Mongo. Los datos se mueven dinámicamente entre nodos para
garantizar que las particiones estén siempre equilibradas. Podemos agregar más nodos al clúster y
aumentar el número de nodos grabables, lo que permite el escalado horizontal para las escrituras.
Haga clic aquí para ver la imagen del código
La división de los datos en el nombre del cliente garantiza que los datos se equilibren entre los
fragmentos para un rendimiento de escritura óptimo; además, cada fragmento puede ser un conjunto
de réplicas que garantiza un mejor rendimiento de lectura dentro del fragmento (Figura 9.3).
Cuando agregamos un nuevo fragmento a este clúster particionado existente, los datos ahora se
equilibrarán en cuatro particiones en lugar de tres. A medida que se produce todo este movimiento
de datos y refactorización de la infraestructura, la aplicación no experimentará ningún tiempo de
inactividad, aunque es posible que el clúster no funcione de manera óptima cuando se muevan
grandes cantidades de datos para reequilibrar las particiones.
Figura 9.3. Configuración de particiones de MongoDB en la que cada partición es
un conjunto de réplicas
La clave de partición juega un papel importante. Es posible que desee colocar los fragmentos de la
base de datos de MongoDB más cerca de sus usuarios, por lo que el particionamiento basado en la
ubicación del usuario puede ser una buena idea. Al particionar por ubicación del cliente, todos los
datos de usuario de la costa este de EE. UU. se encuentran en las particiones que se sirven desde la
costa este, y todos los datos de usuario de la costa oeste se encuentran en las particiones que se
encuentran en la costa oeste.
9.3.Casos de uso adecuados
9.3.1.Registro de eventos
Las aplicaciones tienen diferentes necesidades de registro de eventos; dentro de la empresa, hay
muchas aplicaciones diferentes que desean registrar eventos. Las bases de datos de documentos
pueden almacenar todos estos diferentes tipos de eventos y pueden actuar como un almacén de
datos central para el almacenamiento de eventos. Esto es especialmente cierto cuando el tipo de
datos que capturan los eventos sigue cambiando. Los eventos se pueden particionar por el nombre
de la aplicación en la que se originó el evento o por el tipo de evento, como order_processed o
customer_logged.
Los almacenes de familias de columnas, como Cassandra [Cassandra], HBase [Hbase], Hypertable
[Hypertable] y Amazon SimpleDB [Amazon SimpleDB], permiten almacenar datos con claves
asignadas a valores y los valores agrupados en varias familias de columnas, cada una de las cuales
es un mapa de datos.
{
nombre: "fullName",
valor: "Martin
Fowler", marca de
tiempo: 12345667890
}
La columna tiene una clave de firstName y el valor de Martin y tiene una marca de tiempo
adjunta. Un
row es una colección de columnas adjuntas o vinculadas a una clave; una colección de filas
similares forma una familia de columnas. Cuando las columnas de una familia de columnas son
columnas simples, la familia de columnas se conoce como familia de columnas estándar.
Haga clic aquí para ver la imagen del código
Familia de columnas
{
fila
"pramod-sadalage" : {
nombre: "Pramod",
apellido: "Sadalage",
últimaVisita:
"2012/12/12"
}
fila
"martin-fowler" : {
nombre: "Martin",
apellido: "Fowler",
ubicación: "Boston"
}
}
Cada familia de columnas se puede comparar con un contenedor de filas en una tabla RDBMS
donde la clave
identifica la fila y la fila consta de varias columnas. La diferencia es que varias filas no tienen que
tener las mismas columnas, y las columnas se pueden agregar a cualquier fila en cualquier
momento sin tener que agregarla a otras filas. Tenemos la fila pramod-sadalage y la fila
martin-fowler con columnas diferentes; ambas filas son parte de la familia de columnas.
Cuando una columna consta de un mapa de columnas, entonces tenemos una supercolumna. Una
supercolumna consta de un nombre y un valor que es un mapa de columnas. Piense en una
supercolumna como un contenedor de columnas.
Haga clic aquí para ver la imagen del código
{
Nombre: "Libro:978-0767905923",
valor: {
autor: "Mitch Albon",
título: "Martes con Morrie",
isbn: "978-0767905923"
}
}
Cuando usamos supercolumnas para crear una familia de columnas, obtenemos una familia de
supercolumnas.
Haga clic aquí para ver la imagen del código
Las familias de supercolumnas son buenas para mantener juntos los datos relacionados, pero
cuando algunas de las columnas no son necesarias la mayor parte del tiempo, Cassandra sigue
recuperando y deserializando las columnas, lo que puede no ser óptimo.
Cassandra coloca las familias de columnas estándar y super en espacios de claves. Un espacio
de claves es similar a una base de datos en RDBMS donde se almacenan todas las familias de
columnas relacionadas con la aplicación. Los espacios de claves deben crearse para que se les
puedan asignar familias de columnas:
Crear comercio electrónico de Keyspace
10.2.1.Consistencia
Cuando Cassandra recibe una escritura, los datos se registran primero en un registro de confirmación
y, a continuación, se escriben en un archivo
Estructura en memoria conocida como Memtable. Una operación de escritura se considera correcta
una vez que se escribe en el registro de confirmación y en la memtable. Las escrituras se agrupan en
la memoria y se escriben periódicamente en estructuras conocidas como SSTable. Los SSTables no
se vuelven a escribir después de vaciarlos; si hay cambios en los datos, se escribe un nuevo
SSTable. Los SSTables no utilizados se recuperan mediante compactación.
Echemos un vistazo a la operación de lectura para ver cómo la configuración de coherencia la
afecta. Si tenemos una configuración de coherencia de ONE como predeterminada para todas las
operaciones de lectura, cuando se realiza una solicitud de lectura, Cassandra devuelve los datos de
la primera réplica, incluso si los datos están obsoletos. Si los datos están obsoletos, las lecturas
posteriores obtendrán los datos más recientes (más recientes); Este proceso se conoce como
reparación de lectura. El nivel de coherencia bajo es bueno cuando no le importa si obtiene
datos obsoletos y/o si tiene requisitos de rendimiento de lectura altos.
Del mismo modo, si está realizando escrituras, Cassandra escribiría en el registro de
confirmación de un nodo y devolvería una respuesta al cliente. La coherencia de ONE es buena si
tiene requisitos de rendimiento de escritura muy altos y tampoco le importa si se pierden algunas
escrituras, lo que puede suceder si el nodo deja de funcionar antes de que la escritura se replique
en otros nodos.
Haga clic aquí para ver la imagen del código
Mientras un nodo está inactivo, los datos que se suponía que debían ser almacenados por ese nodo
se entregan a otros nodos. A medida que el nodo vuelve a estar en línea, los cambios realizados en
los datos se devuelven al nodo. Éste
La técnica se conoce como traspaso insinuado. La entrega insinuada permite una restauración más
rápida de los nodos con errores.
10.2.2.Transacciones
Cassandra no tiene transacciones en el sentido tradicional, en las que podríamos iniciar varias
escrituras y luego decidir si queremos confirmar los cambios o no. En Cassandra, una escritura es
atómica en el nivel de fila, lo que significa que la inserción o actualización de columnas para una
clave de fila determinada se tratará como una sola escritura y se realizará correctamente o no. Las
escrituras se escriben primero en los registros de confirmación y las memtables, y solo se
consideran buenas cuando la escritura en el registro de confirmación y en la memtable se realizó
correctamente. Si un nodo deja de funcionar, el registro de confirmación se utiliza para aplicar
cambios al nodo, al igual que el registro de rehacer en Oracle.
Puede utilizar bibliotecas de transacciones externas, como ZooKeeper [ZooKeeper], para
sincronizar las escrituras y las lecturas. También hay bibliotecas como Cages [Cages] que te
permiten envolver tus transacciones a través de ZooKeeper.
10.2.3.Disponibilidad
Cassandra es, por diseño, de alta disponibilidad, ya que no hay ningún maestro en el clúster y cada
nodo es un par en el clúster. La disponibilidad de un clúster se puede aumentar reduciendo el nivel
de coherencia de las solicitudes. La disponibilidad se rige por la fórmula (R + W) > N
("Quórums", p. 57) donde W es el número mínimo de nodos donde la escritura debe escribirse
correctamente, R es el número mínimo de nodos que deben responder correctamente a una lectura y
N es el número de nodos que participan en la replicación de datos. Puede ajustar la disponibilidad
cambiando los valores R y W por un valor fijo de N.
En un clúster Cassandra de 10 nodos con un factor de replicación para el espacio de claves
establecido en 3 (N = 3), si establecemos R = 2 y W = 2, entonces tenemos (2 + 2) > 3. En este
escenario, cuando un nodo deja de funcionar, la disponibilidad no se ve muy afectada, ya que los
datos se pueden recuperar de los otros dos nodos. Si W = 2 y R = 1, cuando dos nodos están
inactivos, el clúster no está disponible para escritura, pero aún podemos leer.
Del mismo modo, si R = 2 y W = 1, podemos escribir pero el clúster no está disponible para lectura.
Con el R+W
> ecuación N, estás tomando decisiones conscientes sobre las compensaciones de consistencia.
Debe configurar los espacios de claves y las operaciones de lectura/escritura en función de sus
necesidades: mayor disponibilidad para la escritura o mayor disponibilidad para la lectura.
10.2.4.Características de consulta
Al diseñar el modelo de datos en Cassandra, se recomienda optimizar las columnas y las familias
de columnas para la lectura de los datos, ya que no tiene un lenguaje de consulta enriquecido; A
medida que los datos se insertan en las familias de columnas, los datos de cada fila se ordenan por
nombres de columna. Si tenemos una columna que se recupera con mucha más frecuencia que
otras columnas, es mejor usar ese valor para la clave de fila en su lugar.
[Link]. Consultas básicas
Las consultas básicas que se pueden ejecutar con un cliente de Cassandra incluyen GET, SET y
DEL. Antes de comenzar a consultar datos, tenemos que emitir el comando de espacio de claves
use ecommerce;. Esto garantiza que todas nuestras consultas se ejecuten en el espacio de claves
en el que colocamos nuestros datos. Antes de comenzar a usar la familia de columnas en el
espacio de claves, tenemos que definir la familia de columnas.
Haga clic aquí para ver la imagen del código
Tenemos una familia de columnas denominada Customer con columnas de nombre, ciudad y
web, y estamos insertando datos en la familia de columnas con un cliente de Cassandra.
Haga clic aquí para ver la imagen del código
SET Cliente['mfowler']['ciudad']='Boston';
SET Cliente['mfowler']['nombre']='Martin Fowler';
SET Cliente['mfowler']['web']='[Link]';
Usando el cliente Java de Hector [Hector], podemos insertar los mismos datos en la familia de
columnas.
Haga clic aquí para ver la imagen del código
Podemos leer los datos usando el comando GET. Hay varias formas de obtener los datos; Podemos
Obtenga toda la familia de columnas.
GET Cliente['mfowler'];
Incluso podemos obtener solo la columna que nos interesa de la familia de columnas.
OBTENER cliente['mfowler']['web'];
Obtener la columna específica que necesitamos es más eficiente, ya que solo se devuelven los
datos que nos interesan, lo que ahorra mucho movimiento de datos, especialmente cuando la familia
de columnas tiene un gran número de columnas. La actualización de los datos es lo mismo que el
uso del comando SET para la columna que debe establecerse en el nuevo valor. Usando el
comando DEL, podemos eliminar una columna o toda la familia de columnas.
Haga clic aquí para ver la imagen del código
DEL Cliente['mfowler']['ciudad'];
Cliente DEL['mfowler'];
Estos índices se implementan como índices de mapa de bits y funcionan bien para los valores
de columna de baja cardinalidad.
[Link]. Lenguaje de consulta Cassandra (CQL)
Cassandra tiene un lenguaje de consulta que admite comandos similares a SQL, conocido como
Cassandra Query Language (CQL). Podemos usar los comandos CQL para crear una familia de
columnas.
Haga clic aquí para ver la imagen del código
Podemos leer datos usando el comando SELECT. Aquí leemos todas las columnas:
SELECCIONE * DEL cliente
CQL tiene muchas más características para consultar datos, pero no tiene todas las características
que tiene SQL.
CQL no permite uniones ni subconsultas, y sus cláusulas where suelen ser simples.
10.2.5.Escalada
El escalado de un clúster de Cassandra existente es cuestión de agregar más nodos. Como ningún
nodo es maestro, cuando agregamos nodos al clúster estamos mejorando la capacidad del clúster
para admitir más escrituras y lecturas. Este tipo de escalado horizontal le permite tener el máximo
tiempo de actividad, ya que el clúster sigue atendiendo las solicitudes de los clientes mientras se
agregan nuevos nodos al clúster.
10.3.Casos de uso adecuados
Analicemos algunos de los problemas en los que las bases de datos de familias de columnas son una
buena opción.
10.3.1.Registro de eventos
Las bases de datos de familias de columnas, con su capacidad para almacenar cualquier estructura
de datos, son una excelente opción para almacenar información de eventos, como el estado de la
aplicación o los errores encontrados por la aplicación. Dentro de la empresa, todas las aplicaciones
pueden escribir sus eventos en Cassandra con sus propias columnas y la clave de fila del formulario
appname:timestamp. Dado que podemos escalar escrituras, Cassandra funcionaría idealmente para
un sistema de registro de eventos (Figura 10.2).
Una vez que se crea una familia de columnas, puede tener columnas arbitrarias para cada
página visitada dentro de la aplicación web para cada usuario.
Haga clic aquí para ver la imagen del código
10.4.Cuándo no usarlo
Hay problemas para los que las bases de datos de familias de columnas no son las mejores
soluciones, como los sistemas que requieren transacciones ACID para escrituras y lecturas. Si
necesita que la base de datos agregue los datos mediante consultas (como SUM o AVG), debe hacerlo
en el lado del cliente utilizando los datos recuperados por el cliente de todas las filas.
Cassandra no es ideal para los primeros prototipos o los picos tecnológicos iniciales: durante las
primeras etapas, no estamos seguros de cómo pueden cambiar los patrones de consulta y, a medida
que cambian los patrones de consulta, tenemos que cambiar el diseño de la familia de columnas.
Esto provoca fricciones para el equipo de innovación de productos y ralentiza la productividad de
los desarrolladores. Los RDBMS imponen un alto costo en el cambio de esquema, que se compensa
por un bajo costo de cambio de consultas; en Cassandra, el costo puede ser mayor para el cambio de
consulta en comparación con el cambio de esquema.
Capítulo 11. Bases de datos de grafos
Las bases de datos de grafos permiten almacenar entidades y relaciones entre estas entidades. Las
entidades también se conocen como nodos, que tienen propiedades. Piense en un nodo como una
instancia de un objeto en la aplicación. Las relaciones se conocen como aristas que pueden tener
propiedades. Los bordes tienen un significado direccional; Los nodos están organizados por
relaciones que le permiten encontrar patrones interesantes entre los nodos. La organización del
grafo permite que los datos se almacenen una vez y luego se interpreten de diferentes maneras en
función de las relaciones.
11.1. ¿Qué es una base de datos de grafos?
En el gráfico de ejemplo de la figura 11.1, vemos un montón de nodos relacionados entre sí. Los
nodos son entidades que tienen propiedades, como name. El nodo de Martin es en realidad un nodo
que tiene la propiedad de name establecida en Martin.
Hemos asignado a la propiedad name de los dos nodos los valores de Martin y Pramod. Una vez
que tenemos más de un nodo, podemos crear una relación:
Haga clic aquí para ver la imagen del código
[Link](pramod, AMIGO);
[Link](martín, AMIGO);
Tenemos que crear una relación entre los nodos en ambas direcciones, para la dirección de la
La relación importa: Por ejemplo, un nodo de producto puede gustar al usuario, pero el producto
no puede gustar
el usuario. Esta direccionalidad ayuda a diseñar un modelo de dominio enriquecido (Figura 11.2).
Los nodos conocen las relaciones ENTRANTES y SALIENTES que se pueden recorrer en ambos
sentidos.
En el código anterior, iniciamos una transacción en la base de datos, luego creamos un nodo y
establecemos las propiedades
en él. Marcamos la transacción como exitosa y finalmente la completamos hasta el final. Una
transacción debe marcarse como correcta, de lo contrario, Neo4J asume que fue un error y la
revierte cuando se emite finish. Establecer el éxito sin emitir finish tampoco compromete los
datos en la base de datos. Esta forma de gestionar las transacciones hay que recordarla a la hora de
desarrollarla, ya que difiere de la forma estándar de hacer transacciones en un RDBMS.
11.2.3.Disponibilidad
Neo4J, a partir de la versión 1.8, logra una alta disponibilidad al proporcionar esclavos replicados.
Estos esclavos también pueden manejar escrituras: cuando se escriben en, sincronizan la escritura
con el maestro actual, y la escritura se confirma primero en el maestro y luego en el esclavo. Otros
esclavos eventualmente recibirán la actualización. Otras bases de datos de gráficos, como Infinite
Graph y FlockDB, proporcionan almacenamiento distribuido de los nodos.
Neo4J utiliza el Apache ZooKeeper [ZooKeeper] para realizar un seguimiento de los últimos ID
de transacción persistentes en cada nodo esclavo y en el nodo maestro actual. Una vez que se inicia
un servidor, se comunica con ZooKeeper y descubre qué servidor es el maestro. Si el servidor es el
primero en unirse al clúster, se convierte en el maestro; Cuando un maestro deja de funcionar, el
clúster elige un maestro de los nodos disponibles, lo que proporciona una alta disponibilidad.
11.2.4.Características de consulta
Las bases de datos de grafos son compatibles con lenguajes de consulta como Gremlin [Gremlin].
Gremlin es un lenguaje específico de dominio para recorrer grafos; puede recorrer todas las bases
de datos de gráficos que implementan el gráfico de propiedades Blueprints [Blueprints]. Neo4J
también tiene el lenguaje de consulta Cypher [Cypher] para consultar el gráfico. Fuera de estos
lenguajes de consulta, Neo4J le permite consultar el grafo para las propiedades de los nodos,
recorrer el grafo o navegar por las relaciones de los nodos utilizando enlaces de lenguaje.
Las propiedades de un nodo se pueden indexar mediante el servicio de indexación. Del mismo
modo, las propiedades de las relaciones o los bordes se pueden indexar, por lo que el valor puede
encontrar un nodo o un borde. Se deben consultar los índices para encontrar el nodo de inicio para
comenzar un recorrido. Echemos un vistazo a la búsqueda del nodo mediante la indexación de
nodos.
Si tenemos el gráfico que se muestra en la Figura 11.1, podemos indexar los nodos a medida
que se agregan a la base de datos, o podemos indexar todos los nodos más tarde iterando sobre
ellos. Primero tenemos que crear un índice para los nodos utilizando el IndexManager.
Haga clic aquí para ver la imagen del código
Estamos indexando los nodos para la propiedad name. Neo4J utiliza Lucene [Lucene] como
su servicio de indexación. Veremos más adelante que también podemos utilizar la capacidad de
búsqueda de texto completo de Lucene. Cuando se crean nuevos nodos, se pueden agregar al
índice.
Haga clic aquí para ver la imagen del código
Transacción de transacción =
[Link](); try {
Index<Node> nodeIndex =
[Link]().forNodes("nodos"); [Link](martin,
"nombre", [Link]("nombre"));
[Link](pramod, "nombre",
[Link]("nombre")); transacción.éxito();
} finalmente {
transacció[Link]();
}
La adición de nodos al índice se realiza dentro del contexto de una transacción. Una vez
indexados los nodos,
Podemos buscarlos usando la propiedad indexada. Si buscamos el nodo con el nombre de
Barbara, consultaríamos el índice para que la propiedad de name tenga un valor de Barbara.
Haga clic aquí para ver la imagen del código
Obtenemos el nodo cuyo nombre es Martín; dado el nodo, podemos obtener todas sus relaciones.
Haga clic aquí para ver la imagen del código
incomingRelations = [Link](Direcció[Link]);
También podemos aplicar filtros direccionales en las consultas al consultar una relación. Con el
gráfico de la Figura 11.1, si queremos encontrar a todas las personas a las que les gusta NoSQL
Distilled, podemos encontrar el nodo NoSQL Distilled y luego obtener sus relaciones con
[Link]. En este punto también podemos agregar el tipo de relación al filtro de
consulta, ya que estamos buscando solo nodos que LIKE NoSQL Distilled.
Haga clic aquí para ver la imagen del código
Encontrar nodos y sus relaciones inmediatas es fácil, pero esto también se puede lograr en bases
de datos RDBMS. Las bases de datos de grafos son realmente eficaces cuando se desea recorrer
los grafos a cualquier profundidad y especificar un nodo de inicio para el recorrido. Esto es
especialmente útil cuando se intenta encontrar nodos que están relacionados con el nodo inicial en
más de un nivel inferior. A medida que aumenta la profundidad del gráfico, tiene más sentido
recorrer las relaciones mediante un Traverser en el que puede especificar que está buscando
ENTRANTES, SALIENTES o AMBOS tipos de relaciones. También puede hacer que el trazador
transversal vaya de arriba hacia abajo o hacia los lados en el gráfico mediante valores Order de
BREADTH_FIRST o DEPTH_FIRST. El recorrido tiene que comenzar en algún nodo, en este ejemplo,
intentamos encontrar todos los nodos a cualquier profundidad que estén relacionados como un
AMIGO con Barbara:
Haga clic aquí para ver la imagen del código
Traverser friendsTraverser =
[Link](Order.BREADTH_FIRST,
StopEvaluator.END_OF_GRAPH,
ReturnableEvaluator.ALL_BUT_START_NODE,
[Link],
[Link]);
El friendsTraverser nos proporciona una forma de encontrar todos los nodos que están
relacionados con Barbara donde el tipo de relación es FRIEND. Los nodos pueden estar a cualquier
profundidad, amigo de un amigo en cualquier nivel, lo que le permite explorar las estructuras de los
árboles.
Una de las buenas características de las bases de datos de grafos es encontrar rutas entre dos
nodos, determinando si hay varias rutas, encontrando todas las rutas o la ruta más corta. En el
gráfico de la figura 11.1, sabemos que Bárbara está conectada a Jill por dos caminos distintos;
para encontrar todos estos caminos y la distancia entre Bárbara y Jill a lo largo de esos diferentes
caminos, podemos usar
Haga clic aquí para ver la imagen del código
Esta función se utiliza en las redes sociales para mostrar las relaciones entre dos nodos
cualesquiera. Para encontrar todas las rutas y la distancia entre los nodos de cada ruta, primero
obtenemos una lista de rutas distintas entre los dos nodos. La longitud de cada ruta es el número de
saltos en el grafo necesarios para llegar al nodo de destino desde el nodo de inicio. A menudo, es
necesario obtener la ruta más corta entre dos nodos; de los dos caminos de Barbara a Jill, el
camino más corto se puede encontrar usando
Haga clic aquí para ver la imagen del código
Muchos otros algoritmos de grafos se pueden aplicar al grafo en cuestión, como el algoritmo de
Dijkstra
[Dijkstra] para encontrar el camino más corto o más barato entre nodos.
Haga clic aquí para ver la imagen del código
Neo4J también proporciona el lenguaje de consulta Cypher para consultar el gráfico. Cypher
necesita un nodo para iniciar la consulta. El nodo de inicio se puede identificar por su ID de nodo,
una lista de ID de nodo o búsquedas de índice. Cypher utiliza la palabra clave MATCH para hacer
coincidir patrones en las relaciones; la palabra clave WHERE filtra las propiedades de un nodo o
relación. La palabra clave RETURN especifica lo que devuelve la consulta
: nodos, relaciones o campos en los nodos o relaciones.
Cypher también proporciona métodos para ORDER, AGGREGATE, SKIP y LIMIT los datos. En la
Figura 11.2, encontramos todos los nodos conectados a Barbara, ya sea entrante o saliente, mediante
el uso de --.
Haga clic aquí para ver la imagen del código
para las relaciones extrovertidas. La coincidencia también se puede realizar en relaciones específicas
utilizando el método
:RELATIONSHIP_TYPE convención y devolviendo los campos o nodos obligatorios.
Haga clic aquí para ver la imagen del código
Comenzamos con Bárbara, encontramos todas las relaciones salientes con el tipo de AMIGO y
devolvemos los nombres de los amigos. La consulta de tipo de relación solo funciona para la
profundidad de un nivel; Podemos hacer que funcione para profundidades mayores y averiguar la
profundidad de cada uno de los nodos resultantes.
Haga clic aquí para ver la imagen del código
Hay muchas otras características de consulta en el lenguaje Cypher que se pueden usar para
consultar gráficos de base de datos.
11.2.5.Escalada
En las bases de datos NoSQL, una de las técnicas de escalado más utilizadas es la fragmentación, en
la que los datos se dividen y distribuyen en diferentes servidores. Con las bases de datos de grafos,
la fragmentación es difícil, ya que las bases de datos de grafos no están orientadas a agregados, sino
a relaciones. Dado que cualquier nodo dado se puede relacionar con cualquier otro nodo, almacenar
nodos relacionados en el mismo servidor es mejor para el recorrido de grafos. Recorrer un grafo
cuando los nodos están en diferentes máquinas no es bueno para el rendimiento. Conociendo esta
limitación de las bases de datos de grafos, todavía podemos escalarlas utilizando algunas técnicas
comunes descritas por Jim Webber [Webber Neo4J Scaling].
En términos generales, hay tres formas de escalar bases de datos de grafos. Dado que las
máquinas ahora pueden venir con mucha RAM, podemos agregar suficiente RAM al servidor para
que el conjunto de trabajo de nodos y relaciones se mantenga completamente en la memoria. Esta
técnica solo es útil si el conjunto de datos con el que estamos trabajando cabe en una cantidad
realista de RAM.
Podemos mejorar el escalado de lectura de la base de datos agregando más esclavos con acceso
de solo lectura a los datos, con todas las escrituras yendo al maestro. Este patrón de escritura una
vez y lectura de muchos servidores es una técnica probada en clústeres MySQL y es realmente útil
cuando el conjunto de datos es lo suficientemente grande como para no caber en la RAM de una
sola máquina, pero lo suficientemente pequeño como para ser replicado en varias máquinas.
Los esclavos también pueden contribuir a la disponibilidad y al escalado de lectura, ya que pueden
configurarse para que nunca se conviertan en maestros, permaneciendo siempre en solo lectura.
Cuando el tamaño del conjunto de datos hace que la replicación no sea práctica, podemos
fragmentar (consulte la sección "Partición" en p. 38) los datos del lado de la aplicación utilizando
el conocimiento específico del dominio. Por ejemplo, los nodos que se relacionan con América
del Norte se pueden crear en un servidor, mientras que los nodos que se relacionan con Asia en
otro. Esta fragmentación a nivel de aplicación debe comprender que los nodos se almacenan en
bases de datos físicamente diferentes (Figura 11.3).
Figura 11.3. Particionamiento de nodos a nivel de aplicación
11.3. Casos de uso adecuados
Veamos algunos casos de uso adecuados para las bases de datos de grafos.
11.3.1.Datos conectados
Las redes sociales son el lugar donde se pueden desplegar y utilizar las bases de datos de gráficos
de forma muy eficaz. Estos gráficos sociales no tienen por qué ser solo del tipo amigo; Por ejemplo,
pueden representar a los empleados, sus conocimientos y dónde trabajaron con otros empleados en
diferentes proyectos. Cualquier dominio rico en enlaces es adecuado para bases de datos de grafos.
Si tiene relaciones entre entidades de dominio de diferentes dominios (como social, espacial,
comercial) en una sola base de datos, puede hacer que estas relaciones sean más valiosas
proporcionando la capacidad de recorrer los dominios.
11.3.2.Servicios de enrutamiento, despacho y basados en la ubicación
Cada ubicación o dirección que tiene una entrega es un nodo, y todos los nodos donde el repartidor
debe realizar la entrega se pueden modelar como un grafo de nodos. Las relaciones entre nodos
pueden tener la propiedad de distancia, lo que le permite entregar los productos de manera eficiente.
Las propiedades de distancia y ubicación también se pueden usar en gráficos de lugares de interés,
de modo que la aplicación pueda proporcionar recomendaciones de buenos restaurantes u opciones
de entretenimiento cercanas. También puede crear nodos para sus puntos de venta, como librerías o
restaurantes, y notificar a los usuarios cuando estén cerca de alguno de los nodos para proporcionar
servicios basados en la ubicación.
11.3.3.Motores de recomendación
A medida que se crean nodos y relaciones en el sistema, se pueden usar para hacer recomendaciones
como "tus amigos también compraron este producto" o "al facturar este artículo, estos otros artículos
generalmente se facturan". O bien, se puede utilizar para hacer recomendaciones a los viajeros
mencionando que cuando otros
Los visitantes que vienen a Barcelona suelen visitar las creaciones de Antonio Gaudí.
Un efecto secundario interesante del uso de las bases de datos de gráficos para las
recomendaciones es que, a medida que aumenta el tamaño de los datos, el número de nodos y
relaciones disponibles para hacer las recomendaciones aumenta rápidamente. Los mismos datos
también se pueden usar para extraer información, por ejemplo, qué productos siempre se compran
juntos o qué artículos siempre se facturan juntos; Se pueden generar alertas cuando no se cumplen
estas condiciones. Al igual que otros motores de recomendación, las bases de datos de gráficos se
pueden utilizar para buscar patrones en las relaciones para detectar fraudes en las transacciones.
11.4. Cuándo no usarlo
En algunas situaciones, es posible que las bases de datos de gráficos no sean apropiadas. Cuando
desee actualizar todas las entidades o un subconjunto de ellas, por ejemplo, en una solución de
análisis en la que es posible que sea necesario actualizar todas las entidades con una propiedad
modificada, es posible que las bases de datos de gráficos no sean óptimas, ya que cambiar una
propiedad en todos los nodos no es una operación sencilla. Incluso si el modelo de datos funciona
para el dominio del problema, es posible que algunas bases de datos no puedan manejar muchos
datos, especialmente en operaciones de grafos globales (aquellas que involucran todo el grafo).
Capítulo 12. Migraciones de esquemas
12.1.Cambios de esquema
La tendencia reciente a hablar de las bases de datos NoSQL es destacar su naturaleza sin esquema:
es una característica popular que permite a los desarrolladores concentrarse en el diseño del dominio
sin preocuparse por los cambios de esquema. Es especialmente cierto con el auge de los métodos
ágiles [Métodos ágiles] donde es importante responder a los requisitos cambiantes.
Las discusiones, iteraciones y bucles de retroalimentación que involucran a expertos en dominios
y propietarios de productos son importantes para obtener la comprensión correcta de los datos; estas
discusiones no deben verse obstaculizadas por la complejidad del esquema de una base de datos.
Con los almacenes de datos NoSQL, los cambios en el esquema se pueden realizar con la menor
cantidad de fricción, lo que mejora la productividad de los desarrolladores ("The Emergence of
NoSQL", p. 9). Hemos visto que el desarrollo y mantenimiento de una aplicación en el nuevo mundo
de las bases de datos sin esquemas requiere que se preste especial atención a la migración de
esquemas.
12.2.Cambios de esquema en RDBMS
Al desarrollar con tecnologías RDBMS estándar, desarrollamos objetos, sus tablas
correspondientes y sus relaciones. Considere un modelo de objetos y un modelo de datos simples
que tenga Customer, Order y OrderItems. El modelo ER se vería como la Figura 12.1.
El script de cambios muestra los cambios de esquema en la base de datos, así como las
migraciones de datos que se deben realizar. En el ejemplo mostrado, estamos usando DBDeploy
[DBDeploy] como marco para administrar los cambios en la base de datos. DBDeploy mantiene
una tabla en la base de datos, denominada ChangeLog, donde se almacenan todos los cambios
realizados en la base de datos. En esta tabla, Change_Number es lo que indica a todos los usuarios
qué cambios se han aplicado a la base de datos. Esta Change_Number, que es la versión de la base de
datos, se utiliza para buscar el script numerado correspondiente en la carpeta y aplicar los cambios
que aún no se han aplicado. Cuando escribimos un script con el número de cambio 007 y lo
aplicamos a la base de datos usando DBDeploy, DBDeploy verificará el ChangeLog y recogerá todos
los scripts de la carpeta que aún no se hayan aplicado. La Figura 12.4 es la captura de pantalla de
DBDeploy aplicando el cambio a la base de datos.
Figura 12.4. DBDeploy actualizando la base de datos con el número de cambio 007
La mejor manera de integrarse con el resto de desarrolladores es utilizar el repositorio de control
de versiones de tu proyecto para almacenar todos estos scripts de cambios, de modo que puedas
llevar un control de la versión del software y de la base de datos en el mismo lugar, eliminando
posibles discrepancias entre la base de datos y la aplicación. Hay muchas otras herramientas para
este tipo de actualizaciones, como Liquibase [Liquibase], MyBatis Migrator [MyBatis Migrator],
DBMaintain [DBMaintain].
12.2.2.Migraciones en proyectos heredados
No todos los proyectos son un campo verde. ¿Cómo implementar migraciones cuando una
aplicación existente está en producción? Descubrimos que tomar una base de datos existente y
extraer su estructura en scripts, junto con todo el código de la base de datos y cualquier dato de
referencia, funciona como una línea de base para el proyecto. Esta línea base no debe contener
datos transaccionales. Una vez que la línea de base esté lista, se pueden realizar más cambios
utilizando la técnica de migraciones descrita anteriormente (Figura 12.5).
Figura 12.5. Uso de scripts de línea base con una base de datos heredada
Uno de los aspectos principales de las migraciones debe ser mantener la compatibilidad con
versiones anteriores del esquema de la base de datos. En muchas empresas hay múltiples
aplicaciones que utilizan la base de datos; Cuando cambiamos la base de datos de una aplicación,
este cambio no debe interrumpir otras aplicaciones. Podemos lograr la compatibilidad con
versiones anteriores manteniendo una fase de transición para el cambio, como se describe en
detalle en Refactorización de bases de datos [Ambler y Sadalage].
Durante una fase de transición, el esquema antiguo y el nuevo esquema se mantienen en paralelo
y están disponibles para todas las aplicaciones que utilizan la base de datos. Para ello, tenemos que
introducir código de andamiaje, como desencadenadores, vistas y columnas virtuales que garanticen
que otras aplicaciones puedan acceder al esquema de la base de datos y a los datos que necesitan sin
ningún cambio en el código.
Haga clic aquí para ver la imagen del código
{
"_id": "4BD8AE97C47016442AF4A580",
"customerid": 99999,
"name": "Foo Sushi Inc",
"since": "12/12/2012",
"order": {
"orderid": "4821-UXWE-122012","orderdate": "12/12/2001",
"orderItems": [{"product": "Galletas de la fortuna",
"precio": 19.99}]
}
}
Cambiar los objetos para agregar preferredShippingType no requiere ningún cambio en la base
de datos, ya que a la base de datos no le importa que diferentes documentos no sigan el mismo
esquema. Esto permite un desarrollo más rápido y despliegues sencillos. Todo lo que se necesita
implementar es la aplicación, no se necesitan cambios en el lado de la base de datos. El código tiene
que asegurarse de que los documentos que no tienen el atributo preferredShippingType se
puedan seguir analizando, y eso es todo.
Por supuesto, aquí estamos simplificando la situación de cambio de esquema. Echemos un
vistazo al cambio de esquema que hicimos antes: introduciendo discountedPrice y cambiando el
nombre del precio a fullPrice. Para realizar este cambio, cambiamos el nombre del atributo
price a fullPrice y agregamos el atributo discountedPrice. El documento modificado es
Haga clic aquí para ver la imagen del código
{
"_id": "5BD8AE97C47016442AF4A580",
"customerid": 66778,
"name": "India
House", "since":
"12/12/2012",
"order": {
"orderid": "4821-UXWE-222012",
"orderdate": "12/12/2001",
"orderItems": [{"product": "Fundas para sillas",
"fullPrice": 29.99,
"discountedPrice":26.99}]
}
}
Una vez que implementamos este cambio, los nuevos clientes y sus pedidos se pueden guardar y
volver a leer sin
problemas, pero para los pedidos existentes no se puede leer el precio de su producto, porque
ahora el código está buscando fullPrice pero el documento solo tiene precio.
12.3.1.Migración incremental
La falta de coincidencia de esquema hace que muchos nuevos conversos pasen al mundo NoSQL.
Cuando se cambia el esquema en la aplicación, debemos asegurarnos de convertir todos los datos
existentes al nuevo esquema (dependiendo del tamaño de los datos, esta puede ser una operación
costosa). Otra opción sería asegurarse de que los datos, antes de cambiar el esquema, aún puedan ser
analizados por el nuevo código y, cuando se guarden, se guarden de nuevo en el nuevo esquema.
Esta técnica, conocida como migración incremental, migrará los datos a lo largo del tiempo; es
posible que algunos datos nunca se migren, porque nunca se accedió a ellos. Estamos leyendo tanto
el precio como el precio completo del documento:
Haga clic aquí para ver la imagen del código
Al usar la migración incremental, puede haber muchas versiones del objeto en el lado de la
aplicación que pueden traducir el esquema antiguo al nuevo esquema; Al volver a guardar el
objeto, se guarda utilizando el nuevo objeto. Esta migración gradual de los datos ayuda a que la
aplicación evolucione más rápido.
La técnica de migración incremental complicará el diseño del objeto, especialmente porque se
están introduciendo nuevos cambios pero no se están eliminando los antiguos. Este período entre la
implementación del cambio y la migración del último objeto de la base de datos al nuevo esquema
se conoce como período de transición (Figura 12.6). Manténgalo lo más corto posible y enfóquelo al
mínimo alcance posible, esto le ayudará a mantener sus objetos limpios.
Es posible que tengamos que cambiar los datos en los nodos. Se pueden derivar nuevos datos del
nodo existente
datos, o podría importarse de alguna otra fuente. La migración se puede realizar obteniendo todos
los nodos utilizando un índice proporcionado por la fuente de datos y escribiendo datos relevantes en
cada nodo.
12.3.3.Modificación de la estructura de agregados
A veces es necesario cambiar el diseño del esquema, por ejemplo, dividiendo objetos grandes en
otros más pequeños que se almacenan de forma independiente. Supongamos que tiene un
agregado de clientes que contiene todos los pedidos de los clientes y desea separar el cliente y
cada uno de sus pedidos en diferentes unidades de agregado.
A continuación, debe asegurarse de que el código puede funcionar con ambas versiones de los
agregados. Si no encuentra los objetos antiguos, buscará los nuevos agregados.
El código que se ejecuta en segundo plano puede leer un agregado a la vez, realizar el cambio
necesario y guardar los datos en diferentes agregados. La ventaja de operar en un agregado a la
vez es que, de este modo, no se afecta a la disponibilidad de los datos para la aplicación.
12.4.Lecturas adicionales
Para obtener más información sobre las migraciones con bases de datos relacionales, vea [Ambler y
Sadalage]. Aunque gran parte de este contenido es específico del trabajo relacional, los principios
generales de la migración también se aplicarán a otras bases de datos.
12.5.Puntos clave
•Las bases de datos con esquemas seguros, como las bases de datos relacionales, se pueden
migrar guardando cada cambio de esquema, además de su migración de datos, en una
secuencia controlada por versiones.
•Las bases de datos sin esquema aún necesitan una migración cuidadosa debido al esquema
implícito en cualquier código que tenga acceso a los datos.
•Las bases de datos sin esquema pueden usar las mismas técnicas de migración que las bases de
datos con esquemas seguros.
•Las bases de datos sin esquema también pueden leer datos de una manera que sea
tolerante a los cambios en el esquema implícito de los datos y usar la migración
incremental para actualizar los datos.
Capítulo 13. Persistencia políglota
Diferentes bases de datos están diseñadas para resolver diferentes problemas. El uso de un único
motor de base de datos para todos los requisitos suele dar lugar a soluciones no rentables; El
almacenamiento de datos transaccionales, el almacenamiento en caché de la información de la
sesión, el recorrido del gráfico de los clientes y los productos que compraron sus amigos son
problemas esencialmente diferentes. Incluso en el espacio RDBMS, los requisitos de un sistema
OLAP y OLTP son muy diferentes, sin embargo, a menudo se ven forzados al mismo esquema.
Pensemos en las relaciones de datos. Las soluciones RDBMS son buenas para hacer cumplir que
las relaciones existen. Si queremos descubrir relaciones, o tenemos que encontrar datos de diferentes
tablas que pertenecen al mismo objeto, entonces el uso de RDBMS comienza a ser difícil.
Los motores de bases de datos están diseñados para realizar muy bien ciertas operaciones en
ciertas estructuras de datos y cantidades de datos, como operar en conjuntos de datos o un
almacén y recuperar claves y sus valores muy rápido, o almacenar documentos enriquecidos o
gráficos complejos de información.
13.1.Necesidades dispares de almacenamiento de datos
Muchas empresas tienden a utilizar el mismo motor de base de datos para almacenar transacciones
comerciales, datos de administración de sesiones y para otras necesidades de almacenamiento,
como informes, BI, almacenamiento de datos o información de registro (Figura 13.1).
Figura 13.1. Uso de RDBMS para todos los aspectos del almacenamiento de la
aplicación
Los datos de la sesión, el carro de la compra o el pedido no necesitan las mismas propiedades de
disponibilidad, coherencia o requisitos de copia de seguridad. ¿El almacenamiento de gestión de
sesiones necesita la misma estrategia rigurosa de copia de seguridad/recuperación que los datos de
los pedidos de comercio electrónico? ¿El almacenamiento de administración de sesiones necesita
más disponibilidad de una instancia del motor de base de datos para escribir o leer datos de sesión?
En 2006, Neal Ford acuñó el término programación políglota, para expresar la idea de que las
aplicaciones deben escribirse en una mezcla de lenguajes para aprovechar el hecho de que
diferentes lenguajes son adecuados para abordar diferentes problemas. Las aplicaciones
complejas combinan diferentes tipos de problemas, por lo que elegir el lenguaje adecuado para
cada trabajo puede ser más productivo que tratar de encajar todos los aspectos en un
Un solo idioma.
Del mismo modo, cuando se trabaja en un problema comercial de comercio electrónico, es
importante utilizar un almacén de datos para el carrito de la compra que tenga una alta
disponibilidad y pueda escalarse, pero el mismo almacén de datos no puede ayudarlo a encontrar los
productos comprados por los amigos de los clientes, que es una pregunta totalmente diferente.
Utilizamos el término persistencia políglota para definir este enfoque híbrido de la persistencia.
13.2.Uso del almacén de datos políglota
Tomemos nuestro ejemplo de comercio electrónico y utilicemos el enfoque de persistencia
políglota para ver cómo se pueden aplicar algunos de estos almacenes de datos (figura 13.2). Se
podría usar un almacén de datos de clave-valor para almacenar los datos del carro de la compra
antes de que el cliente confirme el pedido y también almacenar los datos de la sesión para que el
RDBMS no se use para estos datos transitorios. Los almacenes de clave-valor tienen sentido aquí,
ya que generalmente se accede al carrito de compras por ID de usuario y, una vez confirmado y
pagado por el cliente, se puede guardar en el RDBMS. Del mismo modo, los datos de sesión se
introducen mediante el ID de sesión.
13.8.Puntos clave
•La persistencia políglota consiste en utilizar diferentes tecnologías de almacenamiento de
datos para gestionar las distintas necesidades de almacenamiento de datos.
•La persistencia políglota puede aplicarse en una empresa o en una sola aplicación.
•La encapsulación del acceso a los datos en los servicios reduce el impacto de las
opciones de almacenamiento de datos en otras partes de un sistema.
•La adición de más tecnologías de almacenamiento de datos aumenta la complejidad en la
programación y las operaciones, por lo que las ventajas de un buen ajuste de almacenamiento
de datos deben sopesarse con esta complejidad.
Capítulo 14. Más allá de NoSQL
La aparición de las bases de datos NoSQL ha hecho mucho para sacudir y abrir el mundo de las
bases de datos, pero creemos que el tipo de bases de datos NoSQL que hemos discutido aquí es
solo una parte de la imagen de la persistencia políglota. Por lo tanto, tiene sentido dedicar algún
tiempo a analizar soluciones que no encajan fácilmente en el bucket de NoSQL.
14.1.Sistemas de archivos
Las bases de datos son muy comunes, pero los sistemas de archivos son casi omnipresentes. En las
últimas dos décadas, se han utilizado ampliamente para documentos de productividad personal, pero
no para aplicaciones empresariales. No anuncian ninguna estructura interna, por lo que se parecen
más a almacenes de clave-valor con una clave jerárquica. También proporcionan poco control sobre
la simultaneidad, aparte del simple bloqueo de archivos, que a su vez es similar a la forma en que
NoSQL solo proporciona bloqueo dentro de un solo agregado.
Los sistemas de archivos tienen la ventaja de ser simples y ampliamente implementados. Se las
arreglan bien con entidades muy grandes, como el video y el audio. A menudo, las bases de datos se
utilizan para indexar activos multimedia almacenados en archivos. Los archivos también funcionan
muy bien para el acceso secuencial, como la transmisión, que puede ser útil para los datos que solo
se anexan.
La atención reciente a los entornos agrupados ha visto un aumento de los sistemas de archivos
distribuidos. Tecnologías como Google File System y Hadoop [Hadoop] proporcionan soporte para
la replicación de archivos. Gran parte de la discusión sobre map-reduce se centra en la manipulación
de archivos grandes en sistemas de clúster, con herramientas para la división automática de archivos
grandes en segmentos que se procesarán en varios nodos. De hecho, una ruta de entrada común a
NoSQL es la de las organizaciones que han estado utilizando Hadoop.
Los sistemas de archivos funcionan mejor para un número relativamente pequeño de archivos
grandes que se pueden procesar en grandes fragmentos, preferiblemente en un estilo de
transmisión. Por lo general, un gran número de archivos pequeños tiene un mal rendimiento: aquí
es donde un almacén de datos se vuelve más eficiente. Los archivos tampoco proporcionan
compatibilidad con consultas sin herramientas de indexación adicionales, como Solr [Solr].
14.2.Abastecimiento de eventos
El origen de eventos es un enfoque de persistencia que se concentra en conservar todos los cambios
en un estado persistente, en lugar de persistir en el estado actual de la aplicación en sí. Es un patrón
arquitectónico que funciona bastante bien con la mayoría de las tecnologías de persistencia,
incluidas las bases de datos relacionales. Lo mencionamos aquí porque también sustenta algunas de
las formas más inusuales de pensar sobre la persistencia.
Consideremos un ejemplo de un sistema que mantiene un registro de la ubicación de los barcos
(Figura 14.1). Tiene un registro de barco simple que mantiene el nombre del barco y su ubicación
actual. En la forma habitual de pensar, cuando escuchamos que el barco King Roy ha llegado a San
Francisco, cambiamos el valor del campo de ubicación del Rey Roy a San Francisco. Más tarde,
escuchamos que se ha ido, así que lo cambiamos a en el mar, cambiándolo nuevamente una vez
que sabemos que ha llegado a Hong Kong.
Figura 14.1. En un sistema típico, la notificación de un cambio provoca una actualización del
estado de la aplicación.
Con un sistema de origen de eventos, el primer paso es construir un objeto de evento que capture
la información sobre el cambio (Figura 14.2). Este objeto de evento se almacena en un registro de
eventos duradero. Finalmente, procesamos el evento para actualizar el estado de la aplicación.
Figura 14.2. Con el abastecimiento de eventos, el sistema almacena cada evento, junto
con el estado de la aplicación derivado.
Como consecuencia, en un sistema de origen de eventos, almacenamos todos los eventos que han
causado un cambio de estado del sistema en el registro de eventos, y el estado de la aplicación se
puede derivar completamente de este registro de eventos. En cualquier momento, podemos descartar
de forma segura el estado de la aplicación y reconstruirla a partir del registro de eventos.
En teoría, los registros de eventos son todo lo que necesita porque siempre puede recrear el
estado de la aplicación cuando lo necesite reproduciendo el registro de eventos. En la práctica, esto
puede ser demasiado lento. Como resultado, suele ser mejor proporcionar la capacidad de
almacenar y recrear el estado de la aplicación en una instantánea. Una instantánea está diseñada
para conservar la imagen de memoria optimizada para una recuperación rápida del estado. Es una
ayuda para la optimización, por lo que nunca debe tener prioridad sobre el registro de eventos para
la autoridad sobre los datos.
La frecuencia con la que tome una instantánea depende de sus necesidades de tiempo de
actividad. No es necesario que la instantánea esté completamente actualizada, ya que puede
reconstruir la memoria cargando la instantánea más reciente y, a continuación, reproduciendo todos
los eventos procesados desde que se tomó esa instantánea. Un enfoque de ejemplo sería tomar una
instantánea todas las noches; En caso de que el sistema se caiga durante el día, volvería a cargar la
instantánea de la noche anterior seguida de los eventos de hoy. Si puedes hacerlo lo suficientemente
rápido, todo estará bien.
Para obtener un registro completo de cada cambio en el estado de la aplicación, debe mantener el
registro de eventos hasta el principio de los tiempos de la aplicación. Sin embargo, en muchos casos
no es necesario un registro de larga duración, ya que puede plegar eventos más antiguos en una
instantánea y solo usar el registro de eventos después de la fecha de la instantánea.
El uso del abastecimiento de eventos tiene una serie de ventajas. Puede transmitir eventos a
varios sistemas, cada uno de los cuales puede crear un estado de aplicación diferente para
diferentes propósitos (Figura 14.3). En el caso de los sistemas de lectura intensiva, puede
proporcionar varios nodos de lectura, con esquemas potencialmente diferentes, mientras concentra
las escrituras en un sistema de procesamiento diferente (un enfoque ampliamente conocido como
CQRS [CQRS]).
Figura 14.3. Los eventos se pueden transmitir a múltiples sistemas de
visualización.
El abastecimiento de eventos también es una plataforma eficaz para analizar información
histórica, ya que puede replicar cualquier estado anterior en el registro de eventos. También puede
investigar fácilmente escenarios alternativos de la siguiente manera:
Introducción de eventos hipotéticos en un procesador de análisis.
El abastecimiento de eventos añade cierta complejidad, en particular, debe asegurarse de que
todos los cambios de estado se capturen y almacenen como eventos. Algunas arquitecturas y
herramientas pueden hacer que eso sea un inconveniente. Cualquier colaboración con sistemas
externos debe tener en cuenta el abastecimiento del evento; Deberá tener cuidado con los efectos
secundarios externos al reproducir eventos para reconstruir el estado de una aplicación.
14.3.Imagen de memoria
Una de las consecuencias del abastecimiento de eventos es que el registro de eventos se convierte en
el registro persistente definitivo
—pero no es necesario que el estado de la aplicación sea persistente. Esto abre la opción de
mantener el estado de la aplicación en memoria utilizando solo estructuras de datos en memoria.
Mantener todos los datos de trabajo en la memoria proporciona una ventaja de rendimiento, ya que
no hay E/S de disco con la que lidiar cuando se procesa un evento. También simplifica la
programación, ya que no es necesario realizar la asignación entre el disco y las estructuras de datos
en memoria.
La limitación obvia aquí es que debe poder almacenar todos los datos a los que necesitará
acceder en la memoria. Esta es una opción cada vez más viable: podemos recordar tamaños de
disco que eran considerablemente menores que los tamaños de memoria actuales. También debe
asegurarse de que puede recuperarse lo suficientemente rápido de un bloqueo del sistema, ya sea
volviendo a cargar eventos del registro de eventos o ejecutando un sistema duplicado y cortando.
Necesitará algún mecanismo explícito para tratar la simultaneidad. Una ruta es un sistema de
memoria transaccional, como el que viene con el lenguaje Clojure. Otra ruta es realizar todo el
procesamiento de entrada en un solo subproceso. Diseñado cuidadosamente, un procesador de
eventos de un solo subproceso puede lograr un rendimiento impresionante a baja latencia [Fowler
lmax].
Romper la separación entre los datos en memoria y los persistentes también afecta a la forma en
que se controlan los errores.
Un enfoque común es actualizar un modelo y revertir los cambios en caso de que se produzca un
error. Con una imagen de memoria, por lo general no tendrá una función de reversión
automatizada; Tienes que escribir el tuyo propio (complicado) o asegurarte de hacer una
validación exhaustiva antes de empezar a aplicar cualquier cambio.
14.4.Control de versiones
Para la mayoría de los desarrolladores de software, su experiencia más común con un sistema de
origen de eventos es un sistema de control de versiones. El control de versiones permite a muchas
personas de un equipo coordinar sus modificaciones de un sistema complejo interconectado, con la
capacidad de explorar estados pasados de ese sistema y realidades alternativas a través de la
ramificación.
Cuando pensamos en el almacenamiento de datos, tendemos a pensar en una visión del mundo de
un solo punto de tiempo, que es muy limitante en comparación con la complejidad que soporta un
sistema de control de versiones. Por lo tanto, es sorprendente que las herramientas de
almacenamiento de datos no hayan tomado prestadas algunas de las ideas de los sistemas de control
de versiones. Después de todo, muchas situaciones requieren consultas históricas y soporte para
múltiples visiones del mundo.
Los sistemas de control de versiones se basan en sistemas de archivos y, por lo tanto, tienen
muchas de las mismas limitaciones para el almacenamiento de datos que un sistema de archivos. No
están diseñados para el almacenamiento de datos de aplicaciones, por lo que son incómodos de usar
en ese contexto. Sin embargo, vale la pena tenerlos en cuenta para escenarios en los que sus
capacidades de escala de tiempo sean útiles.
14.5.Bases de datos XML
Alrededor del cambio de milenio, la gente parecía querer usar XML para todo, y había una
oleada de interés en las bases de datos diseñadas específicamente para almacenar y consultar
documentos XML. Si bien esa ráfaga tuvo tan poco impacto en el dominio relacional como las
fanfarronadas anteriores, las bases de datos XML todavía existen.
Pensamos en las bases de datos XML como bases de datos de documentos en las que los
documentos se almacenan en un modelo de datos compatible con XML, y en las que se utilizan
diversas tecnologías XML para manipular el documento. Puede utilizar varias formas de
definiciones de esquema XML (DTD, XML Schema, RelaxNG) para comprobar formatos de
documentos, ejecutar consultas con XPath y XQuery, y realizar transformaciones con XSLT.
Las bases de datos relacionales adoptaron XML y combinaron estas capacidades XML con las
relacionales, generalmente mediante la incrustación de documentos XML como un tipo de
columna y permitiendo alguna forma de combinar lenguajes de consulta SQL y XML.
Por supuesto, no hay ninguna razón por la que no se pueda usar XML como mecanismo de
estructuración dentro de un almacén de clave-valor. XML está menos de moda en estos días que
JSON, pero es igualmente capaz de almacenar agregados complejos, y las capacidades de esquema y
consulta de XML son mayores de lo que normalmente se puede obtener para JSON. El uso de una
base de datos XML significa que la propia base de datos puede aprovechar la estructura XML y no
solo tratar el valor como un blob, sino que esa ventaja debe sopesarse con las demás características
de la base de datos.
14.6.Bases de datos de objetos
Cuando la programación orientada a objetos comenzó su aumento en popularidad, hubo una oleada
de interés en las bases de datos orientadas a objetos. En este caso, se centró en la complejidad de la
asignación de estructuras de datos en memoria a tablas relacionales. La idea de una base de datos
orientada a objetos es evitar esta complejidad: la base de datos administraría automáticamente el
almacenamiento de estructuras en memoria en el disco. Se podría pensar en él como un sistema de
memoria virtual persistente, que le permite programar con persistencia sin prestar atención a una
base de datos en absoluto.
Las bases de datos de objetos no despegaron. Una de las razones era que el beneficio de la
estrecha integración con la aplicación significaba que no podía acceder fácilmente a los datos más
que con esa aplicación. Un cambio de las bases de datos de integración a las bases de datos de
aplicaciones podría hacer que las bases de datos de objetos sean más viables en el futuro.
Un problema importante con las bases de datos de objetos es cómo lidiar con la migración a
medida que cambian las estructuras de datos. En este caso, la estrecha vinculación entre el
almacenamiento persistente y las estructuras en memoria puede convertirse en un problema. Algunas
bases de datos de objetos incluyen la capacidad de agregar funciones de migración a las definiciones
de objetos.
14.7.Puntos clave
•NoSQL es solo un conjunto de tecnologías de almacenamiento de datos. A medida que
aumentan la comodidad con la persistencia políglota, debemos considerar otras
tecnologías de almacenamiento de datos, lleven o no la etiqueta NoSQL.
Capítulo 15. Elección de la base de datos
En este punto del libro, hemos cubierto muchos de los problemas generales que debes tener en
cuenta para tomar decisiones en el nuevo mundo de la persistencia políglota. Ahora es el momento
de hablar sobre la elección de sus bases de datos para el futuro trabajo de desarrollo. Naturalmente,
no conocemos sus circunstancias particulares, por lo que no podemos darle su respuesta, ni podemos
reducirla a un simple conjunto de reglas a seguir. Además, todavía es temprano en el uso de
producción de sistemas NoSQL, por lo que incluso lo que sabemos es inmaduro: en un par de años
podríamos pensar de manera diferente.
Vemos dos grandes razones para considerar una base de datos NoSQL: la productividad del
programador y el rendimiento del acceso a los datos. En diferentes casos, estas fuerzas pueden
complementarse o contradecirse entre sí. Ambos son difíciles de evaluar al principio de un
proyecto, lo cual es incómodo ya que la elección de un modelo de almacenamiento de datos es
difícil de abstraer para permitirle cambiar de opinión más adelante.
15.1.Productividad del programador
Hable con cualquier desarrollador de una aplicación empresarial y sentirá la frustración de trabajar
con bases de datos relacionales. Por lo general, la información se recopila y se muestra en términos
de agregados, pero tiene que transformarse en relaciones para que persista. Esta tarea es más fácil de
lo que solía ser; Durante la década de 1990, muchos proyectos fracasaron bajo el esfuerzo de
construir capas de mapeo relacional de objetos. En la década de 2000, hemos visto marcos de ORM
populares como Hibernate, iBatis y Rails Active Record que reducen gran parte de esa carga. Pero
esto no ha hecho que el problema desaparezca. Los ORM son una abstracción con fugas, siempre
hay algunos casos que necesitan más atención, especialmente para obtener un rendimiento decente.
En esta situación, las bases de datos orientadas a la agregación pueden ofrecer un trato
tentador. Podemos eliminar el ORM y conservar los agregados de forma natural a medida que los
usamos. Nos hemos encontrado con varios proyectos que afirman tener beneficios palpables al
pasar a una solución orientada a agregados.
Las bases de datos de grafos ofrecen una simplificación diferente. Las bases de datos
relacionales no hacen un buen trabajo con datos que tienen muchas relaciones. Una base de datos
de grafos ofrece una API de almacenamiento más natural para este tipo de datos y capacidades
de consulta diseñadas en torno a este tipo de estructuras.
Todos los tipos de sistemas NoSQL se adaptan mejor a datos no uniformes. Si tiene
dificultades con un esquema sólido para admitir campos ad-hoc, las bases de datos NoSQL sin
esquema pueden ofrecer un alivio considerable.
Estas son las principales razones por las que el modelo de programación de bases de datos NoSQL
puede mejorar la productividad de su equipo de desarrollo. El primer paso para evaluar esto para sus
circunstancias es observar lo que su software tendrá que hacer. Revise las funciones actuales y vea si
el uso de datos se ajusta y cómo. A medida que lo hace, puede comenzar a ver que un modelo de
datos en particular parece ser una buena opción. Esa proximidad de ajuste sugiere que el uso de ese
modelo conducirá a una programación más fácil.
Al hacerlo, recuerde que la persistencia políglota consiste en utilizar varias soluciones de
almacenamiento de datos. Es posible que veas que diferentes modelos de almacenamiento de datos
se ajustan a diferentes partes de tus datos. Esto sugeriría el uso de diferentes bases de datos para
diferentes aspectos de sus datos. El uso de varias bases de datos es inherentemente más complejo
que el uso de una sola tienda, pero las ventajas de un buen ajuste en cada caso pueden ser mejores
en general.
Al observar el ajuste del modelo de datos, preste especial atención a los casos en los que haya un
problema. Tú
Es posible que la mayoría de sus características funcionen bien con un agregado, pero algunas no
lo harán. Tener algunas características que no se ajustan bien al modelo no es una razón para
evitar el modelo (es posible que las dificultades del mal ajuste no superen las ventajas del buen
ajuste), pero es útil detectar y resaltar estos casos de mal ajuste.
Revisar sus características y evaluar sus necesidades de datos debería llevarlo a una o más
alternativas sobre cómo manejar sus necesidades de base de datos. Esto te dará un punto de partida,
pero el siguiente paso es probar las cosas construyendo software. Toma algunas características
iniciales y constrúyelas, mientras prestas mucha atención a lo sencillo que es usar la tecnología que
estás considerando. En esta situación, puede valer la pena crear las mismas características con un
par de bases de datos diferentes para ver cuál funciona mejor. La gente suele ser reacia a hacer esto:
a nadie le gusta crear software que se descarte. Sin embargo, esta es una forma esencial de juzgar
qué tan efectivo es un marco en particular.
Lamentablemente, no hay forma de medir adecuadamente la productividad de los diferentes
diseños. No tenemos forma de medir adecuadamente la producción. Incluso si creas exactamente la
misma característica, no puedes comparar realmente la productividad porque el conocimiento de
construirla una vez hace que sea más fácil una segunda vez, y no puedes crearlas simultáneamente
con equipos idénticos. Lo que puedes hacer es asegurarte de que las personas que hicieron el trabajo
puedan dar una opinión. La mayoría de los desarrolladores pueden sentir cuándo son más
productivos en un entorno que en otro. Aunque se trata de un juicio subjetivo, y es muy posible que
haya desacuerdos entre los miembros del equipo, este es el mejor juicio que obtendrá. Al final,
creemos que el equipo que hace el trabajo debe decidir.
Al probar una base de datos para juzgar la productividad, es importante probar también algunos
de los casos de mala adaptación que mencionamos anteriormente. De esa manera, el equipo puede
tener una sensación tanto del camino feliz como del difícil, para obtener una impresión general.
Este enfoque tiene sus defectos. A menudo, no se puede obtener una apreciación completa de una
tecnología sin pasar muchos meses usándola, y realizar una evaluación durante tanto tiempo rara vez
es rentable. Pero como muchas cosas en la vida, tenemos que hacer la mejor evaluación que
podamos, conociendo sus defectos, y seguir con eso. Lo esencial aquí es basar la decisión en la
mayor cantidad de programación real que puedas. Incluso una mera semana trabajando con una
tecnología puede decirte cosas que nunca aprenderías de cien presentaciones de proveedores.
15.2.Rendimiento del acceso a los datos
La preocupación que llevó al crecimiento de las bases de datos NoSQL fue el acceso rápido a una
gran cantidad de datos. A medida que surgían grandes sitios web, querían crecer horizontalmente y
ejecutarse en grandes clústeres. Desarrollaron las primeras bases de datos NoSQL para ayudarlas a
ejecutarse de manera eficiente en dichas arquitecturas. A medida que otros usuarios de datos siguen
su ejemplo, una vez más, la atención se centra en acceder a los datos rápidamente, a menudo con
grandes volúmenes involucrados.
Hay muchos factores que pueden determinar el mejor rendimiento de una base de datos que el
valor predeterminado relacional en diversas circunstancias. Una base de datos orientada a
agregados puede ser muy rápida para leer o recuperar agregados en comparación con una base de
datos relacional donde los datos se distribuyen en muchas tablas. La fragmentación y la
replicación más sencillas en clústeres permiten el escalado horizontal. Una base de datos de grafos
puede recuperar datos altamente conectados más rápidamente que el uso de combinaciones
relacionales.
Si está investigando bases de datos NoSQL en función del rendimiento, lo más importante que
debe hacer es probar su rendimiento en los escenarios que le interesan. El razonamiento sobre el
rendimiento de una base de datos puede ayudarle a crear una lista corta, pero la única forma de
evaluar el rendimiento correctamente es crear algo, ejecutarlo y medirlo.
A la hora de crear una evaluación de rendimiento, lo más difícil suele ser obtener un conjunto
realista de pruebas de rendimiento. No puede compilar su sistema real, por lo que debe crear un
subconjunto representativo. Sin embargo, es importante que este subconjunto sea un representante lo
más fiel posible. No es bueno tomar una base de datos que está destinada a servir a cientos de
usuarios simultáneos y evaluar su rendimiento con un solo usuario. Va a tener que crear cargas y
volúmenes de datos representativos.
Especialmente si está creando un sitio web público, puede ser difícil crear un banco de pruebas de
alta carga.
Aquí, se puede hacer un buen argumento para usar los recursos de computación en la nube tanto para
generar carga como para construir un clúster de prueba. La naturaleza elástica del aprovisionamiento
en la nube es muy útil para el trabajo de evaluación del rendimiento de corta duración.
No podrá probar todas las formas en que se usará su aplicación, por lo que debe crear un
subconjunto representativo. Elija escenarios que sean los más comunes, los que más dependan del
rendimiento y los que no parezcan encajar bien con su modelo de base de datos. Este último puede
alertarle sobre cualquier riesgo fuera de sus casos de uso principales.
Encontrar volúmenes para probar puede ser complicado, especialmente al principio de un
proyecto cuando no está claro cuáles serán sus volúmenes de producción. Tendrás que encontrar
algo en lo que basar tu pensamiento, así que asegúrate de hacerlo explícito y comunicarlo a todas
las partes interesadas. Hacerlo explícito reduce la posibilidad de que diferentes personas tengan
ideas diferentes sobre lo que es una "carga de lectura pesada". También le permite detectar
problemas más fácilmente en caso de que sus descubrimientos posteriores se desvíen de sus
suposiciones originales. Sin hacer explícitas tus suposiciones, es más fácil alejarse de ellas sin darte
cuenta de que necesitas rehacer tu banco de pruebas a medida que aprendes nueva información.
15.3.Seguir con lo predeterminado
Naturalmente, pensamos que NoSQL es una opción viable en muchas circunstancias, de lo contrario,
no habríamos pasado varios meses escribiendo este libro. Pero también nos damos cuenta de que hay
muchos casos, de hecho la mayoría de los casos, en los que es mejor quedarse con la opción
predeterminada de una base de datos relacional.
Las bases de datos relacionales son bien conocidas; Puede encontrar fácilmente personas con la
experiencia de usarlos.
Son maduros, por lo que es menos probable que te encuentres con las asperezas de la nueva
tecnología. Hay muchas herramientas que se basan en la tecnología relacional que puedes
aprovechar. Tampoco tiene que lidiar con los problemas políticos de tomar una decisión inusual:
elegir una nueva tecnología siempre introducirá un riesgo de problemas en caso de que las cosas
se encuentren con dificultades.
Por lo tanto, en general, tendemos a adoptar la opinión de que para elegir una base de datos
NoSQL debe mostrar una ventaja real sobre las bases de datos relacionales para su situación. No
hay que avergonzarse de hacer las evaluaciones de programabilidad y rendimiento, sin encontrar
ninguna ventaja clara y quedarse con la opción relacional. Creemos que hay muchos casos en los
que es ventajoso utilizar bases de datos NoSQL, pero "muchos" no significa "todos" o incluso "la
mayoría".
15.4.Cubriendo sus apuestas
Una de las mayores dificultades que tenemos a la hora de asesorar a la hora de elegir una opción de
almacenamiento de datos es que no tenemos tantos datos para continuar. Mientras escribimos esto,
solo vemos a los primeros usuarios discutiendo sus experiencias con estas tecnologías, por lo que no
tenemos una imagen clara de los pros y los contras reales.
Con una situación tan incierta, hay más argumentos para encapsular la elección de la base de
datos: mantener todo el código de la base de datos en una sección de la base de código que sea
relativamente fácil de usar.
Reemplace si decide cambiar su elección de base de datos más adelante. La forma clásica de hacerlo
es a través de una capa de almacén de datos explícita en la aplicación, mediante patrones como Data
Mapper y Repository [Fowler PoEAA]. Una capa de encapsulación de este tipo conlleva un costo,
especialmente cuando no está seguro de usar modelos bastante diferentes, como los modelos de
clave-valor frente a los modelos de datos de grafos. Peor aún, todavía no tenemos experiencia con la
encapsulación de capas de datos entre estos tipos tan diferentes de almacenes de datos.
En general, nuestro consejo es encapsular como una estrategia predeterminada, pero preste
atención al costo de la capa aislante. Si se está volviendo demasiado pesado, por ejemplo,
dificultando el uso de algunas características útiles de la base de datos, entonces es un buen
argumento para usar la base de datos que tiene esas características. Esta información puede ser justo
lo que necesita para hacer una elección de base de datos y, por lo tanto, eliminar la encapsulación.
Este es otro argumento para descomponer la capa de la base de datos en servicios que encapsulan
el almacenamiento de datos ("Uso de servicios sobre el uso directo del almacén de datos", p. 136).
Además de reducir el acoplamiento entre varios servicios, esto tiene la ventaja adicional de facilitar
la sustitución de una base de datos en caso de que las cosas no funcionen en el futuro. Este es un
enfoque plausible incluso si terminas usando la misma base de datos en todas partes: si las cosas van
mal, puedes cambiarla gradualmente, enfocándote primero en los servicios más problemáticos.
Este consejo de diseño se aplica igual de bien si prefieres quedarte con una opción relacional. Al
encapsular segmentos de su base de datos en servicios, puede reemplazar partes de su almacén de
datos con una tecnología NoSQL a medida que madura y las ventajas se vuelven más claras.
15.5.Puntos clave
•Las dos razones principales para utilizar la tecnología NoSQL son:
•Mejorar la productividad del programador mediante el uso de una base de datos que se
adapte mejor a las necesidades de una aplicación.
•Para mejorar el rendimiento del acceso a los datos mediante alguna combinación de
manejo de volúmenes de datos más grandes, reducción de la latencia y mejora del
rendimiento.
•Es esencial poner a prueba sus expectativas sobre la productividad y/o el rendimiento
del programador antes de comprometerse con el uso de una tecnología NoSQL.
•La encapsulación de servicios admite cambios en las tecnologías de almacenamiento de datos
a medida que evolucionan las necesidades y la tecnología. La separación de partes de las
aplicaciones en servicios también le permite introducir NoSQL en una aplicación existente.
•La mayoría de las aplicaciones, especialmente las no estratégicas, deben ceñirse a la
tecnología relacional, al menos hasta que el ecosistema NoSQL madure.
15.6.Reflexiones finales
Esperamos que este libro te haya resultado esclarecedor. Cuando comenzamos a escribirlo, nos
frustramos por la falta de algo que nos diera una visión amplia del mundo NoSQL. Al escribir este
libro tuvimos que hacer esa encuesta nosotros mismos, y nos ha parecido un viaje agradable.
Esperamos que su viaje a través de este material sea considerablemente más rápido pero no menos
agradable.
En este punto, es posible que esté considerando hacer uso de una tecnología NoSQL. Si es así,
este libro es solo un primer paso en la construcción de su comprensión. Le instamos a que descargue
algunas bases de datos y trabaje con ellas, porque tenemos la firme convicción de que solo se puede
entender correctamente una tecnología trabajando con ella, encontrando sus fortalezas y los
inevitables trucos que nunca llegan a la documentación.
Esperamos que la mayoría de las personas, incluida la mayoría de los lectores de este libro, no
usen NoSQL por un tiempo. Es una tecnología nueva y todavía estamos en las primeras etapas del
proceso de entender cuándo usarla y cómo usarla bien. Pero como con cualquier cosa en el mundo
del software, las cosas están cambiando más rápidamente de lo que nos atrevemos a predecir, así que
esté atento a lo que está sucediendo en este campo.
Esperamos que también encuentres otros libros y artículos que te ayuden. Creemos que el mejor
material sobre NoSQL se escribirá después de que este libro esté terminado, por lo que no podemos
señalarle ningún lugar en particular mientras escribimos esto. Tenemos una presencia activa en la
Web, por lo que para obtener ideas más actualizadas sobre el mundo NoSQL, eche un vistazo a
[Link] y [Link]
Bibliografía
[Pritchett] [Link]/interviews/dan-pritchett-ebay-architecture.
[Proyecto Voldemort] [Link]
[RavenDB]
[Link] [Redis]
[Link]
[Rekon]
[Link] [Riak]
[Link] [Solr]
[Link]
[Strozzi NoSQL] [Link]/cgi-bin/CSA/tw7/I/en_US/NoSQL.
[Tanenbaum y Van Steen] Tanenbaum, Andrew y Maarten Van Steen. Sistemas distribuidos.
Prentice-Hall. 2007. ISBN 0132392275.
[Terrastore] [Link]
[Vogels] Vogels, Werner. Finalmente consistente, revisado.
[Link]/2008/12/eventually_consistent.htm
l.
[Webber Neo4J Escalado] [Link]
[Link].
[Guardián del zoológico] [Link]
Índice
Un
Transacciones ACID (atómicas, coherentes, aisladas y duraderas),
19 en bases de datos de familias de columnas, 109
en bases de datos de grafos, 28, 50, 114–115
en bases de datos relacionales, 10, 26
vs. BASE, 56
banners publicitarios, 108–109
Bases de datos orientadas a agregados, 14, 19–23, 147
Actualizaciones atómicas en, 50, 61
Desventajas de, 30
no hay transacciones ACID en, 50
rendimiento de, 149
frente a bases de datos de
grafos, 28 agregados, 14–23
estructura cambiante de, 98, 132
modelado, 31
Análisis en tiempo real con, 33
Actualizaciones, 26
Métodos ágiles, 123
Amazonas, 9
Consulte también DynamoDB,
análisis de SimpleDB
contando los visitantes del sitio
web, 108 de información
histórica, 144
en tiempo real, 33,
98 Lenguaje Apache
Pig, 76
Biblioteca Apache ZooKeeper, 104, 115
Bases de datos de aplicaciones, 7,
146 Actualización de vistas
materializadas en, 31
arcos (bases de datos de grafos). Ver
bordes operaciones atómicas entre
documentos, 98 reequilibrio atómico,
58
Transacciones atómicas, 92, 104
Actualizaciones atómicas, 50, 61
conmutaciones por error automatizadas, 94
fusiones automatizadas, 48
reversiones automatizadas, 145
Fragmentación automática, 39
disponibilidad, 53
en bases de datos de familias de
columnas, 104–105 en bases de
datos de documentos, 93
en bases de datos de
grafos, 115 vs.
consistencia, 54
Véase también Promedios
del teorema CAP ,
calculando, 72
B
Compatibilidad con versiones anteriores, 126, 131
BASE (Básicamente disponible, Estado blando, Consistencia eventual),
56 Berkeley DB, 81
BigTable DB, 9, 21–22
índices de mapa de bits, 106
blogs, 108
Gráfico de propiedades de
planos, 115 Brewer, Eric, 53
Conjetura del cervecero. Véase Cubos del
teorema CAP (Riak), 82
Valores predeterminados para la
coherencia para, 84 dominio, 83
almacenamiento de todos los
datos juntos en, 82 transacciones
comerciales, 61
C
Caché
rendimiento de, 39,
137 datos obsoletos en,
50
Biblioteca de jaulas, 104
Teorema CAP (Consistencia, disponibilidad y tolerancia de partición),
53–56 para bases de datos de documentos, 93
para Riak, 86
Operaciones CAS (comparar y establecer),
62 Cassandra DB, 10, 21–22, 99–109
Disponibilidad en,
104–105 familias de
columnas en:
comandos para, 105–106
estándar, 101
Súper, 101–102
columnas, 100
a punto de expirar, 108–109
Indexación, 106–107
Lectura, 107
súper, 101
compactación en, 103
consistencia en,
103–104 herramientas
ETL para, 139 traspaso
insinuado en, 104
espacios clave en,
102–104
memtables en, 103
Consultas en, 105–107
reparaciones en, 103–104
factor de replicación en,
103 escalado en, 107
SSTables en, 103
marcas de tiempo en, 100
transacciones en, 104 filas
anchas/delgadas en, 23
clientes, procesamiento en,
67 Clojure idioma, 145
computación en la nube, 149
aglomerantes, 39
clústeres, 8–10, 67–72, 76,
149 en sistemas de
archivos, 8
en Riak, 87
resiliencia de, 8
bases de datos de familias de columnas,
21–23, 99–109 transacciones ACID en,
109
columnas para vistas materializadas en, 31
Combinación de replicación punto a punto y
particionamiento en, 43–44 coherencia en, 103–104
modelando para, 34
rendimiento en, 103
Falta de esquemas de, 28
Bases de datos de vs. key valores,
21 filas anchas/delgadas, 23
Reductores combinables, 70–71
compactación (Casandra), 103
compatibilidad, hacia atrás, 126, 131
simultaneidad, 145
en sistemas de archivos, 141
en bases de datos
relacionales, 4 fuera de
línea, 62
Actualizaciones condicionales, 48,
62–63 conflictos
clave, 82
lectura-escritura, 49–50
resolviendo, 64
escribir-escribir, 47–48, 64
Consistencia,
47–59
eventual, 50, 84
en bases de datos de familias de
columnas, 103–104 en bases de
datos de grafos, 114
en replicación
maestro-esclavo, 52 en
MongoDB, 91
lógico, 50
optimista/pesimista, 48
Léase, 49–52, 56
leer lo que escribes, 52
relajante, 52–56
replicación, 50
Sesión, 52, 63
intercambio, 57
Actualización, 47, 56, 61
vs. disponibilidad, 54
escribir, 92
Véase también Hashes de
contenido del teorema CAP,
62–63
Sistemas de gestión de contenidos, 98, 108
CouchDB, 10, 91
actualizaciones
condicionales en, 63
réplicas en, 94
Contadores, para sellos de versión, 62–63
CQL (lenguaje de consulta Cassandra), 10, 106
CQRS (Segregación de responsabilidades de consulta de
comandos), 143 operaciones entre documentos, 98
C-Store DB, 21
Lenguaje cifrado, 115-119
D
Mapeador de datos y patrón de repositorio,
151 modelos de datos, 13, 25
Orientado a los agregados, 14–23, 30
documento, 20
clave-valor, 20
relacional, 13–14
Redundancia de
datos, 94 bases de
datos
Escogiendo, 7, 147–152
despliegue, 139
encapsulado en capa explícita,
151 NoSQL, definición de, 10–11
integración compartida de, 4, 6
Datastax Ops Center, 139
DBDeploy framework, 125
Herramienta DBMaintain, 126
Puntos muertos, 48
Acceso a la demostración, 108
Dependencia Patrón de red, 77
complejidad de implementación, 139
Algoritmo de Dijkstra, 118
recuperación ante desastres, 94
Sistemas de archivos distribuidos, 76,
141 Sistemas de control de versiones
distribuidos, 48
Sellos de versión, 64
modelos de distribución,
37–43
Véase también replicaciones, fragmentación, bases de
datos de documentos con enfoque de servidor único, 20,
23, 89–98
Disponibilidad en, 93
incrustación de documentos
secundarios en, 90 índices en, 25
replicación maestro-esclavo en, 93
rendimiento en, 91
consultas en, 25,
94–95 conjuntos de
réplicas, 94 escalado
horizontal, 95
ausencia de esquemas de,
28, 98 soporte XML en,
146
cubos de dominio (Riak),
83 Diseño basado en
dominios, 14
DTD (definiciones de tipo de
documento), 146 durabilidad, 56–57
DynamoDB, 9, 81, 100
carros de la compra
en, 55 Dynomite DB,
10
E
Primeros prototipos,
109 Comercio
electrónico
modelado de datos para,
14 esquemas flexibles
para, 98
persistencia políglota de, 133–138 carros
de la compra en, 55, 85, 87
Bordes (bases de datos de grafos), 26, 111
Reglas de elegibilidad,
26 empresas
soporte comercial de NoSQL para,
138–139 simultaneidad en, 4
DB como almacén de
respaldo para, 4 inicio de
sesión de eventos, 97
integración en, 4
Persistencia políglota en, 138–139
Seguridad de los datos en, 139
Manejo de errores, 4, 145
etags, 62
Herramientas ETL, 139
Evans, Eric, 10 años
Registro de eventos, 97, 107–108
Abastecimiento de eventos, 138, 142, 144
consistencia eventual, 50
en Riak, 84
Uso caducante, 108–109
F
conmutaciones por error, automatizadas, 94
sistemas de archivos, 141
como almacén de respaldo
para RDBMS, 3 compatibles
con clústeres, 8
simultaneidad en, 141
distribuidos, 76, 141
rendimiento de, 141
consultas en, 141
FlockDB, 113
modelo de datos de, 27
distribución de nodos en, 115
G
Gilbert, Seth, 53 años
Google, 9
Google BigTable. Véase
BigTable Sistema de archivos de
Google, 141
Bases de datos de grafos, 26–28, 111–121, 148
Transacciones ACID en, 28, 50, 114–115
ignorancia agregada de, 19
disponibilidad en, 115
consistencia en, 114
creando, 113
bordes (arcos) en, 26, 111
mantenida enteramente en la
memoria, 119 replicación
amo-esclavo en, 115
migraciones en, 131
modelando para, 35
Nodos en, 26, 111–117
rendimiento de, 149
Propiedades en, 111
Consultas en, 115–119
relaciones en, 111-121
escalando, 119
Falta de esquemas de, 28
Configuración de un solo
servidor de, 38 transversales,
111–117
frente a bases de datos agregadas, 28
vs. bases de datos relacionales,
27, 112 envoltura en servicio, 136
Lenguaje gremlin, 115
GUID (identificador único global), 62
H
Proyecto Hadoop, 67, 76, 141
HámsterDB, 81
Tablas de hash, 62–63, 81
HBase DB, 10, 21–22, 99–100
Cliente Héctor, 105
Marco de hibernación, 5, 147
Traspaso insinuado, 104
colmena DB, 76
Copia de seguridad en caliente, 40, 42
Reserva de hotel, 4, 55
HTTP (Protocolo de transferencia de
hipertexto), 7 interfaces basadas en, 85
Actualización con, 62
Hipertabla DB, 10, 99–100
Yo
iBATIS, 5, 147
Desajuste de impedancia,
5, 12 inconsistencias
en los carritos de
compras, 55 de
lecturas, 49
de actualizaciones, 56
ventana de, 50–51, 56
índices
mapa de bits, 106
En bases de datos
documentales, 25 datos
obsoletos, 138
actualización, 138
Infinite Graph DB, 113
modelo de datos de,
27
distribución de nodos en,
114-115 picos técnicos
iniciales, 109 bases de datos de
integración, 6, 11
interoperabilidad, 7
J
JSON (notación de objetos JavaScript), 7, 94–95, 146
K
Claves (bases de datos
clave-valor) compuestas,
74
conflictos de, 82
diseño, 85
a punto de expirar, 85
agrupación en particiones, 70
espacios de claves (Cassandra),
102–104
Bases de datos de clave-valor, 20, 23, 81–88
consistencia de, 83–84
Modelado para, 31–33
no hay múltiples operaciones
clave en, 88 falta de esquema
de, 28
fragmentación en, 86
estructura de valores en,
86 transacciones en, 84,
88
frente a bases de datos de familias de columnas, 21
Compatibilidad con XML en, 146
L
Herramienta Liquibase, 126
Servicios basados en la
ubicación, 120 cerraduras
muertos, 48
fuera de línea, 52
Actualizaciones perdidas, 47
Lotus DB, 91 años
Biblioteca Lucene, 85, 88, 116
Lynch, Nancy, 53 años
M
Marco MapReduce, 67
Patrón de map-reducción,
67–77
cálculos con, 72
incremental, 31, 76–77
Mapas en, 68
Vistas materializadas en,
76 particiones en, 70
Reutilización de salidas
intermedias en, 76 etapas para,
73–76
Replicación maestro-esclavo, 40–42
Nombramiento de maestros
en, 41, 57 combinación con
fragmentación, 43
consistencia de, 52
en bases de datos
documentales, 93 en bases
de datos de grafos, 115
sellos de versión, 63
Vistas materializadas, 30
en map-reduce, 76
Actualización, 31
Memcached DB, 81, 87
imágenes de memoria, 144–145
(Casandra), 103
fusiones, automatizadas, 48
Microsoft SQL Server, 8
migraciones, 123–132
durante el desarrollo, 124, 126
en bases de datos de grafos,
131
en proyectos heredados, 126–128
en bases de datos orientadas a
objetos, 146 en bases de datos sin
esquema, 128–132 incrementales,
130
Fase de transición de,
126–128 aplicaciones móviles,
131
MongoDB, 10, 91–97
colecciones en, 91
consistencia en, 91
bases de datos en,
91 herramientas
ETL para, 139
consultas en, 94–95
Conjuntos de réplicas en, 91,
93, 96 Migraciones de
esquema en, 128–131
Particionamiento en, 96
slaveOk en, 91–92, 96
Terminología en, 89
WriteConcern en, 92
MongoDB Monitoring Service,
139 MyBatis Migrator tool, 126
MySQL DB, 53, 119
N
Neo4J DB, 113–118
Transacciones ACID en, 114–115
disponibilidad en, 115
creación de gráficos en,
113 modelo de datos de,
27 esclavos replicados en,
115 envoltura de
servicios, 136
Nodos (bases de datos de grafos),
26, 111 almacenamiento
distribuido para, 114 encontrar
rutas entre, 117 propiedades de
indexación de, 115–116
datos no uniformes, 10, 28,
30 bases de datos NoSQL
Ventajas de, 12
Definición de, 10–11
Falta de soporte para transacciones en,
10, 61 ejecución de clústeres, 10
Falta de esquema de, 10
O
Bases de datos orientadas a objetos, 5, 146
Migraciones en, 146
frente a bases de datos
relacionales, 6 simultaneidad
fuera de línea, 62
cerraduras fuera de línea, 52
Bloqueo fuera de línea
optimista, 62 Oracle DB
Rehacer inicio de sesión,
104 Terminología en, 81,
89
Oracle RAC DB, 8
OrientDB, 91, 113
Marcos ORM (Mapeo Relacional de Objetos), 5–6, 147
Oskarsson, Johan, 9
P
Tolerancia de partición, 53–54
Véase también partición
del teorema CAP, 69-70
replicación punto a punto, 42–43
durabilidad de, 58
inconsistencia de, 43
sellos de versión en,
63–64
Herramienta Pentaho,
rendimiento 139
y fragmentación, 39
y transacciones, 53
protocolos binarios para,
7 almacenamiento en
caché para, 39, 137
acceso a datos, 149–150
en bases de datos orientadas a
agregados, 149 en bases de datos de
familias de columnas, 103
en bases de datos de
documentos, 91 en bases
de datos de grafos, 149
capacidad de respuesta de,
48
pruebas para, 149
Enfoque de tuberías y filtros, 73
persistencia políglota, 11, 133–139, 148
y complejidad de implementación,
139
en las empresas, 138–139
Programación políglota, 133–134
procesamiento, en
clientes/servidores, 67 productividad
del programador, 147–149
órdenes de compra, 25
Q
Consultas
frente a la estructura agregada
variable, 98 por datos, 88, 94
por llave, 84–86
para archivos, 141
en bases de datos de familias de
columnas, 105–107 en bases de
datos de documentos, 25, 94–95
En bases de datos de grafos,
115–119 precalculadas y
almacenadas en caché, 31 a
través de vistas, 94
Quórumes, 57, 59
leer, 58
escribir, 58, 84
R
Marco de registro activo de Rails, 147
RavenDB, 91
operaciones atómicas entre documentos
en, 98 conjuntos de réplicas en, 94
transacciones en, 92
RDBMS. Ver lecturas de bases de
datos relacionales
consistencia de, 49–52, 56, 58
Escalado horizontal para, 94, 96
inconsistente, 49
nodos múltiples para,
143 rendimiento de,
52
quórumes de, 58
reparaciones de, 103
resiliencia de, 40-41
separando de las
escrituras, 41 rancio, 56
Conflictos de lectura y escritura, 49–50
Consistencia de lectura de
escrituras, 52 Análisis en
tiempo real, 33
BI en tiempo real, 33
reequilibrio, atómico, 58
Recomendación Motores, 26, 35, 121, 138
Redis DB, 81–83
registro de rehacer, 104
funciones de reducción, 69
combinable, 70–71
Regiones. Ver patrón de map-reducción,
particiones en el navegador Rekon para Riak,
139
bases de datos relacionales (RDBMS), 13, 17
ventajas de, 3–5, 7–8, 150
ignorancia agregada de,
19 almacenamiento de
respaldo en, 3 agrupados,
8
columnas en, 13, 90
simultaneidad en, 4
definiendo esquemas
para, 28
Desajuste de impedancia en, 5, 12
Costos de licencia de, 8
Entrada de memoria principal, 3
Modificación de varios registros a la
vez en, 26 particiones en, 96
persistencia en, 3
Relaciones (tablas) en, 5, 13
Esquemas para, 29–30, 123–128
seguridad en, 7
fragmentación en, 8
simplicidad de las relaciones en,
112 fuerte consistencia de, 47
terminología en, 81, 89
Transacciones en, 4, 26, 92
tuplas (filas) en, 5, 13–14
Vistas en, 30
frente a bases de datos de
grafos, 27, 112 frente a bases
de datos orientadas a objetos, 6
soporte XML en, 146
Relaciones, 25, 111–121
colgando, 114
dirección de, 113, 116, 118
en RDBMS, 112
Propiedades de, 113–115
Travesía, 111–117
RelájateNG, 146
Conjuntos de réplicas, 91, 93, 96
factor de replicación, 58
en bases de datos de familias de
columnas, 103 en Riak, 84
Réplicas, 37
combinación con
particionamiento, 43
consistencia de, 42, 50
durabilidad de, 57
sobre clústeres, 149
rendimiento de, 39 sellos
de versión, 63–64
Consulte también replicación maestro-esclavo, resistencia de
replicación punto a punto
y fragmentación, 39
Leer, 40–41
capacidad de
respuesta, 48
Riak DB, 81-83
conglomerados en, 87
controlando CAP en,
86
consistencia final en, 84
interfaz basada en HTTP
de, 85 enlace que camina
en, 25
Recuperación parcial en,
25 Factor de replicación
en, 84 Envoltura de
servicios, 136
Terminología en, 81
transacciones en, 84
tolerancia de
escritura de, 84
Riak Search, 85, 88
modelo de dominio
enriquecido, 113
reversiones, automatizadas, 145
enrutamiento, 120
filas (RDBMS). Ver tuplas
S
Código de andamios, 126
escalado, 95
horizontal, 149
para lecturas, 94, 96
para escrituras, 96
en bases de datos de familias de
columnas, 107 en bases de datos
de documentos, 95
en bases de datos de
grafos, 119 verticales, 8
Patrón de dispersión-recolección, 67
Bases de datos sin esquema, 28–30,
148 Esquema implícito de, 29
Cambios de esquema en, 128–132
esquemas
retrocompatibilidad de, 126, 131
cambiante, 128–132
durante el desarrollo, 124, 126
implícito, 29
migraciones de, 123-132
motores de búsqueda, 138
seguridad, 139
servidores
mantenimiento de, 94
procesamiento en, 67
arquitectura orientada a servicios, 7
servicios, 136
y seguridad, 139
descomposición de la capa de base
de datos en, 151 desacoplamiento
entre bases de datos y, 7 a través de
HTTP, 7
Sesiones
afinidad, 52
consistencia de, 52,
63 claves de
caducidad para, 85
gestión de, 133
pegajoso, 52
Almacenamiento, 57, 87
fragmentación, 37–38, 40, 149
y rendimiento, 39
y resiliencia, 39
Auto, 39
por ubicación del cliente, 97 en
combinación con replicación,
43 en bases de datos
clave-valor, 86
en MongoDB, 96
en bases de datos
relacionales, integración de 8
bases de datos compartidas, 4, 6
carritos de compras
claves de caducidad para,
85 inconsistencia en, 55
persistencia de, 133
almacenamiento, 87
arrastrando los pies, 70
SimpleDB, 99
ventana de inconsistencia
de, 50 enfoque de servidor
único, 37–38
consistencia de, 53
sin tolerancia de partición en, 54
transacciones en, 53
Sellos de versión, 63
Procesadores de eventos de un solo
subproceso, 145 instantáneas, 142–143
Redes sociales, 26, 120
relaciones entre nodos en, 117
motor de indexación Solr, 88, 137,
141 situación de cerebro dividido, 53
SQL (lenguaje de consulta estructurado), 5
SSTables (Cassandra), 103
Datos obsoletos
en caché, 50
en índices/motores de búsqueda,
138 lectura, 56
familias de columnas estándar
(Cassandra), 101 sesiones adhesivas, 52
modelos de almacenamiento, 13
Strozzi, Carlo, 9
Familias de supercolumnas (Cassandra),
101–102 supercolumnas (Cassandra), 101
transacciones del sistema, 61
T
Mesas. Ver bases de datos relacionales,
relaciones en datos telemétricos de
dispositivos físicos, 57 Terrastore DB, 91,
94
Marcas de tiempo
noción coherente de tiempo
para, 64 en bases de datos de
familias de columnas, 100 de la
última actualización, 63
sistemas de memoria transaccional, 145
transacciones, 50
ÁCIDO, 10, 19, 26, 28, 50, 56, 109, 114–115
en múltiples operaciones, 92
y rendimiento, 53
atómico, 92, 104
negocios, 61
En bases de datos de grafos, 28, 114–115
en bases de datos de clave-valor, 84, 88
en RDBMS, 4, 26, 92
en sistemas de un solo servidor, 53
falta de soporte en NoSQL para, 10, 61
multioperación, 88
abierto durante la interacción del
usuario, 52 reversión, 4
sistema, 61
estructuras arbóreas, 117
gatillos, 126
TTL (tiempo de vida),
108–109 tuplas (RDBMS), 5,
13–14
U
Actualizaciones
atómico, 50, 61
condicional, 48, 62–63
consistencia de, 47, 56, 61
perdidos, 47
fusión, 48
Marcas de tiempo de, 63–64
Comentarios de los usuarios, 98
Preferencias del usuario, 87
Perfiles de usuario, 87, 98
Registros de usuarios, 98
sesiones de usuario, 57
V
reloj vectorial, 64
Sistemas de control de versiones, 126, 145
distribuidos, 48, 64
Sellos de versión, 52, 61–64
versión vectorial, 64
Vistas, 126
columnas virtuales, 126
Voldemort DB, 10, 82
W
servicios web, 7
sitios web
distribución de páginas
para, 39 en grandes
clústeres, 149
publicación, 98
Contadores de visitantes
para, 108 procesadores de
texto, 3
Tolerancia de escritura, 84
escribe, 64
atómico, 104
conflictos de, 47-48
consistencia de, 92
escalado horizontal
para, 96 rendimiento de,
91
quórumes de, 58
separando de lecturas,
41 serializando, 47
X
XML (Lenguaje de marcado extensible), 7, 146
Bases de datos XML,
145–146 Lenguaje de
esquema XML, 146
Lenguaje XPath, 146
XQuery idioma, 146
XSLT (Transformaciones extensibles del lenguaje de hojas de estilo), 146
Z
Guardián del zoológico. Ver Apache ZooKeeper