Sistemas de bases de datos distribuidas 101
O, Bases de datos distribuidas: ¿qué significa realmente el FK ' escala web '?
Los sistemas de bases de datos distribuidas son criaturas complejas y vienen en varios sabores diferentes. Si profundizo en mis estudios apenas recordados sobre esto en la universidad, intentaré explicar algunos de los problemas clave de ingeniería para construir un sistema de base de datos distribuido.
Primero, alguna terminología
Propiedades de ACID (Atomicidad, Consistencia, Aislamiento y Durabilidad): Estas son las invariantes clave que deben aplicarse para que una transacción se implemente de manera confiable sin causar efectos secundarios indeseables.
Atomicity requiere que la transacción se complete o revierta por completo. Las transacciones parcialmente finalizadas nunca deben ser visibles, y el sistema debe construirse de manera que evite que esto suceda.
La coherencia requiere que una transacción nunca viole los invariantes (como la integridad referencial declarativa) garantizados por el esquema de la base de datos. Por ejemplo, si existe una clave externa, debería ser imposible insertar un registro secundario con una reverencia a un padre inexistente.
El aislamiento requiere que las transacciones no interfieran entre sí. El sistema debe garantizar los mismos resultados si las transacciones se ejecutan en paralelo o secuencialmente. En la práctica, la mayoría de los productos RDBMS permiten modos que intercambian el aislamiento con el rendimiento.
La durabilidad requiere que una vez confirmada, la transacción permanezca en un almacenamiento persistente de una manera que sea robusta ante fallas de hardware o software.
Explicaré algunos de los obstáculos técnicos que estos requisitos presentan en los sistemas distribuidos a continuación.
Arquitectura de disco compartido: una arquitectura en la que todos los nodos de procesamiento en un clúster tienen acceso a todo el almacenamiento. Esto puede presentar un cuello de botella central para el acceso a datos. Un ejemplo de un sistema de disco compartido es Oracle RAC o Exadata .
Arquitectura Nothing Shared: una arquitectura en la que los nodos de procesamiento en un clúster tienen almacenamiento local que no es visible para otros nodos del clúster. Ejemplos de sistemas de nada compartido son Teradata y Netezza .
Arquitectura de memoria compartida: una arquitectura en la que múltiples CPU (o nodos) pueden acceder a un grupo compartido de memoria. La mayoría de los servidores modernos son de un tipo de memoria compartida. La memoria compartida facilita ciertas operaciones como cachés o primitivas de sincronización atómica que son mucho más difíciles de hacer en sistemas distribuidos.
Sincronización: un término genérico que describe varios métodos para garantizar el acceso constante a un recurso compartido por múltiples procesos o subprocesos. Esto es mucho más difícil de hacer en sistemas distribuidos que en sistemas de memoria compartida, aunque algunas arquitecturas de red (por ejemplo, BYNET de Teradata) tenían primitivas de sincronización en el protocolo de red. La sincronización también puede venir con una gran cantidad de sobrecarga.
Semi-Join: Una primitiva utilizada para unir datos contenidos en dos nodos diferentes de un sistema distribuido. Esencialmente, consiste en suficiente información sobre las filas para unir que se agrupan y pasan de un nodo a otro para resolver la unión. En una consulta grande, esto podría implicar un tráfico de red significativo.
Consistencia eventual: un término utilizado para describir la semántica de transacciones que compensa la actualización inmediata (consistencia en las lecturas) en todos los nodos de un sistema distribuido para el rendimiento (y, por lo tanto, un mayor rendimiento de las transacciones) en las escrituras. La coherencia eventual es un efecto secundario del uso de Quorum Replication como una optimización del rendimiento para acelerar las confirmaciones de transacciones en bases de datos distribuidas donde se mantienen múltiples copias de datos en nodos separados.
Algoritmo de Lamport: un algoritmo para implementar exclusión mutua (sincronización) en sistemas sin memoria compartida. Normalmente, la exclusión mutua dentro de un sistema requiere una instrucción atómica de lectura-comparación-escritura o similar de un tipo que normalmente solo es práctico en un sistema de memoria compartida. Existen otros algoritmos de sincronización distribuida, pero Lamport fue uno de los primeros y es el más conocido. Como la mayoría de los mecanismos de sincronización distribuidos, el algoritmo de Lamport depende en gran medida de la sincronización precisa y la sincronización del reloj entre los nodos del clúster.
Two Phase Commit (2PC): una familia de protocolos que aseguran que las actualizaciones de la base de datos que involucran múltiples sistemas físicos se comprometen o retrocedan de manera consistente. Si 2PC se usa dentro de un sistema o en múltiples sistemas a través de un administrador de transacciones, conlleva una sobrecarga significativa.
En un protocolo de confirmación de dos fases, el administrador de transacciones le pide a los nodos participantes que persistan en la transacción de tal manera que puedan garantizar que se comprometerá y luego señalar este estado. Cuando todos los nodos han devuelto un estado 'feliz', entonces les indica a los nodos que se comprometan. La transacción todavía se considera abierta hasta que todos los nodos envíen una respuesta indicando que la confirmación se ha completado. Si un nodo se cae antes de indicar que se completa la confirmación, el administrador de transacciones volverá a consultar el nodo cuando vuelva a funcionar hasta que obtenga una respuesta positiva que indique que la transacción se ha confirmado.
Control de concurrencia de múltiples versiones (MVCC): gestión de contención escribiendo nuevas versiones de los datos en una ubicación diferente y permitiendo que otras transacciones vean la versión anterior de los datos hasta que se confirme la nueva versión. Esto reduce la contención de la base de datos a expensas de algún tráfico de escritura adicional para escribir la nueva versión y luego marcar la versión anterior como obsoleta.
Algoritmo de elección: los sistemas distribuidos que involucran múltiples nodos son inherentemente menos confiables que un solo sistema, ya que hay más modos de falla. En muchos casos, se necesita algún mecanismo para que los sistemas en clúster se ocupen de la falla de un nodo. Los algoritmos de elección son una clase de algoritmos utilizados para seleccionar un líder para coordinar un cálculo distribuido en situaciones en las que el nodo 'líder' no está 100% determinado o es confiable.
Particionamiento horizontal: una tabla puede dividirse en múltiples nodos o volúmenes de almacenamiento por su clave. Esto permite que un gran volumen de datos se divida en fragmentos más pequeños y se distribuya entre los nodos de almacenamiento.
Fragmentación: un conjunto de datos puede dividirse horizontalmente en múltiples nodos físicos en una arquitectura de nada compartido. Cuando esta partición no es transparente (es decir, el cliente debe conocer el esquema de partición y determinar qué nodo consultar explícitamente), esto se conoce como fragmentación. Algunos sistemas (por ejemplo, Teradata) dividen los datos entre nodos, pero la ubicación es transparente para el cliente; el término normalmente no se usa junto con este tipo de sistema.
Hashing coherente: un algoritmo utilizado para asignar datos a particiones basadas en la clave. Se caracteriza por una distribución uniforme de las claves hash y la capacidad de expandir o reducir elásticamente el número de cubos de manera eficiente. Estos atributos lo hacen útil para particionar datos o cargar a través de un grupo de nodos donde el tamaño puede cambiar dinámicamente con los nodos que se agregan o caen del grupo (tal vez debido a una falla).
Replicación multimaestro: una técnica que permite que las escrituras a través de múltiples nodos en un clúster se repliquen a los otros nodos. Esta técnica facilita el escalado al permitir que algunas tablas se particionen o particionen en servidores y otras se sincronicen en el clúster. Las escrituras deben replicarse en todos los nodos en lugar de un quórum, por lo que los compromisos de transacción son más caros en una arquitectura replicada de varios maestros que en un sistema replicado de quórum.
Conmutador sin bloqueo: un conmutador de red que utiliza el paralelismo interno del hardware para lograr un rendimiento proporcional al número de puertos sin cuellos de botella internos. Una implementación ingenua puede usar un mecanismo de barra cruzada, pero esto tiene una complejidad O (N ^ 2) para N puertos, lo que lo limita a conmutadores más pequeños. Los conmutadores más grandes pueden usar una topología interna más compleja llamada conmutador de expansión mínima sin bloqueo para lograr una escala de rendimiento lineal sin necesidad de hardware O (N ^ 2).
Hacer un DBMS distribuido: ¿qué tan difícil puede ser?
Varios desafíos técnicos hacen que esto sea bastante difícil de hacer en la práctica. Además de la complejidad añadida de construir un sistema distribuido, el arquitecto de un DBMS distribuido tiene que superar algunos problemas de ingeniería difíciles.
Atomicidad en sistemas distribuidos: si los datos actualizados por una transacción se extienden a través de múltiples nodos, la confirmación / reversión de los nodos debe coordinarse. Esto agrega una sobrecarga significativa en los sistemas de nada compartido. En los sistemas de disco compartido, esto no es un problema, ya que todos los nodos pueden ver todo el almacenamiento para que un solo nodo pueda coordinar la confirmación.
Consistencia en sistemas distribuidos: para tomar el ejemplo de clave externa citado anteriormente, el sistema debe poder evaluar un estado consistente. Por ejemplo, si el padre y el hijo de una relación de clave externa podrían residir en diferentes nodos, se necesita algún tipo de mecanismo de bloqueo distribuido para garantizar que la información desactualizada no se use para validar la transacción. Si esto no se aplica, podría tener (por ejemplo) una condición de carrera donde el padre se elimina después de que se verifique su presencia antes de permitir la inserción del niño.
La aplicación tardía de las restricciones (es decir, esperar hasta que se confirme la validación de DRI) requiere que el bloqueo se mantenga durante la transacción. Este tipo de bloqueo distribuido viene con una sobrecarga significativa.
Si se retienen múltiples copias de datos (esto puede ser necesario en sistemas de nada compartido para evitar tráfico de red innecesario de semi-uniones), entonces todas las copias de los datos deben actualizarse.
Aislamiento en sistemas distribuidos: cuando los datos afectados en una transacción residen en múltiples nodos del sistema, las cerraduras y la versión (si MVCC está en uso) deben sincronizarse entre los nodos. Garantizar la serialización de las operaciones, particularmente en arquitecturas de nada compartido donde se pueden almacenar copias redundantes de datos, requiere un mecanismo de sincronización distribuido como el Algoritmo de Lamport, que también conlleva una sobrecarga significativa en el tráfico de red.
Durabilidad en sistemas distribuidos: en un sistema de disco compartido, el problema de durabilidad es esencialmente el mismo que el de un sistema de memoria compartida, con la excepción de que todavía se requieren protocolos de sincronización distribuida en todos los nodos. El DBMS debe escribir en el registro en el diario y escribir los datos de manera consistente. En un sistema de nada compartido puede haber múltiples copias de los datos o partes de los datos almacenados en diferentes nodos. Se necesita un protocolo de confirmación de dos fases para garantizar que la confirmación se realice correctamente en los nodos. Esto también incurre en gastos generales significativos.
En un sistema de nada compartido, la pérdida de un nodo puede significar que los datos no están disponibles para el sistema. Para mitigar estos datos, se pueden replicar en más de un nodo. La coherencia en esta situación significa que los datos deben replicarse en todos los nodos donde normalmente residen. Esto puede generar una sobrecarga considerable en las escrituras.
Una optimización común realizada en los sistemas NoSQL es el uso de la replicación del quórum y la consistencia eventual para permitir que los datos se repliquen perezosamente mientras se garantiza un cierto nivel de resistencia de los datos al escribir en un quórum antes de informar la transacción como comprometida. Los datos se replican perezosamente en los otros nodos donde residen las copias de los datos.
Tenga en cuenta que la "coherencia eventual" es una compensación importante en la coherencia que puede no ser aceptable si los datos deben verse de manera coherente tan pronto como se confirme la transacción. Por ejemplo, en una aplicación financiera, un saldo actualizado debe estar disponible de inmediato.
Sistemas de disco compartido
Un sistema de disco compartido es aquel en el que todos los nodos tienen acceso a todo el almacenamiento. Por lo tanto, el cálculo es independiente de la ubicación. Muchas plataformas DBMS también pueden funcionar en este modo: Oracle RAC es un ejemplo de dicha arquitectura.
Los sistemas de disco compartido pueden escalar sustancialmente ya que pueden soportar una relación M: M entre los nodos de almacenamiento y los nodos de procesamiento. Una SAN puede tener múltiples controladores y varios servidores pueden ejecutar la base de datos. Estas arquitecturas tienen un conmutador como un cuello de botella central, pero los conmutadores de barra cruzada permiten que este conmutador tenga mucho ancho de banda. Parte del procesamiento puede descargarse en los nodos de almacenamiento (como en el caso de Exadata de Oracle), lo que puede reducir el tráfico en el ancho de banda de almacenamiento.
Aunque el cambio es teóricamente un cuello de botella, el ancho de banda disponible significa que las arquitecturas de disco compartido se escalarán de manera bastante efectiva a grandes volúmenes de transacciones. La mayoría de las arquitecturas DBMS convencionales adoptan este enfoque porque ofrece escalabilidad "suficientemente buena" y alta confiabilidad. Con una arquitectura de almacenamiento redundante, como el canal de fibra, no hay un único punto de falla, ya que hay al menos dos rutas entre cualquier nodo de procesamiento y cualquier nodo de almacenamiento.
Sistemas de nada compartido
Los sistemas de nada compartido son sistemas en los que al menos parte de los datos se mantienen localmente en un nodo y no son directamente visibles para otros nodos. Esto elimina el cuello de botella de un interruptor central, permitiendo que la base de datos se escale (al menos en teoría) con el número de nodos. La partición horizontal permite que los datos se dividan en nodos; Esto puede ser transparente para el cliente o no (ver Sharding arriba).
Debido a que los datos se distribuyen inherentemente, una consulta puede requerir datos de más de un nodo. Si una unión necesita datos de diferentes nodos, se utiliza una operación de semiunión para transferir suficientes datos para soportar la unión de un nodo a otro. Esto puede generar una gran cantidad de tráfico de red, por lo que optimizar la distribución de los datos puede marcar una gran diferencia en el rendimiento de las consultas.
A menudo, los datos se replican en los nodos de un sistema de nada compartido para reducir la necesidad de semiuniones. Esto funciona bastante bien en los dispositivos de almacenamiento de datos, ya que las dimensiones suelen ser muchos órdenes de magnitud más pequeñas que las tablas de hechos y se pueden replicar fácilmente en todos los nodos. Por lo general, también se cargan en lotes, por lo que la sobrecarga de replicación es menos problemática de lo que sería en una aplicación transaccional.
El paralelismo inherente de una arquitectura de nada compartido los hace muy adecuados para el tipo de consultas de escaneo / agregado de tabla características de un almacén de datos. Este tipo de operación puede escalar casi linealmente con el número de nodos de procesamiento. Las grandes uniones entre nodos tienden a generar más gastos generales ya que las operaciones de semiunión pueden generar mucho tráfico de red.
Mover grandes volúmenes de datos es menos útil para las aplicaciones de procesamiento de transacciones, donde la sobrecarga de múltiples actualizaciones hace que este tipo de arquitectura sea menos atractivo que un disco compartido. Por lo tanto, este tipo de arquitectura tiende a no usarse ampliamente fuera de las aplicaciones de almacenamiento de datos.
Fragmentación, replicación de quórum y consistencia eventual
Quorum Replication es una instalación donde un DBMS replica datos para alta disponibilidad. Esto es útil para sistemas destinados a trabajar en hardware más barato que no tiene características integradas de alta disponibilidad como una SAN. En este tipo de sistema, los datos se replican en múltiples nodos de almacenamiento para el rendimiento de lectura y el almacenamiento redundante para que el sistema sea resistente a la falla de hardware de un nodo.
Sin embargo, la replicación de escrituras en todos los nodos es O (M x N) para M nodos y N escrituras. Esto hace que las escrituras sean caras si la escritura debe replicarse en todos los nodos antes de que una transacción pueda confirmarse. La replicación de quórum es un compromiso que permite que las escrituras se repliquen en un subconjunto de nodos de inmediato y luego se escriban perezosamente en los otros nodos mediante una tarea en segundo plano. Las escrituras pueden confirmarse más rápidamente, al tiempo que proporcionan un cierto grado de redundancia al garantizar que se replican en un subconjunto mínimo (quórum) de nodos antes de que la transacción se informe como confirmada al cliente.
Esto significa que las lecturas de nodos fuera del quórum pueden ver versiones obsoletas de los datos hasta que el proceso en segundo plano haya terminado de escribir datos en el resto de los nodos. La semántica se conoce como 'Consistencia eventual' y puede o no ser aceptable dependiendo de los requisitos de su aplicación, pero significa que los compromisos de transacción están más cerca de O (1) que O (n) en el uso de recursos.
Sharding requiere que el cliente sea consciente de la partición de datos dentro de las bases de datos, a menudo utilizando un tipo de algoritmo conocido como 'hashing consistente'. En una base de datos fragmentada, el cliente utiliza la clave para determinar a qué servidor del clúster emitir la consulta. Como las solicitudes se distribuyen entre los nodos en el clúster, no existe un cuello de botella con un solo nodo coordinador de consultas.
Estas técnicas permiten que una base de datos se escale a una velocidad casi lineal agregando nodos al clúster. Teóricamente, la replicación del quórum solo es necesaria si el medio de almacenamiento subyacente no se considera confiable. Esto es útil si se van a utilizar servidores básicos, pero tiene menos valor si el mecanismo de almacenamiento subyacente tiene su propio esquema de alta disponibilidad (por ejemplo, una SAN con controladores duplicados y conectividad de múltiples rutas a los hosts).
Por ejemplo, BigTable de Google no implementa Quorum Replication por sí solo, aunque sí se sienta en GFS, un sistema de archivos en clúster que sí usa la replicación de quorum. BigTable (o cualquier sistema de nada compartido) podría usar un sistema de almacenamiento confiable con múltiples controladores y dividir los datos entre los controladores. El acceso paralelo se lograría mediante la partición de los datos.
Volver a las plataformas RDBMS
No hay una razón inherente de que estas técnicas no puedan usarse con un RDBMS. Sin embargo, la administración de versiones y bloqueos sería bastante compleja en dicho sistema y es probable que cualquier mercado para dicho sistema sea bastante especializado. Ninguna de las plataformas RDBMS convencionales utiliza la replicación de quórum y no conozco específicamente ningún producto RDBMS (al menos no uno con una absorción significativa) que lo haga.
Los sistemas de disco compartido y nada compartido pueden escalar hasta cargas de trabajo muy grandes. Por ejemplo, Oracle RAC puede admitir 63 nodos de procesamiento (que podrían ser grandes máquinas SMP por derecho propio) y un número arbitrario de controladores de almacenamiento en la SAN. Un IBM Sysplex (un grupo de mainframes zSeries) puede admitir múltiples mainframes (cada uno con una potencia de procesamiento sustancial y ancho de banda de E / S propio) y múltiples controladores SAN. Estas arquitecturas pueden admitir volúmenes de transacciones muy grandes con semántica ACID, aunque suponen un almacenamiento confiable. Teradata, Netezza y otros proveedores hacen plataformas analíticas de alto rendimiento basadas en diseños compartidos que se escalan a volúmenes de datos extremadamente grandes.
Hasta el momento, MySQL domina el mercado de plataformas RDBMS totalmente ácidas y de volumen ultra alto, pero que es compatible con fragmentación y replicación multimaestro. MySQL no utiliza la replicación de quórum para optimizar el rendimiento de escritura, por lo que las confirmaciones de transacciones son más caras que en un sistema NoSQL. Sharding permite rendimientos de lectura muy altos (por ejemplo, Facebook usa MySQL ampliamente), por lo que este tipo de arquitectura escala bien en cargas de trabajo de lectura pesada.
Un debate interesante
BigTable es una arquitectura de nada compartido (esencialmente un par clave-valor distribuido) como lo señala Michael Hausenblas a continuación . Mi evaluación original incluía el motor MapReduce, que no forma parte de BigTable pero que normalmente se usaría junto con él en sus implementaciones más comunes (por ejemplo, Hadoop / HBase y el marco MapReduce de Google).
Al comparar esta arquitectura con Teradata, que tiene afinidad física entre el almacenamiento y el procesamiento (es decir, los nodos tienen almacenamiento local en lugar de una SAN compartida), podría argumentar que BigTable / MapReduce es una arquitectura de disco compartida a través del sistema de almacenamiento paralelo globalmente visible.
El rendimiento de procesamiento de un sistema de estilo MapReduce como Hadoop está limitado por el ancho de banda de un conmutador de red sin bloqueo. 1 Sin embargo, los conmutadores sin bloqueo pueden manejar agregados de gran ancho de banda debido al paralelismo inherente en el diseño, por lo que rara vez son una restricción práctica significativa en el rendimiento. Esto significa que una arquitectura de disco compartida (tal vez mejor conocida como sistema de almacenamiento compartido) puede escalar a grandes cargas de trabajo a pesar de que el conmutador de red es teóricamente un cuello de botella central.
El punto original era señalar que aunque este cuello de botella central existe en los sistemas de disco compartido, un subsistema de almacenamiento particionado con múltiples nodos de almacenamiento (por ejemplo, servidores de tableta BigTable o controladores SAN) aún puede escalar hasta grandes cargas de trabajo. Una arquitectura de conmutador sin bloqueo puede (en teoría) manejar tantas conexiones actuales como puertos.
1 Por supuesto, el procesamiento y el rendimiento de E / S disponibles también constituyen un límite en el rendimiento, pero el conmutador de red es un punto central a través del cual pasa todo el tráfico.