[AUDIO EN BLANCO] [MÚSICA] Bienvenidos al segundo vídeo dedicado a las tecnologías de bases de datos no relacionales. En esta sesión, repasaremos los conceptos en cuanto a modelo de datos y arquitectura de las soluciones que comentamos en el anterior vídeo. Y finalmente, repasaremos cuáles son las ventajas e inconvenientes de cada una de ellas. Empezaremos con HBASE. HBASE, como you vimos, es un proyecto que actualmente lleva Apache, y que está fuertemente integrado con el ecosistema Hadoop. Como también comentamos, es un proyecto que nació como clon de Google Big Table, y por lo tanto hereda mucha de sus características, principalmente lo que es la organización del modelo de datos y el concepto de familia de columnas. No obstante, obviamente, ha ido evolucionando con el tiempo, y actualmente presenta sus características propias. Si nos fijamos en el modelo de datos, vemos que cada uno de los registros tiene cuatro componentes muy importantes. En primer lugar, tenemos la clave de fila, tenemos un time stamp, y luego vemos que los datos vienen referenciados de la siguiente manera, que es, nombre de familia de columna, dos puntos, nombre de calificador de columna, lo cual nos indica más concretamente qué tipo de datos tenemos en cada uno de los registros. Como podemos ver en este ejemplo, existirían varias celdas vacías de datos. Esto en la realidad no es así. Y una representación más aproximada de la realidad vendría a ser como ésta, tipo JSON, en las que en realidad las celdas que aparecían vacías no ocupan espacio de memoria y ni tan siquiera existen. De hecho, la existencia de datos para cada familia de columnas, como you comentamos, pues no es requerida. Bien, aquí podemos observar distintos campos que contienen distintos datos para familias de columnas y calificadores de columnas, pero físicamente hemos de tener en cuenta que los datos no están almacenados de manera que cada fila contiene todas las columnas de familias, sino que a nivel físico los datos están almacenados simplemente por clave de fila, time stamp, y luego, familias de columnas por separado. Es decir, cada registro tiene una clave de fila, un time stamp, y los datos relativos a una sola familia de columna. De todos modos, por simplificar, vamos a imaginar que almacenamos todos los campos de todas las familias de columnas. Si tenemos en cuenta esto, podremos observar en este ejemplo que la arquitectura de HBASE lo que hace es un particionamiento horizontal de los datos de modo que cada fragmento viene identificado por una clave de fila inicial y una clave de fila final. Una vez tenemos particionados los datos, estos datos se distribuyen en distintas regiones. Esto corresponde a una distribución en distintas ubicaciones lógicas. Cada una de esta región está gobernada por un servidor de región, y cada conjunto de regiones que están gobernadas por un servidor de región pueden estar distribuidas geográficamente. En este esquema, hay dos componentes esenciales para el funcionamiento de HBASE. Por un lado, tenemos HMaster, que como su nombre indica, será el demonio principal del sistema, y tenemos Apache Zookeeper, este será el elemento de coordinación distribuida esencial también para el funcionamiento de toda la arquitectura. Como podemos ver, estas son las interacciones que hay entre los distintos componentes. A partir de estas claves, HBASE distribuye los datos en distintas ubicaciones lógicas que son denominadas regiones. Cada una de estas regiones a su vez están gobernadas por un servidor de región o region server. A su vez, cada conjunto de regiones pueden estar distribuidas físicamente y en cada ubicación física disponemos de uno o más servidores de región que mantendrán el estado de cada una de las regiones. En este esquema, existen dos componentes esenciales para que la arquitectura de HBASE funcione. Estos son, Zookeeper y HMaster. HMaster, como su nombre lo indica, es el proceso principal que se encarga de la asignación y reasignación de las distintas regiones, efectúa las tareas DDL, de la base de datos, estos son las tareas de, de data definition, pues como son la creación y eliminación de tablas, y mantenimiento de la coherencia de los datos. Y también se ocupa de todo lo relacionado con el balanceo de carga. El componente Zookeeper es esencial en el sentido de que es el coordinador principal de todo el sistema. Si os fijáis, en este esquema de interacciones, un cliente cuando realiza cualquier petición lo primero que ha de hacer es ponerse en contacto con Zookeeper para obtener la dirección del servidor de región asociado a los datos a los que desea acceder. Luego, una vez obtenida estas direcciones, el cliente podrá realizar todas las operaciones de tipo data management, es decir, lectura de registros, actualización, etcétera, directamente hablando con los servidores de región. A su vez, Zookeeper mantiene la configuración global del sistema y monitoriza las caídas de todos los componentes de ellos. La relación entre Zookeeper y HMaster a su vez es muy estrecha, de manera que HMaster y Zookeeper se intercambian continuamente hard bits, y HMaster informa en todo momento del estado de cada uno de los region servers a Zookeeper. Bien, una vez vista por encima la arquitectura de HBASE, pasamos a Cassandra. Cassandra, como you comentamos, es un proyecto que nace inicialmente de Facebook, y muchos expertos lo consideran una suma de características de Google Big Table y de Amazon Dinamo. Como veis en este gráfico, Facebook lo liberó en modo de Open Source a partir de 2008, y en 2009 Apache se hizo cargo del proyecto. En cuanto al modelo de datos, Cassandra es muy parecido a HBASE. Como you estuvimos comentando, Cassandra responde a un modelo de datos de familias de columnas. Lo que sí que hace especialmente particular al sistema Cassandra es su arquitectura. Como vemos en este esquema, podemos ver cómo todos los nodos que contienen datos se distribuyen topológicamente en forma de anillo. De manera simplificada, podemos decir que un cliente cuando tiene una petición se puede dirigir a cualquiera de estos nodos, y en el momento en el que se dirige a cualquiera de estos nodos, este nodo asume la responsabilidad de coordinador para esta petición exclusiva. Cada nodo dentro del anillo de Cassandra, conoce el estado y los datos que contienen el resto de nodos. Por lo tanto, el nodo coordinador de la petición lo que hará es redirigir la consulta a los nodos que contienen esta información. Y una vez obtenida la respuesta, redirigirá esta al propio cliente. ¿Cómo gestiona todo esto el sistema Cassandra? Bueno, en primer lugar, tenemos que tener en cuenta que Cassandra realiza un particionamiento de los datos basado también en clave, de manera análoga a como lo hacía HBASE, pero con una sutil diferencia que es que cada clave de fila, a cada clave de fila le aplica una función Hash, mediante la cual obtenemos un valor Hash, que le vamos a llamar número de token. Y por lo tanto, cada porción de datos lo vamos a denominar token. De este modo, Cassandra maneja un rango de valores de token que va de menos dos a la 63, a dos a la 63 menos uno. Vamos a imaginar, para poder comprenderlo de manera más rápida, que este rango va de cero a 999. Bien, pues este rango completo se distribuiría uniformemente entre todos los nodos del anillo. De modo, que tenemos que hay nodos que tienen un rango de cero a 199, de 200 a 399, así hasta llegar hasta 999. Una vez realizamos la función Hash a la porción de datos, a una porción de datos indeterminada, si obtenemos por ejemplo un valor 700, pues este, este dato o esta porción de datos, iría a parar al nodo que tiene asignado ese rango de valores, el rango de valores donde cae este valor obtenido por la función Hash. Además de esto, Cassandra permite o aumenta la disponibilidad de los datos vía un sistema de réplicas. Así que, además del nodo que contiene la información original, esta información es replicada en n nodos del mismo anillo. Cassandra tiene un sistema particular de coordinación entre nodos. Como podemos ver, esta arquitectura de anillo se aleja completamente del esquema tradicional Master-Slave, aquí no hay master ni slave. Y de hecho, hemos visto que cada petición puede ser atendida por cualquiera de los nodos. ¿Cómo sabe, por lo tanto, cada nodo qué información tiene el restos de nodos? Bien. Cassandra implementa lo que se denomina el protocolo gossip, que consiste en una comunicación constante nodo a nodo. Es decir, cada nodo tiene comunicación constante y frecuente con cada uno de los nodos restantes del anillo. En esta comunicación, y mediante este protocolo, pues se intercambia la información de estado, de número de tokens, de tamaño disponible, etcétera. Esta arquitectura es escalable y la podemos repetir entre distintas ubicaciones. De modo que podemos tener lo que se conoce como distintos data centers que, por ejemplo, podrían estar compuestos, en este caso cada uno por un solo anillo. Y los data centers los agruparemos conceptualmente en clusters. Los clusters, obviamente, pueden estar distribuidos también geográficamente. De este modo, el esquema se repite, pero con la peculiaridad de que la réplica de datos puede estar distribuida entre todos o varios data centers. De manera que el cliente, cuando hace una petición y se es asignado un nodo coordinador de esta petición, este nodo, además de preguntar a los nodos que figuran en su mismo anillo, preguntará a un nodo de otro data center que a su vez se convertirá en coordinador de esa petición para ese data center. De este modo, obtendremos una respuesta de un determinado nodo y satisfaremos la petición. Esta arquitectura que ofrece Cassandra está claramente orientada a disponer de una gran disponibilidad de los datos y en realidad es mucho más compleja, con lo que si queréis entrar en detalle, os recomiendo encarecidamente que consultéis la documentación oficial de Cassandra. Algo importante que podemos observar dentro de los distintos detalles de configuración es que a pesar de que Cassandra siempre lo hemos categorizado como un sistema AP dentro del teorema CAP, nosotros podemos llegar a ofrecer una serie de consistencia más o menos elevada. Es decir, esta consistencia es ajustable y Cassandra implementa distintos métodos para aumentar la consistencia, básicamente basados en lo que denominamos quórum. De modo que, por ejemplo, nosotros para poder dar una petición por satisfecha, dependiendo del número de réplicas que existan en cada uno de los anillos de los data centers, será necesario que la respuesta, será necesario obtener una respuesta coherente de n nodos. Y esa n es lo que constituirá el quórum que podemos configurar de antemano. De este modo, como vemos, a más quórum, pues una mayor consistencia. A menor quórum, menor latencia, pero posibilidad de conflictos de consistencia. Muy bien. Pasemos a MongoDB. MongoDB es una base de datos, como you comentamos, orientada a documentos, es decir, el modelo de datos responde al concepto de documento, donde los datos son mucho más vastos que los que venimos analizando en los paradigmas de familias de columnas, y los podemos ver representados de esta manera. Como podéis observar, esta manera de representar un cierto registro es absolutamente análoga a lo que vendría a ser un objeto tipo JSON. De hecho, los documentos en MongoDB se codifican mediante un tipo de datos llamado BSON, que es como un JSON pero en binario. Esto se hace porque de este modo podemos, MongoDB puede ofrecer el tipado de los datos que contiene cada documento. De manera que podemos tener campos con valores enteros, campos con valores string, campos dobles, podemos tener arrays de distintos tipos de datos, etcétera. Bien. Estos documentos BSON conforman la unidad básica de datos que maneja MongoDB. Estos documentos, a su vez, se pueden apilar en lo que se denominan colecciones. Y como veremos, las bases de datos contienen colecciones de documentos y estas colecciones además podrán ser subdividas en chunks o fragmentos. Algo interesante también a considerar respecto a este modelo de datos es la relación que se establece entre los distintos documentos. De manera que Mongo proporciona una manera de relacionar los datos que bien puede ser mediante enlaces, es decir, utilizando los propios identificadores internos que se generan para cada documento, o bien con documentos incrustados. Esto es incrustar directamente un subdocumento como valor de un determinado campo. MongoDB ofrece un sistema de réplica de datos muy consistente. El sistema que ofrece en cuanto a réplica de datos es denominado replica set y consiste en que varias instancias corren en distintos nodos y contienen los mismos datos, siguiendo un esquema master/slave, a diferencia de lo que acabamos de ver en Cassandra. De manera que, como aquí podéis ver, tenemos en este caso un nodo primario que actuará de master y dos nodos secundarios que actuarán de slaves. Solamente el nodo primario atenderá las peticiones de lectura y escritura, y cuando se atienda una operación de lectura, se responderá e inmediatamente después se realizará una replicación en los componentes secundarios. El sistema master/slave se mantiene gracias a que entre todos los componentes, tanto secundarios como el componente master, se envían frecuentemente señales de heartbeat. De manera que si tenemos que un nodo primario cae, automáticamente los nodos secundarios organizan una votación en la que una cierta cantidad de nodos se postulan como candidatos, se lleva a cabo una votación de manera automática para ver quién es el nuevo master y una vez se ha decidido este master, pues adquiere el rol y, por lo tanto, atenderá a las peticiones de lectura y escritura y realizará las réplicas correspondientes en los nodos secundarios. Un concepto muy importante en el que los desarrolladores de Mongo han puesto mucha atención es en el concepto de sharding o particionamiento. Sharding, you hemos visto en videos anteriores que es una técnica que permite escalar horizontalmente, dividiendo los datos en varios servidores y, en el caso de Mongo, esta división se realiza utilizando lo que denominan una shard key, que vendría a ser análogo a las claves de fila que utilizábamos en el esquema de HBase. De esta manera, podemos ver que una simple colección de datos la podemos dividir entre varios shards, dividiendo de esta manera lo que son los distintos documentos contenidos en una misma colección. En un esquema de sharding de MongoDB existen tres componentes principales, que son el enrutador, los servers de configuración y los shards en sí. Los shards en sí serán las particiones lógicas o físicas que contendrán los datos. El router, que estará dirigido por el proceso llamado mongos, se encargará de atender las peticiones y, como su nombre indica, pues de enrutarlas al shard correspondiente. Y todos los datos relativos a la configuración de este sistema se mantendrán en un conjunto de servidores de configuración. ¿Hecho? Una configuración típica en los sistemas de MongoDB, pues vendría a ser como esta, en la que vemos que podemos disponer de varios enrutadores, varios servidores de configuración y varios shards. Aquí podemos ver que tanto los shards como los servidores de configuración están contenidos dentro de un replica set, lo cual aumenta la consistencia y la disponibilidad. Y la división entre varios enrutadores lo que nos permite es tener distintos servidores de aplicación y de este modo balancear la carga de peticiones que vienen desde el exterior. Otro aspecto interesante a tener en cuenta es que, aparte de este esquema de particionado o de sharding que Mongo puede realizar automáticamente, además Mongo cada subconjunto que provenía de una colección y que está ubicado en un determinado shard, lo puede dividir a su vez en lo que conocemos como distintos chunks. A partir de esta manera de fragmentar los datos, lo que nos permite Mongo es aumentar las prestaciones en cuanto a latencia de cada una de las operaciones, permite aumentar la escalabilidad del sistema global y también nos permite balancear la carga en cuanto a los datos que contiene cada uno de los shards. De manera que Mongo puede decidir automáticamente migrar determinados conjuntos de chunks de un shard a otro. De este modo, tenemos bien equilibrada la carga de datos que existe en cada una de las particiones. Obviamente, cada solución presenta muchos más detalles de los que podemos abordar en un solo video. Así que, como he dicho antes, os animo a estudiar la documentación oficial de cada una de ellas. Sí tenéis que tomar la decisión de incorporar una u otra en vuestro trabajo o en vuestros proyectos. Lo que sí que diría que es importante a la hora de tener en cuenta es, primero os centréis dentro del teorema CAP, ¿qué necesita vuestro sistema? Como hemos visto, tanto HBase como MongoDB los clasificaríamos dentro de un sistema CP, mientras que Cassandra sería un sistema AP. Por otro lado, aquí podemos ver pues que HBase y Cassandra comparten las ventajas de que tienen una capacidad de responder a búsquedas de manera muy rápida. Además, sabemos que HBase está plenamente integrado con el ecosistema Hadoop y con el sistema de ficheros HDFS. Cassandra tiene o proporciona una alta disponibilidad y además, según los expertos, es muy fácil de administrar. Por su lado, MongoDB, como you dije en el anterior video, es un sistema que está muy orientado a los desarrolladores, de manera que es muy fácil de aprender y es muy fácil de aplicar en el desarrollo. Y tiene, a diferencia de HBase y Cassandra, una API de alto nivel para tratar los datos. Además, pues como hemos visto, proporciona unas estrategias bastante completas de balanceo particionado de la carga y también de alta escalabilidad. Bien. Podemos ver que la complejidad en cuanto a la administración en MongoDB es más alta. Pero bueno, como he dicho, pues todos tienen sus ventajas e inconvenientes y la decisión al final la tendréis que tomar estudiándolos a fondo. Muchas gracias y hasta pronto. [AUDIO_EN_BLANCO]