Capítulo 1. Introducción a la tecnología Big Data
Este trabajo se ha traducido utilizando IA. Agradecemos tus opiniones y comentarios: translation-feedback@oreilly.com
Apache Hadoop es un ecosistema estrechamente integrado de diferentes productos de software creados para proporcionar almacenamiento distribuido escalable y fiable y procesamiento distribuido. La inspiración de gran parte del ecosistema Hadoop fue una secuencia de artículos publicados por Google en la década de 2000, en los que se describían innovaciones en sistemas para producir almacenamiento fiable (el Sistema de Archivos de Google), procesamiento (MapReduce, Pregel) y consultas de acceso aleatorio de baja latencia (Bigtable) en cientos o miles de servidores potencialmente poco fiables. Para Google, la principal motivación para desarrollar estos sistemas fue la pura conveniencia: en aquel momento no existían tecnologías capaces de almacenar y procesar los enormes conjuntos de datos con los que estaba tratando. El enfoque tradicional para realizar cálculos sobre conjuntos de datos consistía en invertir en unos cuantos servidores extremadamente potentes con muchos procesadores y mucha RAM, absorber los datos desde una capa de almacenamiento (por ejemplo, NAS o SAN), realizar un cálculo y escribir los resultados en el almacenamiento. A medida que aumentaba la escala de los datos, este planteamiento resultó poco práctico y caro.
La innovación clave, y que aún resiste el paso del tiempo, consistió en distribuir los conjuntos de datos entre muchas máquinas y dividir cualquier cálculo sobre esos datos en muchos trozos independientes, "no compartidos", cada uno de los cuales podía ejecutarse en las mismas máquinas que almacenaban los datos. Aunque las tecnologías existentes podían ejecutarse en varios servidores, normalmente dependían en gran medida de la comunicación entre los componentes distribuidos, lo que conduce a rendimientos decrecientes a medida que aumenta el paralelismo (véase la ley de Amdahl). En cambio, en el enfoque distribuido por diseño, el problema de la escala se maneja de forma natural, porque cada pieza independiente del cálculo es responsable de sólo una pequeña parte del conjunto de datos. El aumento de almacenamiento y potencia de cálculo puede obtenerse simplemente añadiendo más servidores, lo que se denomina arquitectura escalable horizontalmente. Un punto clave del diseño cuando se computa a tales escalas es diseñar asumiendo el fallo de los componentes para construir un sistema fiable a partir de componentes poco fiables. Estos diseños resuelven el problema del escalado rentable porque el almacenamiento y el cálculo pueden realizarse en servidores básicos estándar.
Nota
Con los avances en las redes de productos básicos y el paso generalizado a la computación y el almacenamiento en la nube, el requisito de ejecutar cálculos localmente a los datos es cada vez menos crítico. Si tu infraestructura de red es lo suficientemente buena, ya no es esencial utilizar el mismo hardware subyacente para el cálculo y el almacenamiento. Sin embargo, la naturaleza distribuida y el enfoque escalable horizontalmente siguen siendo fundamentales para el funcionamiento eficaz de estos sistemas.
Hadoop es una implementación de código abierto de estas técnicas. En esencia, ofrece un sistema de archivos distribuido (HDFS) y un medio para ejecutar procesos en un clúster de servidores (YARN). La aplicación original de procesamiento distribuido construida sobre Hadoop fue MapReduce, pero desde su creación, han surgido en torno a Hadoop una amplia gama de marcos y bibliotecas de software adicionales, cada uno de los cuales aborda un caso de uso diferente. En la siguiente sección, haremos un recorrido relámpago por las tecnologías centrales del proyecto Hadoop, así como por algunos de los marcos de código abierto más populares del ecosistema que se ejecutan en clústeres Hadoop.
Recorrido por el paisaje
Cuando decimos "Hadoop", solemos referirnos realmente a Hadoop y a todos los proyectos y marcos de ingeniería de datos que se han construido a su alrededor. En esta sección, repasamos brevemente algunas tecnologías clave, clasificadas por casos de uso. No podemos abarcar todos los marcos de trabajo en detalle -en muchos casos tienen sus propios tratamientos completos a nivel de libro-, pero intentamos dar una idea de lo que hacen. Puedes saltarte esta sección si ya estás familiarizado con estas tecnologías, o puedes utilizarla como una práctica referencia rápida para recordar los fundamentos.
El zoo de marcos, y cómo se relacionan y dependen unos de otros, puede parecer desalentador al principio, pero con un poco de familiarización, las relaciones se hacen más claras. Es posible que hayas visto representaciones similares a la Figura 1-2, que intentan mostrar cómo los distintos componentes se construyen unos sobre otros. Estos diagramas pueden ser una ayuda útil para la comprensión, pero no siempre dejan claras todas las dependencias entre proyectos. Los proyectos dependen unos de otros de diferentes maneras, pero podemos pensar en dos tipos principales de dependencia: de datos y de control. En el plano de datos, un componente depende de otro al leer y escribir datos, mientras que en el plano de control, un componente depende de otro para los metadatos o la coordinación. Para los inclinados a los gráficos, algunas de estas relaciones se muestran en la Figura 1-3. Que no cunda el pánico; esto no pretende asustar, y en esta fase no es fundamental que entiendas exactamente cómo funcionan las dependencias entre los componentes. Pero los gráficos demuestran la importancia de desarrollar una comprensión básica de la finalidad de cada elemento de la pila. El objetivo de esta sección es darte ese contexto.
Nota
Cuando existen varias tecnologías con un diseño, una arquitectura y un caso de uso similares, sólo cubrimos una, pero nos esforzamos por señalar las alternativas en la medida de lo posible, ya sea en el texto o en las secciones "Considéralo también".
Componentes básicos
El primer conjunto de proyectos son los que forman el núcleo del propio proyecto Hadoop o son tecnologías facilitadoras clave para el resto de la pila: HDFS, YARN, Apache ZooKeeper y Apache Hive Metastore. Juntos, estos proyectos forman la base de la que dependen la mayoría de los demás marcos, proyectos y aplicaciones que se ejecutan en el clúster.
HDFS
El Sistema de Archivos Distribuidos Hadoop (HDFS) es el sistema de archivos escalable, tolerante a fallos y distribuido de Hadoop. Basado en el caso de uso original de análisis sobre conjuntos de datos a gran escala, HDFS está optimizado para almacenar cantidades muy grandes de datos inmutables con archivos a los que se suele acceder en largos escaneos secuenciales. HDFS es la tecnología de apoyo fundamental para muchos de los demás componentes de la pila.
Al almacenar datos, HDFS divide un archivo en bloques de tamaño configurable, normalmente algo como 128 MiB, y almacena una réplica de cada bloque en varios servidores para aumentar la resistencia y el paralelismo de los datos. Cada nodo trabajador del clúster ejecuta un demonio llamado DataNode que acepta nuevos bloques y los persiste en sus discos locales. El DataNode también es responsable de servir los datos a los clientes. El DataNode sólo conoce los bloques y sus ID; no sabe a qué archivo pertenece una réplica concreta. Esta información es curada por un proceso de coordinación, el NameNode, que se ejecuta en los servidores maestros y es responsable de mantener una asignación de archivos a los bloques, así como metadatos sobre los propios archivos (cosas como nombres, permisos, atributos y factor de replicación).
Los clientes que deseen almacenar bloques deben comunicarse primero con el NodoNombre para recibir una lista de NodosDatos en los que escribir cada bloque. El cliente escribe en el primer Nodo de Datos, que a su vez transmite los datos al siguiente Nodo de Datos, y así sucesivamente en una cadena. Al proporcionar una lista de DataNodes para el canal, el NameNode tiene en cuenta una serie de cosas, como el espacio disponible en el DataNode y la ubicación del nodo -su localidad de rack-. El NameNode se asegura contra los fallos de los nodos y los bastidores, asegurándose de que cada bloque esté en al menos dos bastidores diferentes. En la Figura 1-4, un cliente escribe un archivo formado por tres bloques en HDFS, y el proceso distribuye y replica los datos en los DataNodes.
Del mismo modo, al leer datos, el cliente pide al NodoNombre una lista de los N odosDatos que contienen los bloques de los archivos que necesita. A continuación, el cliente lee los datos directamente de los DataNodes, prefiriendo las réplicas que sean locales o cercanas, en términos de red.
El diseño de HDFS implica que no permite actualizaciones in situ de los archivos que almacena. Esto puede parecer inicialmente bastante restrictivo hasta que te das cuenta de que esta inmutabilidad le permite conseguir la escalabilidad horizontal y la resistencia necesarias de una forma relativamente sencilla.
HDFS es tolerante a fallos porque el fallo de un disco individual, de un DataNode o incluso de un rack no pone en peligro la seguridad de los datos. En estas situaciones, el NameNode simplemente indica a uno de los DataNodes que mantiene una réplica superviviente que copie el bloque a otro DataNode hasta que se reafirme el factor de replicación necesario. Los clientes que leen datos son dirigidos a una de las réplicas restantes. Como tal, todo el sistema es autorreparable, siempre que permitamos suficiente capacidad y redundancia en el propio clúster.
HDFS es escalable, dado que podemos aumentar simplemente la capacidad del sistema de archivos añadiendo más DataNodes con almacenamiento local. Esto también tiene el agradable efecto secundario de aumentar el rendimiento de lectura y escritura disponible para HDFS en su conjunto.
Sin embargo, es importante señalar que HDFS no consigue esta resistencia y escalabilidad por sí solo. Tenemos que utilizar los servidores adecuados y diseñar la disposición de nuestros clústeres para aprovechar las características de resistencia y escalabilidad que ofrece HDFS y, en gran parte, de eso trata este libro. En el Capítulo 3, analizamos en detalle cómo HDFS interactúa con los servidores en los que se ejecutan sus demonios y cómo utiliza los discos conectados localmente en estos servidores. En el Capítulo 4, examinamos las opciones a la hora de elaborar un plan de red, y en el Capítulo 12, cubrimos cómo hacer que HDFS esté tan altamente disponible y sea tan tolerante a fallos como sea posible.
Una nota final antes de continuar. En esta breve descripción de HDFS, hemos pasado por alto el hecho de que Hadoop abstrae gran parte de estos detalles del cliente. La API que utiliza un cliente es en realidad un sistema de archivos compatible con Hadoop, del que HDFS es sólo una implementación. En este libro nos encontraremos con otras implementaciones de uso común, como las ofertas de almacenamiento de objetos basadas en la nube, como Amazon S3.
YARN
Aunque es útil poder almacenar datos de forma escalable y resistente, lo que realmente queremos es poder obtener información de esos datos. Para ello, necesitamos poder calcular cosas a partir de los datos, de forma que puedan escalarse a los volúmenes que esperamos almacenar en nuestro sistema de archivos Hadoop. Y lo que es más, necesitamos poder ejecutar muchos cálculos distintos al mismo tiempo, haciendo un uso eficiente de los recursos disponibles en todo el clúster y minimizando el esfuerzo necesario para acceder a los datos. Cada cálculo procesa diferentes volúmenes de datos y requiere diferentes cantidades de potencia de cálculo y memoria. Para gestionar estas demandas contrapuestas, necesitamos un gestor de clúster centralizado, que conozca todos los recursos informáticos disponibles y las demandas actuales de cargas de trabajo contrapuestas.
Esto es exactamente lo que YARN (Yet Another Resource Negotiator) está diseñado para ser. YARN ejecuta un demonio en cada nodo trabajador, llamado NodeManager, que informa a un proceso maestro, llamado ResourceManager. Cada NodeManager indica al ResourceManager cuántos recursos de cálculo (en forma de núcleos virtuales o vcores) y cuánta memoria hay disponible en su nodo. Los recursos se distribuyen entre las aplicaciones que se ejecutan en el clúster en forma de contenedores, cada uno de los cuales tiene una demanda de recursos definida: por ejemplo, 10 contenedores con 4 núcleos virtuales y 8 GB de RAM cada uno. Los Gestores de Nodos son responsables de iniciar y monitorizar los contenedores en sus nodos locales y de eliminarlos si superan sus asignaciones de recursos establecidas.
Una aplicación que necesite ejecutar cálculos en el clúster debe solicitar primero al Gestor de Recursos un contenedor único en el que ejecutar su propio proceso de coordinación, llamado ApplicationMaster (AM). A pesar de su nombre, el AM se ejecuta en realidad en una de las máquinas trabajadoras. Los Maestros de Aplicaciones de diferentes aplicaciones se ejecutarán en diferentes máquinas trabajadoras, garantizando así que el fallo de una sola máquina trabajadora afecte sólo a un subconjunto de las aplicaciones que se ejecutan en el clúster. Una vez que el MA se está ejecutando, solicita contenedores adicionales al Gestor de Recursos para ejecutar su cálculo real. Este proceso se esboza en la Figura 1-5: tres clientes ejecutan aplicaciones con distintas demandas de recursos, que se traducen en contenedores de distinto tamaño y se reparten entre los Gestores de Nodos para su ejecución.
El Gestor de Recursos ejecuta un hilo especial, que se encarga de programar las solicitudes de las aplicaciones y de garantizar que los contenedores se asignen equitativamente entre las aplicaciones y los usuarios que ejecutan aplicaciones en el clúster. Este programador se esfuerza por asignar los núcleos y la memoria de forma equitativa entre los inquilinos. Los inquilinos y las cargas de trabajo se dividen en grupos jerárquicos, cada uno de los cuales tiene una parte configurable de los recursos globales del clúster.
Debe quedar claro por la descripción que YARN en sí no realiza ningún cálculo, sino que es un marco para lanzar aplicaciones de este tipo distribuidas en un clúster. YARN proporciona un conjunto de API para crear estas aplicaciones; cubrimos dos de estas implementaciones, MapReduce y Apache Spark, en "Marcos computacionales".
Obtendrás más información sobre la alta disponibilidad de YARN en el capítulo 12.
Apache ZooKeeper
El problema del consenso es un tema importante en informática. Cuando una aplicación está distribuida en muchos nodos, una preocupación clave es conseguir que estos componentes dispares se pongan de acuerdo sobre los valores de algunos parámetros compartidos. Por ejemplo, para las estructuras con varios procesos maestros, ponerse de acuerdo sobre qué proceso debe ser el maestro activo y cuál debe estar en espera es fundamental para su correcto funcionamiento.
Apache ZooKeeper es el servicio de configuración resistente y distribuido del ecosistema Hadoop. En ZooKeeper, los datos de configuración se almacenan y se accede a ellos en un árbol de nodos similar a un sistema de archivos, llamados znodes, cada uno de los cuales puede contener datos y ser el padre de cero o más nodos hijos. Los clientes abren una conexión a un único servidor ZooKeeper para crear, leer, actualizar y eliminar los znodos.
Para mayor resistencia, las instancias de ZooKeeper deben desplegarse en diferentes servidores como un conjunto. Como ZooKeeper funciona por consenso mayoritario, se necesita un número impar de servidores para formar quórum. Aunque puede desplegarse un número par, el servidor adicional no proporciona resistencia adicional al conjunto. Cada servidor es idéntico en funcionalidad, pero uno del conjunto es elegido como nodo líder; todos los demás servidores son designados seguidores. ZooKeeper garantiza que las actualizaciones de datos sean aplicadas por la mayoría de los servidores ZooKeeper. Mientras la mayoría de los servidores estén en funcionamiento, el conjunto estará operativo. Los clientes pueden abrir conexiones con cualquiera de los servidores para realizar lecturas y escrituras, pero las escrituras se reenvían de los servidores seguidores al líder para garantizar la coherencia. ZooKeeper asegura que todo el estado sea coherente, garantizando que las actualizaciones se apliquen siempre en el mismo orden.
Consejo
En general, un quórum con n miembros puede sobrevivir hasta fallos floor((n-1)/2) y seguir funcionando. Así, un conjunto de cuatro miembros tiene las mismas propiedades de resistencia que un conjunto de tres miembros.
Como se indica en la Tabla 1-1, muchos marcos de trabajo del ecosistema dependen de ZooKeeper para mantener procesos maestros de alta disponibilidad, coordinar tareas, realizar un seguimiento del estado y establecer parámetros generales de configuración. Obtendrás más información sobre cómo utilizan ZooKeeper otros componentes para la alta disponibilidad en el Capítulo 12.
Proyecto | Uso de ZooKeeper |
---|---|
HDFS |
Coordinar la alta disponibilidad |
HBase |
Metadatos y coordinación |
Solr |
Metadatos y coordinación |
Kafka |
Metadatos y coordinación |
YARN |
Coordinar la alta disponibilidad |
Colmena |
Bloqueo de tablas y particiones y alta disponibilidad |
Apache Hive Metastore
Cubriremos la funcionalidad de consulta de Apache Hive en una sección posterior, cuando hablemos de los motores SQL analíticos, pero un componente del proyecto -el Metastore de Hive- es una tecnología de apoyo tan clave para otros componentes de la pila que necesitamos presentarlo al principio de este estudio.
El Metastore Hive conserva información sobre los conjuntos de datos estructurados (en contraposición a los datos binarios no estructurados) que residen en Hadoop y los organiza en una jerarquía lógica de bases de datos, tablas y vistas. Las tablas Hive tienen esquemas definidos, que se especifican durante la creación de la tabla. Estas tablas admiten la mayoría de los tipos de datos comunes que conoces del mundo de las bases de datos relacionales. Se espera que los datos subyacentes en el motor de almacenamiento coincidan con este esquema, pero en HDFS esto sólo se comprueba en tiempo de ejecución, un concepto comúnmente denominado esquema en lectura. Las tablas Hive pueden definirse para los datos de varios motores de almacenamiento, como Apache HBase y Apache Kudu, pero la ubicación más habitual es, con diferencia, HDFS.
En HDFS, las tablas Hive no son más que directorios que contienen archivos. Para tablas grandes, Hive admite la partición mediante subdirectorios dentro del directorio de la tabla, que a su vez pueden contener particiones anidadas, si es necesario. Dentro de una misma partición, o en una tabla no particionada, todos los archivos deben almacenarse en el mismo formato; por ejemplo, archivos de texto delimitados por comas o un formato binario como Parquet u ORC. El metastore permite definir las tablas como gestionadas o externas. Para las tablas gestionadas, Hive controla activamente los datos en el motor de almacenamiento: si se crea una tabla, Hive construye las estructuras en el motor de almacenamiento, por ejemplo creando directorios en HDFS. Si se elimina una tabla, Hive borra los datos del motor de almacenamiento. En el caso de las tablas externas, Hive no realiza modificaciones en el motor de almacenamiento subyacente en respuesta a los cambios en los metadatos, sino que se limita a mantener los metadatos de la tabla en su base de datos.
Otros proyectos, como Apache Impala y Apache Spark, confían en el Hive Metastore como única fuente de verdad para los metadatos sobre conjuntos de datos estructurados dentro del clúster. Como tal, es un componente crítico en cualquier Implementación.
Profundizar
Hay algunos libros muy buenos sobre el núcleo del ecosistema Hadoop, que merece la pena leer para comprenderlo a fondo. En particular, consulta
-
Hadoop: La Guía Definitiva, 4ª Edición, de Tom White (O'Reilly)
-
ZooKeeper, por Benjamin Reed y Flavio Junqueira (O'Reilly)
-
Programando Hive, por Dean Wampler, Jason Rutherglen y Edward Capriolo (O'Reilly)
Marcos computacionales
Con los componentes básicos de Hadoop, tenemos datos almacenados en HDFS y un medio para ejecutar aplicaciones distribuidas a través de YARN. Han surgido muchos marcos que permiten a los usuarios definir y componer cálculos arbitrarios y que estos cálculos se dividan en trozos más pequeños y se ejecuten de forma distribuida. Veamos dos de los principales marcos.
Hadoop MapReduce
MapReduce es la aplicación original para la que se construyó Hadoop, y es una implementación basada en Java del modelo establecido en el documento MapReduce de Google. Originalmente, era un marco independiente que se ejecutaba en el clúster, pero posteriormente fue portado a YARN a medida que el proyecto Hadoop evolucionaba para dar soporte a más aplicaciones y casos de uso. Aunque ha sido sustituido por motores más recientes, como Apache Spark y Apache Flink, sigue mereciendo la pena comprenderlo, dado que muchos marcos de nivel superior compilan sus entradas en trabajos MapReduce para su ejecución. Entre ellos se incluyen:
-
Colmena Apache
-
Apache Sqoop
-
Apache Oozie
-
Cerdo Apache
Nota
Los términos map y reduce están tomados de la programación funcional, en la que un map aplica una función de transformación a cada elemento de una colección, y un reduce aplica una función de agregación a cada elemento de una lista, combinándolos en menos valores resumidos.
Esencialmente, MapReduce divide un cálculo en tres fases secuenciales: map, shuffle y reduce. En la fase de mapa, los datos relevantes se leen del HDFS y se procesan en paralelo por múltiples tareas de mapa independientes. Estas tareas deberían ejecutarse idealmente dondequiera que se encuentren los datos -normalmente, nuestro objetivo es una tarea de mapa por bloque HDFS-. El usuario define una función map()
(en código) que procesa cada registro del archivo y produce salidas clave-valor listas para la siguiente fase. En la fase de mezcla, MapReduce obtiene los resultados del mapa y los envía a través de la red para que sirvan de entrada a las tareas de reducción. Una función reduce()
definida por el usuario recibe sucesivamente todos los valores de una clave y los agrega o combina en menos valores que resumen las entradas. Lo esencial del proceso se muestra en la Figura 1-6. En el ejemplo, los mapeadores leen los archivos del HDFS y los barajan por clave según una columna ID. Los reductores agregan las columnas restantes y escriben los resultados de nuevo en HDFS.
Las secuencias de estas tres sencillas fases lineales pueden componerse y combinarse esencialmente en cualquier cálculo de complejidad arbitraria; por ejemplo, transformaciones avanzadas, uniones, agregaciones, etc. A veces, para transformaciones simples que no requieren agregaciones, la fase de reducción no es necesaria en absoluto. Normalmente, las salidas de un trabajo MapReduce se almacenan de nuevo en HDFS, donde pueden formar las entradas de otros trabajos.
A pesar de su simplicidad, MapReduce es increíblemente potente y extremadamente robusto y escalable. Sin embargo, tiene un par de inconvenientes. En primer lugar, es bastante complicado desde el punto de vista del usuario, que tiene que codificar y compilar las funciones map()
y reduce()
en Java, lo cual es un listón demasiado alto para muchos analistas: componer complejas cadenas de procesamiento en MapReduce puede ser una tarea desalentadora. En segundo lugar, MapReduce en sí no es especialmente eficiente. Hace mucha E/S basada en disco, lo que puede resultar caro cuando se combinan etapas de procesamiento o se realizan operaciones iterativas. Los pipelines multietapa se componen de trabajos MapReduce individuales con una barrera de E/S HDFS entre ellos, sin reconocer las optimizaciones potenciales en todo el gráfico de procesamiento.
Debido a estos inconvenientes, se han desarrollado varios sucesores de MapReduce que pretenden tanto simplificar el desarrollo como hacer más eficientes las cadenas de procesamiento. A pesar de ello, los fundamentos conceptuales de MapReduce -que el procesamiento de datos debe dividirse en múltiples tareas independientes que se ejecutan en diferentes máquinas (mapas), cuyos resultados se barajan, agrupan y cotejan en otro conjunto de máquinas (reducciones)- son fundamentales para todos los motores de procesamiento de datos distribuidos, incluidos los marcos basados en SQL. Apache Spark, Apache Flink y Apache Impala, aunque bastante diferentes en sus especificidades, son esencialmente diferentes implementaciones de este concepto.
Apache Spark
Apache Spark es un marco de computación distribuida, con énfasis en la eficiencia y la facilidad de uso, que admite tanto el cálculo por lotes como el streaming. En lugar de que el usuario tenga que expresar las manipulaciones de datos necesarias en términos de funciones puras de map()
y reduce()
como en MapReduce, Spark expone una rica API de operaciones comunes, como filtrar, unir, agrupar y agregar directamente sobre Conjuntos de Datos, que comprenden filas todas ellas adheridas a un tipo o esquema particular. Además de utilizar los métodos de la API, los usuarios pueden enviar operaciones directamente utilizando un dialecto de estilo SQL (de ahí el nombre general de este conjunto de funciones, Spark SQL), eliminando gran parte de la necesidad de componer canalizaciones mediante programación. Con su API, Spark hace que la tarea de componer complejas cadenas de procesamiento sea mucho más fácil para el usuario. Como ejemplo sencillo, en la Figura 1-7, se leen tres conjuntos de datos. Dos de ellos se unen y se juntan con un tercer conjunto de datos filtrado. El resultado se agrupa según una columna y se agrega y escribe en una salida. Las fuentes y los sumideros de los conjuntos de datos podrían funcionar por lotes y utilizar HDFS o Kudu, o podrían procesarse en un flujo hacia y desde Kafka.
Una característica clave de las operaciones sobre conjuntos de datos es que los grafos de procesamiento se ejecutan a través de un optimizador de consultas estándar antes de la ejecución, muy similar a los que se encuentran en las bases de datos relacionales o en los motores de consultas de procesamiento paralelo masivo. Este optimizador puede reorganizar, combinar y podar el grafo de procesamiento para obtener el canal de ejecución más eficiente. De este modo, el motor de ejecución puede operar sobre conjuntos de datos de un modo mucho más eficiente, evitando gran parte de la E/S intermedia de la que adolece MapReduce.
Uno de los principales objetivos de diseño de Spark era aprovechar al máximo la memoria de los nodos de los trabajadores, que está disponible en cantidades cada vez mayores en los servidores básicos. La capacidad de almacenar y recuperar datos de la memoria principal a velocidades que son órdenes de magnitud más rápidas que las de los discos giratorios hace que determinadas cargas de trabajo sean radicalmente más eficientes. En particular, las cargas de trabajo de aprendizaje automático distribuido, que a menudo operan con los mismos conjuntos de datos de forma iterativa, pueden obtener enormes beneficios en los tiempos de ejecución con respecto a la ejecución equivalente de MapReduce. Spark permite que los conjuntos de datos se almacenen en caché en la memoria de los ejecutores; si los datos no caben por completo en la memoria, las particiones que no pueden almacenarse en caché se vuelcan al disco o se vuelven a calcular de forma transparente en tiempo de ejecución.
Spark implementa el procesamiento en flujo como una serie de microlotes periódicos de conjuntos de datos. Este enfoque sólo requiere pequeñas diferencias de código en las transformaciones y acciones aplicadas a los datos -esencialmente, se puede utilizar el mismo código o uno muy similar tanto en el modo por lotes como en el de flujo-. Un inconveniente del enfoque de microlotes es que un evento tarda al menos el intervalo entre lotes en procesarse, por lo que no es adecuado para casos de uso que requieran latencias de milisegundos. Sin embargo, esta debilidad potencial es también una fortaleza, porque el microbatching permite un rendimiento de datos mucho mayor que el que se puede conseguir procesando los eventos uno a uno. En general, hay relativamente pocos casos de uso de streaming que requieran realmente tiempos de respuesta de subsegundos. Sin embargo, la funcionalidad de streaming estructurado de Spark promete aportar muchas de las ventajas de un gráfico optimizado de cálculo por lotes de Spark a un contexto de streaming, así como un modo de streaming continuo de baja latencia.
Spark incluye una serie de bibliotecas y API integradas para el aprendizaje automático. Spark MLlib permite que el proceso de creación de un modelo de aprendizaje automático (preparación de datos, limpieza, extracción de características y ejecución de algoritmos) se componga en una canalización distribuida. No todos los algoritmos de aprendizaje automático pueden ejecutarse automáticamente de forma distribuida, por lo que Spark incluye algunas implementaciones de clases comunes de problemas, como agrupamiento, clasificación y regresión, y filtrado colaborativo.
Spark es un marco extraordinariamente potente para el procesamiento de datos, y a menudo es (con razón) la elección de facto cuando se crean nuevos casos de uso de procesamiento por lotes, aprendizaje automático y streaming. Sin embargo, no es la única opción; los arquitectos de aplicaciones también deben considerar opciones como Apache Flink para el procesamiento por lotes y de flujo, y Apache Impala (ver "Apache Impala") para SQL interactivo.
Profundizar
Una vez más, Hadoop: The Definitive Guide, de Tom White, es el mejor recurso para aprender más sobre Hadoop MapReduce. Para Spark, hay algunas buenas referencias en :
-
Spark: La guía definitiva, de Bill Chambers y Matei Zaharia (O'Reilly)
-
Spark de alto rendimiento, de Holden Karau y Rachel Warren (O'Reilly)
Motores SQL analíticos
Aunque MapReduce y Spark son marcos de trabajo extremadamente flexibles y potentes, para utilizarlos necesitas estar cómodo programando en un lenguaje como Java, Scala o Python, y deberías sentirte feliz implementando y ejecutando código desde la línea de comandos. La realidad es que, en la mayoría de las empresas, SQL sigue siendo la lingua franca de la analítica, y la base de conocimientos más amplia y accesible se encuentra ahí. A veces, necesitas hacer las cosas sin tener que codificar, compilar, implementar y ejecutar una aplicación completa. Además, un gran número de herramientas de apoyo a la toma de decisiones y de inteligencia empresarial interactúan con almacenes de datos exclusivamente a través de SQL. Por estas razones, se ha dedicado mucho tiempo y esfuerzo a desarrollar interfaces similares a SQL para los datos estructurados almacenados en Hadoop. Muchas de ellas utilizan MapReduce o Spark como mecanismo de cálculo subyacente, pero algunas son motores de cálculo por derecho propio. Cada motor se centra en la consulta de datos que ya existen en el motor de almacenamiento o en la inserción masiva de nuevos datos en dichos motores. Están diseñados para el análisis a gran escala y no para el procesamiento transaccional a pequeña escala. Veamos los principales actores.
Colmena Apache
Apache Hive es la tecnología original de almacenamiento de datos para Hadoop. Se desarrolló en Facebook y fue la primera en ofrecer un lenguaje similar a SQL, llamado HiveQL, para permitir a los analistas consultar datos estructurados almacenados en HDFS sin tener que compilar e implementar primero el código. Hive admite conceptos de consulta SQL comunes, como uniones de tablas, uniones, subconsultas y vistas. A alto nivel, Hive analiza una consulta de usuario, la optimiza y la compila en uno o más cálculos por lotes encadenados, que ejecuta en el clúster. Normalmente, estos cálculos se ejecutan como trabajos MapReduce, pero Hive también puede utilizar Apache Tez y Spark como motor de ejecución de respaldo. Hive tiene dos componentes principales: un servidor de metadatos y un servidor de consultas. Ya hemos tratado antes el Metastore de Hive, así que en esta sección nos centraremos en la funcionalidad de consulta.
Los usuarios que deseen ejecutar consultas SQL lo hacen a través del servidor de consultas, llamado HiveServer2 (HS2). Los usuarios abren sesiones con el servidor de consultas y envían consultas en el dialecto HQL. Hive analiza estas consultas, las optimiza en la medida de lo posible y las compila en uno o varios trabajos por lotes. Las consultas que contienen subconsultas se compilan en trabajos multietapa, con datos intermedios de cada etapa almacenados en una ubicación temporal en HDFS. HS2 admite varias sesiones de usuario simultáneas y garantiza la coherencia mediante bloqueos compartidos o exclusivos en ZooKeeper. El analizador y compilador de consultas utiliza un optimizador basado en costes para construir un plan de consultas y puede utilizar estadísticas de tablas y columnas (que también se almacenan en el metastore) para elegir la estrategia adecuada al unir tablas. Hive puede leer multitud de formatos de archivo a través de sus bibliotecas de serialización y deserialización incorporadas (llamadas SerDes) y también puede ampliarse con formatos personalizados.
La Figura 1-8 muestra una vista de alto nivel del funcionamiento de Hive. Un cliente envía consultas a una instancia de HiveServer2 como parte de una sesión de usuario. HiveServer2 recupera la información de las bases de datos y las tablas de las consultas del Hive Metastore. A continuación, las consultas se optimizan y compilan en secuencias de trabajos (J) en MapReduce, Tez o Spark. Una vez completados los trabajos, los resultados se devuelven al cliente remoto a través de HiveServer2.
En general, no se considera que Hive sea un motor de consulta interactivo (aunque recientemente se han introducido mejoras de velocidad mediante procesos de larga duración que empiezan a acercarlo a este ámbito). Muchas consultas dan lugar a cadenas de trabajos MapReduce que pueden tardar muchos minutos (o incluso horas) en completarse. Por tanto, Hive es ideal para trabajos por lotes fuera de línea para operaciones de extracción, transformación y carga (ETL); elaboración de informes; u otras manipulaciones de datos a granel. Los flujos de trabajo basados en Hive son un elemento básico de confianza de los clústeres de big data y, en general, son extremadamente robustos. Aunque Spark SQL está ganando cada vez más adeptos, Hive sigue siendo -y seguirá siendo- una herramienta esencial en el conjunto de herramientas de big data.
Volveremos a encontrarnos con Hive cuando hablemos de cómo implementarlo para alta disponibilidad en el Capítulo 12.
Profundizar
Mucha información sobre Hive está contenida en entradas de blog y otros artículos repartidos por la web, pero hay algunas buenas referencias:
-
La wiki de Apache Hive (contiene mucha información útil, incluida la referencia del lenguaje HQL)
-
Programando Hive, por Dean Wampler, Jason Rutherglen y Edward Capriolo (O'Reilly)
Nota
Aunque lo tratamos en "Marcos computacionales", Spark también es un actor clave en el espacio SQL analítico. La funcionalidad SQL de Spark admite una amplia gama de cargas de trabajo, tanto para ETL como para informes, y también puede desempeñar un papel en casos de uso de consultas interactivas. Para nuevas implementaciones de cargas de trabajo SQL por lotes, Spark debería considerarse probablemente el punto de partida por defecto.
Apache Impala
Apache Impala es un motor de procesamiento paralelo masivo (MPP) diseñado para soportar consultas SQL rápidas e interactivas sobre conjuntos de datos masivos en Hadoop o almacenamiento en la nube. Su principal objetivo de diseño es permitir que múltiples consultas concurrentes, ad hoc, de tipo informe, que cubran terabytes de datos, se completen en pocos segundos. Su objetivo es ayudar a los analistas que deseen ejecutar sus propias consultas SQL, directamente o a través de herramientas de inteligencia empresarial (BI) con interfaz de usuario.
A diferencia de Hive o Spark SQL, Impala no convierte las consultas en trabajos por lotes para ejecutarlos en YARN. En su lugar, es un servicio independiente, implementado en C++, con sus propios procesos de trabajo que ejecutan consultas, los demonios Impala. A diferencia de Hive, no existe un servidor de consultas centralizado; cada demonio Impala puede aceptar consultas de los usuarios y actúa como nodo coordinador de la consulta. Los usuarios pueden enviar consultas a través de JDBC u ODBC, mediante una interfaz de usuario como Hue, o a través del shell de línea de comandos suministrado. Las consultas enviadas se compilan en un plan de consulta distribuido. Este plan es un árbol de operadores dividido en fragmentos. Cada fragmento es un grupo de nodos del plan en el árbol que pueden ejecutarse juntos. El demonio envía diferentes instancias de los fragmentos del plan a los demonios del cluster para que los ejecuten contra sus datos locales, donde se ejecutan en uno o más hilos dentro del proceso del demonio.
Debido a su enfoque en la velocidad y la eficiencia, Impala utiliza un modelo de ejecución diferente, en el que los datos se transmiten desde su origen a través de un árbol de operadores distribuidos. Las filas leídas por los nodos de exploración son procesadas por instancias de fragmentos y transmitidas a otras instancias, que pueden encargarse de unir, agrupar o agregar mediante operadores de intercambio. Los resultados finales de las instancias de fragmentos distribuidos se envían al demonio coordinador, que ejecuta las agregaciones finales antes de informar al usuario de que hay resultados que obtener.
El proceso de consulta se describe en la Figura 1-9. Un cliente elige un servidor demonio Impala al que enviar su consulta. Este nodo coordinador compila y optimiza la consulta en fragmentos de ejecución remota que se envían a los demás demonios del cluster (inicialización de la consulta). Los demonios ejecutan los operadores en los fragmentos e intercambian filas entre sí según sea necesario (ejecución distribuida). A medida que están disponibles, transmiten los resultados al coordinador, que puede realizar agregaciones y cálculos finales antes de transmitirlos al cliente.
Impala puede leer datos de una amplia gama de fuentes de datos, incluidos archivos de texto, tablas HBase y Avro, pero su formato preferido en disco es Parquet. Impala puede aprovechar el hecho de que los archivos Parquet almacenan los datos por columnas para limitar los datos leídos del disco sólo a las columnas a las que se hace referencia en la propia consulta. Impala también utiliza el pushdown de predicados para filtrar filas justo en el momento en que se leen. Actualmente Impala puede leer datos almacenados en HDFS, Apache HBase, Apache Kudu, Amazon S3 y Microsoft Azure Data Lake Store (ADLS).
Profundizar
Para más detalles sobre Impala, recomendamos las siguientes fuentes:
-
Primeros pasos con Impala, de John Russell (O'Reilly)
Considera también
Existen muchos más marcos analíticos. Algunos otros motores de consulta distribuida basados en SQL que sin duda debes tener en cuenta y considerar para tus casos de uso son:
-
Apache Phoenix (basado en Apache HBase, del que se habla en la siguiente sección)
Motores de almacenamiento
El motor de almacenamiento original de en el ecosistema Hadoop es HDFS, que destaca en el almacenamiento de grandes cantidades de datos sólo apilados a los que se accede en exploraciones secuenciales. Pero, ¿qué ocurre con otros patrones de acceso, como la recuperación y actualización aleatoria de registros? ¿Y la búsqueda de documentos? Muchas cargas de trabajo tratan con conjuntos de datos grandes y variados, pero no son de naturaleza analítica. Para atender a estos diferentes casos de uso, se han desarrollado o adaptado algunos proyectos para su uso con Hadoop.
Apache HBase
El deseo de algunas de las primeras empresas web de almacenar decenas de miles de millones a billones de registros y permitir su recuperación y actualización eficientes condujo al desarrollo de Apache HBase, un almacén de valores clave semiestructurado y de acceso aleatorio que utiliza HDFS como almacén persistente. Como ocurre con muchos de los proyectos Hadoop, el proyecto original del marco procedía de un documento publicado por Google en el que se describía su sistema Bigtable. Esencialmente, HBase proporciona un medio por el cual una carga de trabajo de lectura/escritura de acceso aleatorio (que es muy ineficiente para HDFS) se convierte en E/S secuencial (en la que HDFS destaca).
HBase no es un almacén relacional. En su lugar, almacena pares clave-valor semiestructurados, denominados celdas, en una tabla distribuida. HBase subdivide la clave de celda en una jerarquía de componentes para permitir que las celdas relacionadas se almacenen juntas y se acceda a ellas de forma eficiente. La primera parte de la clave se denomina clave de fila, y define una agrupación lógica de celdas, denominada fila. A continuación, la clave se subdivide en familias de columnas, que también representan una agrupación de celdas. Las familias de columnas se almacenan por separado en memoria y en disco, y no suele haber más de un puñado por tabla. Las familias de columnas son la única parte del esquema de claves que hay que definir cuando se crea la tabla. Dentro de una familia de columnas hay otra subdivisión, llamada calificador de columna, de la que puede haber millones o más por fila. Por último, cada celda tiene una marca de tiempo que define una versión. Varias celdas con diferentes marcas de tiempo, pero por lo demás la misma clave, pueden almacenarse como versiones diferentes. HBase trata cada componente de la clave (aparte de la marca de tiempo) y el valor como matrices de bytes. Como resultado, HBase no impone ninguna restricción ni tiene conocimiento de los tipos de ninguna parte de la celda, lo que lo convierte en un almacén semiestructurado.
En HBase, las celdas se almacenan ordenadas según sus componentes clave. Se ordenan primero por su clave de fila y luego por familia de columnas, calificador de columna y, por último, por marca de tiempo. HBase emplea particionamiento horizontal, es decir, las celdas de una tabla se dividen en particiones, que se distribuyen por el clúster. El espacio de las claves de fila de una tabla se divide en particiones llamadas regiones, cada una responsable de un rango no solapado de las claves de fila ordenadas. Los límites entre regiones se denominan divisiones de regiones. Por ejemplo, si sabes que tus filas tendrán claves de fila con un prefijo alfabético aleatorio, podrías crear tu tabla inicialmente con 26 regiones con divisiones en b
, c
, d
, ...
, v
, w
, x
, y
, z
. Cualquier clave que empiece por a
irá en la primera región, c
en la tercera y z
en la última. Las nuevas divisiones pueden añadirse manualmente o pueden ser creadas automáticamente por HBase para las regiones ocupadas. De este modo, una tabla puede distribuirse y escalarse fácilmente.
La curva de aprendizaje de los aspectos operativos de HBase puede ser empinada, y no es necesariamente para los débiles de corazón. Conseguir el diseño adecuado para la tabla y las claves de celda es absolutamente crítico para el rendimiento de tu caso de uso y patrón de acceso dados. Diseñar la disposición correcta de la tabla requiere un sólido conocimiento práctico de cómo funciona HBase, o es probable que acabes teniendo comportamientos patológicos, como escaneos de toda la tabla, hotspotting de regiones o tormentas de compactación. HBase sobresale en el servicio de cargas de trabajo de E/S aleatorias: solicitudes de escritura o lectura bien distribuidas para grupos relativamente pequeños de celdas, a través de escaneos de filas o de rangos. No es tan bueno soportando escaneos mucho mayores, como los típicos de las cargas de trabajo analíticas. Éstos son caros de ejecutar y devolver al cliente. Tales cargas de trabajo suelen realizarse mucho mejor directamente contra los propios archivos HDFS.
Si se gestiona bien y se utiliza correctamente, HBase es una de las herramientas más valiosas del ecosistema y puede ofrecer un rendimiento rapidísimo en enormes conjuntos de datos. Es absolutamente necesario utilizarlo, pero asegúrate de que lo haces para lo correcto y de la forma adecuada.
Profundizar
Hay algunas referencias de lectura obligada si te tomas en serio el uso o la ejecución de HBase:
-
HBase: La guía definitiva, 2ª edición, de Lars George (O'Reilly)
-
Architecting HBase Applications, de Jean-Marc Spaggiari y Kevin O'Dell (O'Reilly)
Kudu apache
Uno de los principales puntos débiles de la arquitectura tradicional basada en Hadoop es que, para soportar tanto análisis eficientes de alto rendimiento como lecturas de acceso aleatorio de baja latencia sobre los mismos datos, deben utilizarse varios motores de almacenamiento. Esto da lugar a conductos de ingestión y orquestación relativamente complejos. Estos casos de uso requieren algo como HBase o Accumulo para dar servicio a las consultas de acceso aleatorio, junto con una combinación de HDFS, Parquet e Impala, Spark SQL o Hive para las cargas de trabajo analíticas.
Si los datos entrantes incluyen actualizaciones de filas existentes, el panorama se complica aún más, ya que puede requerir reescrituras al por mayor de los datos en HDFS o consultas complejas basadas en la aplicación de los últimos deltas. Conscientes de ello, los creadores de Kudu se propusieron crear un motor de almacenamiento y consulta que pudiera satisfacer ambos patrones de acceso (acceso aleatorio y escaneos secuenciales) y permitir eficazmente las actualizaciones de los datos existentes. Naturalmente, para permitir esto, es inevitable hacer algunas concesiones en cuanto al rendimiento, pero el objetivo es acercarse a los niveles de rendimiento de cada una de las tecnologías nativas, es decir, dar servicio a lecturas de acceso aleatorio en decenas de milisegundos y realizar escaneos de archivos a cientos de MiB/s.
Kudu es un almacén de datos estructurado que almacena filas con columnas tipificadas en tablas con un esquema predefinido. Un subconjunto de las columnas se designa como clave principal de la tabla y forma un índice en la tabla mediante el cual Kudu puede realizar búsquedas de filas. Kudu admite las siguientes operaciones de escritura: insertar, actualizar, upsert (insertar si la fila no existe, o actualizar si existe) y eliminar. En cuanto a la lectura, los clientes pueden construir una exploración con proyecciones de columnas y filtrar filas mediante predicados basados en los valores de las columnas.
Kudu distribuye las tablas por el clúster mediante particionamiento horizontal. Una tabla se divide en pastillas mediante uno de los dos mecanismos de partición, o una combinación de ambos. Una fila sólo puede estar en una tabla, y dentro de cada tabla, Kudu mantiene un índice ordenado de las columnas de clave primaria. El primer mecanismo de partición es la partición por rango y debería resultar familiar a los usuarios de HBase y Accumulo. Cada tabla tiene un límite superior e inferior dentro del rango, y todas las filas con claves de partición que se ordenan dentro del rango pertenecen a la tabla.
El segundo mecanismo de partición es la partición hash. Los usuarios pueden especificar un número fijo de cubos de hash por los que se particionará la tabla y pueden elegir una o varias columnas de la fila que se utilizarán para calcular el hash de cada fila. Para cada fila, Kudu calcula el hash de las columnas módulo al número de cubos y asigna la fila a un cubo en consecuencia.
Los dos mecanismos de particionado pueden combinarse para proporcionar múltiples niveles de particionado, con cero o más niveles de particionado hash (cada uno de los cuales hace hash de un conjunto diferente de columnas) y una partición final opcional de rango. El particionamiento multinivel es extremadamente útil para determinados casos de uso que, de otro modo, estarían sujetos a hotspotting de escritura. Por ejemplo, las series temporales siempre escriben en el final de un rango, que será sólo una tabla si sólo se utiliza el particionado de rango. Añadiendo una partición hash en una columna sensible, las escrituras pueden repartirse uniformemente entre todas las tabletas de la tabla y ésta puede escalarse dividiendo cada cubo hash por rango.
Con todos los motores de almacenamiento y consulta, elegir el esquema y la distribución de tablas adecuados es importante para un funcionamiento eficaz. Kudu no es diferente, y los profesionales tendrán que familiarizarse con las ventajas y desventajas inherentes a los distintos esquemas de filas y estrategias de partición para elegir la combinación óptima para el caso de uso en cuestión. Los casos de uso habituales de Kudu son:
-
Grandes series temporales métricas, como las que se ven en los conjuntos de datos IoT
-
Cargas de trabajo de elaboración de informes sobre conjuntos de datos mutables a gran escala, como análisis de tipo OLAP contra tablas de esquema en estrella
Profundizar
El mejor lugar para empezar a aprender más sobre Kudu es la documentación oficial del proyecto. Otros recursos que merece la pena leer son
-
"Kudu: Storage for Fast Analytics on Fast Data", de Todd Lipcon et al. (el documento original que describe el diseño y el funcionamiento de Kudu).
-
Getting Started with Kudu, de Jean-Marc Spaggiari et al. (O'Reilly)
Apache Solr
A veces SQL no es suficiente. Algunas aplicaciones necesitan la capacidad de realizar búsquedas más flexibles en datos no estructurados o semiestructurados. Muchos casos de uso, como la búsqueda de registros, bóvedas de documentos y análisis de ciberseguridad, pueden implicar la recuperación de datos mediante búsqueda de texto libre, búsqueda difusa, búsqueda facetada, coincidencia de fonemas, coincidencia de sinónimos, búsqueda geoespacial, etc. Para estos requisitos, a menudo denominados búsqueda empresarial, necesitamos la capacidad de procesar, analizar, indexar y consultar automáticamente miles de millones de documentos y cientos de terabytes de datos. Actualmente existen dos tecnologías principales en el ecosistema: Apache Solr y Elasticsearch. Aquí sólo cubrimos Apache Solr, pero Elasticsearch también es una gran opción para implementaciones de producción. Merece la pena investigar ambas cuidadosamente para tu caso de uso de búsqueda empresarial.
Para apoyar sus capacidades de búsqueda, Solr utiliza índices invertidos suministrados por Apache Lucene, que son simplemente mapas de términos a una lista de documentos coincidentes. Los términos pueden ser palabras, tallos, rangos, números, coordenadas y más. Los documentos contienen campos, que definen el tipo de términos que se encuentran en ellos. Los campos pueden dividirse en tokens individuales e indexarse por separado. Los campos que contiene un documento se definen en un esquema.
El procesamiento de indexación y la estructura de almacenamiento permiten recuperar rápidamente documentos clasificados, y una serie de analizadores avanzados de consultas pueden realizar coincidencias exactas, difusas, de expresiones regulares, etc. Para una consulta determinada, un buscador de índices recupera los documentos que coinciden con los predicados de la consulta. Los documentos se puntúan y, opcionalmente, se ordenan según determinados criterios; por defecto, se devuelven primero los documentos con la puntuación más alta.
En esencia, Solr envuelve la biblioteca Lucene en un servicio RESTful, que proporciona gestión de índices y conductos de consulta e indexación flexibles y componibles. A través de la funcionalidad SolrCloud, un índice lógico puede distribuirse entre muchas máquinas para el procesamiento escalable de consultas y la indexación. Además, Solr puede almacenar sus archivos de índices en HDFS para una mayor resistencia y un almacenamiento escalable.
Solr almacena los documentos en colecciones. Las colecciones pueden crearse con un esquema predefinido, en el que los campos y sus tipos son fijados por el usuario. Para casos de uso que traten documentos con nombres arbitrarios, se pueden utilizar campos dinámicos. Éstos especifican qué tipo utilizar para los campos del documento que coinciden con un determinado patrón de nombres. Las colecciones Solr también pueden funcionar en el llamado modo sin esquema. En este modo, Solr adivina los tipos de los campos suministrados y añade otros nuevos a medida que aparecen en los documentos.
SolrCloud permite particionar y distribuir colecciones entre servidores Solr y, por tanto, almacenar miles de millones de documentos y soportar una alta concurrencia de consultas. Como ocurre con todos los motores de almacenamiento y consulta, Solr tiene sus puntos fuertes y débiles. En general, una implementación de SolrCloud bien operada y configurada puede soportar colecciones distribuidas que contengan miles de millones de documentos, pero debes tener cuidado de distribuir adecuadamente la carga de consulta e indexación. Los puntos fuertes de Solr residen en su sintaxis de consulta flexible y en su capacidad para realizar búsquedas complejas de subsegundos en millones de documentos, devolviendo en última instancia decenas o cientos de documentos al cliente. En general, no es adecuado para casos de uso analítico a gran escala que devuelven millones de documentos a la vez. Y para los que no pueden vivir sin él, Solr soporta ahora un dialecto SQL para consultar colecciones.
Obtendrás más información sobre el uso de Solr en contextos de alta disponibilidad en "Solr".
Profundizar
Aquí sólo hemos tratado los aspectos básicos de Solr. Te recomendamos encarecidamente que consultes la documentación oficial, que contiene muchos más detalles sobre el diseño de esquemas y el funcionamiento de SolrCloud. También merece la pena echar un vistazo a lo siguiente:
-
Solr in Action, 3ª Edición, por Trey Grainger y Timothy Potter (Manning). Aunque ligeramente anticuado, este recurso contiene una excelente descripción del funcionamiento interno de Solr.
-
Solr y Cosas. El blog de Yonik Seeley contiene abundante información de fondo sobre diversas funciones de Solr y una "Guía no oficial de Solr".
Considera también
Como hemos señalado antes, Elasticsearch es una sólida alternativa a Solr.
Apache Kafka
Uno de los principales impulsores de un clúster es disponer de una única plataforma que pueda almacenar y procesar datos de multitud de fuentes. Las fuentes de datos de una empresa son muchas y variadas: registros web, registros de máquinas, eventos empresariales, datos transaccionales, documentos de texto, imágenes y mucho más. Estos datos llegan a través de una multitud de modos, incluidos los basados en push, pull, lotes y flujos, y en una amplia gama de protocolos: HTTP, SCP/SFTP, JDBC, AMQP, JMS, etc. Dentro del ecosistema de la plataforma, existen múltiples sumideros para los datos entrantes: HDFS, HBase, Elasticsearch y Kudu, por nombrar sólo algunos. Gestionar y orquestar la ingestión en la plataforma en todos estos modos puede convertirse rápidamente en una pesadilla de diseño y operativa.
En el caso de los flujos de datos, en particular, las tecnologías de intermediación de mensajes existentes tienen dificultades para adaptarse a las exigencias de los grandes datos. Los puntos especialmente delicados son las exigencias de dar soporte a cientos de clientes, todos deseosos de escribir y leer a grandes anchos de banda y todos deseosos de mantener sus propias posiciones en los flujos. Garantizar la entrega de forma escalable utilizando estas tecnologías es todo un reto, al igual que hacer frente a las acumulaciones de datos inducidas por ráfagas de gran volumen en los flujos entrantes o por procesos descendentes fallidos. Estas exigencias condujeron directamente al desarrollo de Apache Kafka en LinkedIn.
Consejo
Lee más sobre los antecedentes y motivaciones de una arquitectura de publicación/suscripción basada en registros en "El Registro: Lo que todo ingeniero de software debe saber sobre la abstracción unificadora de datos en tiempo real".
Apache Kafka es un sistema de publicación/suscripción diseñado para ser escalable horizontalmente tanto en volumen como en ancho de banda de lectura y escritura del cliente. Su idea central es utilizar registros distribuidos y secuenciales como mecanismo de almacenamiento de los mensajes entrantes y permitir que los clientes, o grupos de clientes, consuman datos desde un punto determinado utilizando simples desplazamientos numéricos. Kafka se ha convertido en una tecnología de cola crítica, que proporciona un búfer de ingestión resistente y altamente disponible, que integra múltiples fuentes ascendentes y sumideros descendentes. Cada vez más, el procesamiento de flujos y las consultas de estado de flujos se soportan dentro del propio ecosistema Kafka, con Kafka operando como sistema de registro.
La estructura de datos fundamental en Kafka es el tema, que es una secuencia de mensajes (o registros) distribuidos en múltiples servidores (o brokers). Cada tema puede crearse con múltiples particiones, cada una de las cuales está respaldada por un registro en disco. Para mayor resiliencia, las particiones tienen múltiples réplicas que residen en diferentes brokers.
Los mensajes en Kafka son pares clave-valor, donde la clave y el valor son matrices de bytes arbitrarias. Los clientes publican mensajes en particiones de temas Kafka a través de productores. Cada partición de un tema es un registro ordenado e inmutable. Los mensajes nuevos se añaden secuencialmente al final del registro, lo que hace que la operación de E/S sea muy eficiente. Dentro de la partición, cada mensaje se escribe junto con un desplazamiento, que es un índice siempre creciente en el registro. Los clientes pueden leer de los temas utilizando consumidores. Para la escalabilidad, los consumidores separados pueden combinarse en un grupo de consumidores. Los consumidores pueden recuperar su último desplazamiento conocido al iniciarse y reanudar fácilmente su actividad donde la dejaron.
Kafka puede utilizarse de muchas formas. Lo más habitual es que se utilice como buffer escalable para el flujo de datos hacia y desde motores de almacenamiento en Hadoop. También se utiliza con frecuencia como bus de intercambio de datos en cadenas flexibles de procesamiento de flujos, donde sistemas como Kafka Connect, Apache Flume o Spark Streaming consumen y procesan datos y escriben sus resultados en nuevos temas.
Cada vez se construyen más arquitecturas en las que Kafka actúa como sistema central de registro y las vistas temporales se construyen en sistemas servidores externos, como bases de datos y almacenes de valores clave. Por este motivo, clasificamos a Kafka como motor de almacenamiento y no como tecnología de ingestión. Se utilice como se utilice, Kafka es una tecnología de integración clave en las plataformas de big data empresariales.
Profundizar
Existe abundante información sobre los antecedentes y el uso de Kafka. Algunos buenos sitios para empezar son:
-
Kafka: La guía definitiva, de Gwen Shapira, Neha Narkhede y Todd Palino (O'Reilly)
-
I Heart Logs, por Jay Kreps (O'Reilly)
Ingestión
Hay muchas tecnologías en el espacio de la ingesta, demasiadas para cubrirlas en este estudio. Tradicionalmente, dos de las principales tecnologías de ingestión han sido Apache Flume, orientada a la ingestión escalable de flujos de datos, y Apache Sqoop, centrada en la importación y exportación de datos en bases de datos relacionales. Sin embargo, han surgido muchas otras opciones para simplificar el proceso de los conductos de ingestión y eliminar la necesidad de codificación personalizada.
Dos opciones notables de código abierto son:
Orquestación
Las canalizaciones de ingesta y análisis por lotes suelen constar de múltiples fases dependientes, que potencialmente utilizan diferentes tecnologías en cada fase. Necesitamos orquestar y programar tales canalizaciones y ser capaces de expresar sus complejas interdependencias.
Apache Oozie
Apache Oozie es el marco de programación y ejecución de trabajos para Hadoop. Las unidades básicas de ejecución dentro de los trabajos de Oozie son las acciones, que representan tareas que se ejecutan en el ecosistema Hadoop, como consultas Hive o trabajos MapReduce. Las acciones se componen en flujos de trabajo, que representan secuencias u ordenaciones lógicas de tareas que deben ejecutarse juntas. Los flujos de trabajo pueden ejecutarse según una programación mediante coordinadores, que a su vez pueden agruparse en paquetes para la agrupación lógica de aplicaciones. Un trabajo Oozie puede referirse a un flujo de trabajo, a un coordinador o a un paquete.
Los trabajos de Oozie se definen mediante archivos XML. Cada flujo de trabajo contiene un grafo dirigido (acíclico) de acciones, básicamente parecido a un diagrama de flujo de procesamiento. Los coordinadores definen un calendario de ejecución para los flujos de trabajo, basado en intervalos de tiempo y en la disponibilidad del conjunto de datos de entrada. Los paquetes definen grupos de coordinadores relacionados con una hora de inicio global.
Los trabajos se envían al servidor Oozie, que valida el XML suministrado y se encarga del ciclo de vida del trabajo. Esto significa cosas diferentes para los distintos tipos de trabajos. Para los flujos de trabajo, significa iniciar y realizar un seguimiento de las ejecuciones de acciones individuales en el clúster Hadoop y proceder a través del gráfico de acciones hasta que el flujo de trabajo finalice con éxito o encuentre un error. Para los coordinadores, el servidor Oozie se encarga de que los flujos de trabajo se inicien según la programación y comprueba que todos los conjuntos de datos de entrada estén disponibles para la instancia concreta de la ejecución del flujo de trabajo, pudiendo retenerla hasta que sus datos de entrada estén listos. El servidor Oozie ejecuta cada uno de los coordinadores definidos en un paquete.
Las acciones del flujo de trabajo son de dos tipos: asíncronas y síncronas. La mayoría de las acciones se ejecutan de forma asíncrona en YARN mediante lanzadores. Los lanzadores son trabajos de sólo mapa que, a su vez, pueden enviar otro trabajo Hadoop (por ejemplo, Spark, MapReduce o Hive). Esta arquitectura permite al servidor Oozie seguir siendo ligero y, en consecuencia, ejecutar fácilmente cientos de acciones de forma concurrente. También aísla a las aplicaciones de larga ejecución de los fallos del servidor Oozie; como el estado del trabajo se mantiene en una base de datos subyacente, el servidor Oozie puede continuar donde lo dejó tras un reinicio sin afectar a las acciones en ejecución. Algunas acciones se consideran lo suficientemente ligeras como para no necesitar ejecutarse a través de YARN, sino que se ejecutan de forma sincrónica, directamente en el servidor Oozie. Entre ellas se incluyen el envío de correos electrónicos y algunos comandos HDFS. Las definiciones de trabajo de Oozie y todos los archivos y bibliotecas asociados deben almacenarse en HDFS, normalmente en un directorio por aplicación. Oozie expone una API RESTful HTTP respaldada por un servidor web multihilo a través del cual un usuario envía, monitorea y controla los trabajos.
En "Oozie" tratamos más a fondo el tema de la alta disponibilidad .
Considera también
Oozie no es del gusto de todos, y han surgido un par de contendientes muy capaces. Podría decirse que son más flexibles y utilizables, y merece la pena tenerlos en cuenta para implementaciones nuevas:
Resumen
Hemos cubierto bastante terreno en este manual, empezando por la definición básica de un clúster, que trataremos con más detalle en el próximo capítulo. A partir de ahí, hemos examinado los componentes básicos de los clústeres Hadoop, los marcos computacionales, los marcos analíticos SQL, los motores de almacenamiento, las tecnologías de ingesta y, por último, los sistemas de orquestación. La Tabla 1-2 resume brevemente los componentes que se trataron y esboza su funcionalidad principal prevista.
Proyecto | Descripción | Se utiliza para | Depende de |
---|---|---|---|
ZooKeeper |
Servicio de configuración distribuida |
Compartir metadatos entre procesos distribuidos y bloqueo distribuido |
- |
HDFS |
Almacenamiento de archivos distribuido |
Almacenamiento masivo escalable de datos inmutables |
ZooKeeper |
YARN |
Marco de programación y ejecución de recursos distribuidos |
Marcos que requieren recursos informáticos escalables y distribuidos |
ZooKeeper, HDFS |
MapReduce |
Marco genérico de computación distribuida |
Cargas de trabajo de cálculo por lotes |
YARN, HDFS |
Chispa |
Marco genérico de computación distribuida |
Cargas de trabajo por lotes, SQL analítico y streaming |
Programador de recursos (por ejemplo, YARN o Mesos), fuentes de datos (por ejemplo, HDFS, Kudu) |
Colmena |
Marco de consultas analíticas SQL |
Cargas de trabajo SQL analíticas |
YARN, fuentes de datos (por ejemplo, HDFS, Kudu) |
Impala |
Motor de análisis MPP SQL |
Cargas de trabajo SQL analíticas e interactivas |
Fuentes de datos (HDFS, Kudu, HBase) |
HBase |
Almacén distribuido y ordenado para datos jerárquicos clave-valor |
Acceso de lectura/escritura aleatorio y de baja latencia a datos basados en filas con claves estructuradas |
HDFS, ZooKeeper |
Kudu |
Almacén distribuido de datos estructurados |
Acceso aleatorio combinado de lectura/escritura y cargas de trabajo analíticas |
- |
Solr |
Marco de búsqueda empresarial |
Indexación y consulta escalable de documentos en campos arbitrarios |
HDFS, ZooKeeper |
Kafka |
Marco de mensajería pub/sub distribuida |
Publicación y consumo escalables de datos en streaming |
ZooKeeper |
Oozie |
Programador de flujos de trabajo |
Pipelines de procesamiento de datos regulares y a la carta |
- |
Con este conocimiento práctico en tu haber, el resto del libro debería ser más fácil de digerir. Si olvidas algunos detalles, siempre puedes utilizar esta sección para volver a familiarizarte con las tecnologías clave.
1 Al igual que la mayoría de los proyectos de código abierto, evitamos el término esclavo siempre que sea posible.
Get Arquitectura de plataformas de datos modernas now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.