Capítulo 1. Introducción a Apache Spark: Un motor analítico unificado

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

Este capítulo expone los orígenes de Apache Spark y su filosofía subyacente. También repasa los principales componentes del proyecto y su arquitectura distribuida. Si estás familiarizado con la historia de Spark y los conceptos de alto nivel, puedes saltarte este capítulo.

La génesis de la chispa

En esta sección, trazaremos el curso de la breve evolución de Apache Spark: su génesis, inspiración y adopción en la comunidad como motor de procesamiento unificado de big data de facto.

Big Data y Computación Distribuida en Google

Cuando pensamos en escala, no podemos evitar pensar en la capacidad del motor de búsqueda de Google para indexar y buscar los datos del mundo en Internet a la velocidad del rayo. El nombre Google es sinónimo de escala. De hecho, Google es un error ortográfico deliberado del término matemático googol: ¡es 1 más 100 ceros!

Ni los sistemas de almacenamiento tradicionales, como los sistemas de gestión de bases de datos relacionales (RDBMS) de , ni las formas imperativas de programación eran capaces de manejar la escala a la que Google quería construir y buscar los documentos indexados de Internet. La consiguiente necesidad de nuevos enfoques llevó a la creación de Google File System (GFS), MapReduce (MR) y Bigtable.

Mientras que GFS proporcionaba un sistema de archivos distribuido y tolerante a fallos a través de muchos servidores de hardware básico en una granja de clústeres, Bigtable ofrecía almacenamiento escalable de datos estructurados a través de GFS. MR introdujo un nuevo paradigma de programación paralela, basado en la programación funcional, para el procesamiento a gran escala de datos distribuidos en GFS y Bigtable.

En esencia, tus aplicaciones MR interactúan con el sistema MapReduce, que envía el código de cálculo (funciones de mapeo y reducción) al lugar donde residen los datos, favoreciendo la localidad de datos y la afinidad de rack de clúster en lugar de llevar los datos a tu aplicación.

Los trabajadores del clúster agregan y reducen los cálculos intermedios y producen una salida final anexa de la función reducir, que luego se escribe en un almacenamiento distribuido donde es accesible para tu aplicación. Este enfoque reduce significativamente el tráfico de red y mantiene la mayor parte de la entrada/salida (E/S) local en el disco, en lugar de distribuirla por la red.

La mayor parte del trabajo realizado por Google era privativo, pero las ideas expresadas en los tres documentos mencionados estimularon ideas innovadoras en otros lugares de la comunidad de código abierto, especialmente en Yahoo!, que se enfrentaba a retos similares de big data a escala para su motor de búsqueda.

¡Hadoop en Yahoo!

Los retos computacionales y las soluciones expresadas en el documento GFS de Google proporcionaron un anteproyecto para el Sistema de Archivos Hadoop (HDFS), incluida la implementación de MapReduce como marco para la computación distribuida. Donado a la Apache Software Foundation (ASF), una organización sin ánimo de lucro neutral en cuanto a proveedores, en abril de 2006, pasó a formar parte del marco Apache Hadoop de módulos relacionados: Hadoop Common, MapReduce, HDFS y Apache Hadoop YARN.

Aunque Apache Hadoop había conseguido una amplia adopción fuera de Yahoo!, inspirando a una gran comunidad de colaboradores de código abierto y a dos empresas comerciales basadas en código abierto (Cloudera y Hortonworks, ahora fusionadas), el marco MapReduce sobre HDFS tenía algunos defectos.

En primer lugar, era difícil de gestionar y administrar, con una engorrosa complejidad operativa. En segundo lugar, su API MapReduce general de procesamiento por lotes era verbosa y requería mucho código de configuración repetitivo, con una frágil tolerancia a fallos . En tercer lugar, con grandes lotes de trabajos de datos con muchos pares de tareas MR, el resultado calculado intermedio de cada par se escribe en el disco local para la siguiente etapa de su funcionamiento (ver Figura 1-1). Esta repetición de la E/S del disco pasó factura: los grandes trabajos de RM podían durar horas e incluso días.

Intermittent iteration of reads and writes between map and reduce computations
Figura 1-1. Iteración intermitente de lecturas y escrituras entre los cálculos map y reduce

Y por último, aunque Hadoop MR era propicio para trabajos a gran escala para el procesamiento general por lotes, se quedaba corto para combinar otras cargas de trabajo como el aprendizaje automático, el streaming o las consultas interactivas tipo SQL.

Para gestionar estas nuevas cargas de trabajo, los ingenieros desarrollaron sistemas a medida (Apache Hive, Apache Storm, Apache Impala, Apache Giraph, Apache Drill, Apache Mahout, etc.), cada uno con sus propias API y configuraciones de clúster, lo que aumentó aún más la complejidad operativa de Hadoop y la pronunciada curva de aprendizaje para los desarrolladores.

La pregunta entonces fue (teniendo en cuenta el adagio de Alan Kay, "Las cosas simples deben ser simples, las cosas complejas deben ser posibles"), ¿había alguna forma de hacer Hadoop y MR más simples y rápidos?

Los primeros años de Spark en AMPLab

Los investigadores de la UC Berkeley que habían trabajado anteriormente en Hadoop MapReduce asumieron este reto con un proyecto al que llamaron Spark. Reconocieron que MR era ineficiente (o intratable) para trabajos de computación interactiva o iterativa y un marco complejo de aprender, por lo que desde el principio abrazaron la idea de hacer Spark más simple, rápido y fácil. Este empeño comenzó en 2009 en el RAD Lab, que más tarde se convirtió en el AMPLab (y ahora se conoce como RISELab).

Los primeros artículos publicados sobre Spark demostraban que era entre 10 y 20 veces más rápido que Hadoop MapReduce para determinados trabajos. Hoy, es muchos órdenes de magnitud más rápido. La idea central del proyecto Spark era incorporar ideas tomadas de Hadoop MapReduce, pero para mejorar el sistema: hacerlo altamente tolerante a fallos y vergonzosamente paralelo, admitir el almacenamiento en memoria para resultados intermedios entre cálculos iterativos e interactivos de mapear y reducir, ofrecer API fáciles y componibles en múltiples lenguajes como modelo de programación, y admitir otras cargas de trabajo de forma unificada. Volveremos sobre esta idea de unificación en breve, ya que es un tema importante en Spark.

En 2013, Spark se había generalizado y algunos de sus creadores e investigadores originales -Matei Zaharia, Ali Ghodsi, Reynold Xin, Patrick Wendell, Ion Stoica y Andy Konwinski- donaron el proyecto Spark a la ASF y crearon una empresa llamada Databricks.

Databricks y la comunidad de desarrolladores de código abierto trabajaron para publicar Apache Spark 1.0 en mayo de 2014, bajo el gobierno de la ASF. Esta primera versión importante estableció el impulso para futuras versiones frecuentes y contribuciones de características notables a Apache Spark por parte de Databricks y más de 100 proveedores comerciales.

¿Qué es Apache Spark?

Apache Spark es un motor unificado diseñado para el procesamiento de datos distribuidos a gran escala, in situ en centros de datos o en la nube.

Spark proporciona almacenamiento en memoria para cálculos intermedios, por lo que es mucho más rápido que Hadoop MapReduce. Incorpora bibliotecas con API componibles para el aprendizaje automático (MLlib), SQL para consultas interactivas (Spark SQL), procesamiento de flujos (Structured Streaming) para interactuar con datos en tiempo real, y procesamiento de gráficos (GraphX).

La filosofía de diseño de Spark se centra en cuatro características clave:

  • Velocidad

  • Facilidad de uso

  • Modularidad

  • Extensibilidad

Veamos lo que esto significa para el marco.

Velocidad

Spark ha perseguido el objetivo de la velocidad de varias maneras. En primer lugar, su implementación interna se beneficia enormemente de los enormes avances recientes de la industria del hardware en la mejora del precio y el rendimiento de las CPU y la memoria. Los servidores básicos de hoy en día son baratos, con cientos de gigabytes de memoria, múltiples núcleos y el sistema operativo subyacente basado en Unix que aprovecha el multihilo eficiente y el procesamiento paralelo. El marco está optimizado para aprovechar todos estos factores.

En segundo lugar, Spark construye sus cálculos de consulta como un grafo acíclico dirigido (DAG) ; su programador DAG y su optimizador de consultas construyen un grafo computacional eficiente que normalmente puede descomponerse en tareas que se ejecutan en paralelo entre los trabajadores del clúster. Y en tercer lugar, su motor de ejecución física, Tungsten, utiliza la generación de código de etapa completa para generar código compacto para la ejecución (trataremos la optimización SQL y la generación de código de etapa completa en el Capítulo 3).

Con todos los resultados intermedios retenidos en memoria y su limitada E/S de disco, esto le da un enorme aumento de rendimiento.

Facilidad de uso

Spark logra la simplicidad proporcionando una abstracción fundamental de una estructura de datos lógica simple llamada Conjunto de Datos Distribuidos Resilientes (RDD) sobre la que se construyen todas las demás abstracciones de datos estructurados de nivel superior, como DataFrames y Conjuntos de Datos. Al proporcionar un conjunto de transformaciones y acciones como operaciones, Spark ofrece un modelo de programación sencillo que puedes utilizar para construir aplicaciones de big data en lenguajes familiares.

Modularidad

Las operaciones de Spark pueden aplicarse a muchos tipos de cargas de trabajo y expresarse en cualquiera de los lenguajes de programación admitidos: Scala, Java, Python, SQL y R. Spark ofrece bibliotecas unificadas con API bien documentadas que incluyen los siguientes módulos como componentes básicos: Spark SQL, Spark Structured Streaming, Spark MLlib y GraphX, que combinan todas las cargas de trabajo que se ejecutan en un solo motor. Analizaremos todos ellos más detenidamente en la siguiente sección.

Puedes escribir una única aplicación Spark que lo haga todo: sin necesidad de motores distintos para cargas de trabajo dispares, sin necesidad de aprender API separadas. Con Spark, obtienes un motor de procesamiento unificado para tus cargas de trabajo.

Extensibilidad

Spark se centra en su rápido motor de cálculo paralelo, más que en el almacenamiento. A diferencia de Apache Hadoop, que incluía tanto almacenamiento como computación, Spark desacopla ambos. Esto significa que puedes utilizar Spark para leer datos almacenados en innumerables fuentes -Apache Hadoop, Apache Cassandra, Apache HBase, MongoDB, Apache Hive, RDBMS y más- y procesarlos todos en memoria. DataFrameReader s y DataFrameWriters de Spark también pueden ampliarse para leer datos de otras fuentes, como Apache Kafka, Kinesis, Azure Storage y Amazon S3, en su abstracción lógica de datos, sobre la que puede operar.

La comunidad de desarrolladores de Spark mantiene una lista de paquetes Spark de terceros como parte del creciente ecosistema (ver Figura 1-2). Este rico ecosistema de paquetes incluye conectores Spark para diversas fuentes de datos externas, monitores de rendimiento y mucho más.

Apache Spark’s ecosystem of connectors
Figura 1-2. El ecosistema de conectores de Apache Spark

Analítica unificada

Aunque la noción de unificación no es exclusiva de Spark, es un componente central de su filosofía de diseño y evolución. En noviembre de 2016, la Association for Computing Machinery (ACM) reconoció a Apache Spark y concedió a sus creadores originales el prestigioso Premio ACM por su artículo que describe Apache Spark como un "Motor Unificado para el Procesamiento de Big Data". El documento premiado señala que Spark sustituye a todos los motores separados de procesamiento por lotes, gráficos, flujos y consultas, como Storm, Impala, Dremel, Pregel, etc., por una pila unificada de componentes que aborda diversas cargas de trabajo bajo un único motor rápido distribuido.

Componentes de Apache Spark como pila unificada

Como se muestra en la Figura 1-3, Spark ofrece cuatro componentes distintos como bibliotecas para diversas cargas de trabajo : Spark SQL, Spark MLlib, Spark Structured Streaming y GraphX. Cada uno de estos componentes es independiente del motor central de Spark tolerante a fallos, en el sentido de que utilizas las API para escribir tu aplicación Spark y Spark la convierte en un DAG que es ejecutado por el motor central. Así, tanto si escribes tu código Spark utilizando las API estructuradas proporcionadas (que veremos en el Capítulo 3) en Java, R, Scala, SQL o Python, el código subyacente se descompone en código de bytes altamente compacto que se ejecuta en las JVM de los trabajadores en todo el clúster.

Apache Spark components and API stack
Figura 1-3. Componentes de Apache Spark y pila de API

Veamos cada uno de estos componentes con más detalle.

Spark SQL

Este módulo funciona bien con datos estructurados. Puedes leer datos almacenados en una tabla RDBMS o desde formatos de archivo con datos estructurados (CSV, texto, JSON, Avro, ORC, Parquet, etc.) y luego construir tablas permanentes o temporales en Spark. Además, al utilizar las API estructuradas de Spark en Java, Python, Scala o R, puedes combinar consultas similares a SQL para consultar los datos que acabas de leer en un Spark DataFrame. Hasta la fecha, Spark SQL es compatible con ANSI SQL:2003 y también funciona como un motor SQL puro.

Por ejemplo, en este fragmento de código Scala, puedes leer desde un archivo JSON almacenado en Amazon S3, crear una tabla temporal y emitir una consulta de tipo SQL sobre los resultados leídos en memoria como un Spark DataFrame:

// In Scala
// Read data off Amazon S3 bucket into a Spark DataFrame
spark.read.json("s3://apache_spark/data/committers.json")
  .createOrReplaceTempView("committers")
// Issue a SQL query and return the result as a Spark DataFrame
val results = spark.sql("""SELECT name, org, module, release, num_commits
    FROM committers WHERE module = 'mllib' AND num_commits > 10
    ORDER BY num_commits DESC""")

Puedes escribir fragmentos de código similares en Python, R o Java, y el bytecode generado será idéntico, con el mismo rendimiento.

Spark MLlib

Spark viene con una biblioteca que contiene algoritmos comunes de aprendizaje automático (ML) llamada MLlib. Desde la primera versión de Spark, el rendimiento de este componente de la biblioteca ha mejorado significativamente gracias a las mejoras del motor subyacente de Spark 2.x. MLlib proporciona muchos algoritmos populares de aprendizaje automático construidos sobre APIs de alto nivel basadas en DataFrame para construir modelos.

Nota

A partir de Apache Spark 1.6, el proyecto MLlib se divide en dos paquetes: spark.mllib y spark.ml. La API basada en DataFrame es la segunda, mientras que la primera contiene las API basadas en RDD, que ahora están en modo de mantenimiento. Todas las nuevas funciones van en spark.ml. Este libro se refiere a "MLlib" como la biblioteca paraguas para el aprendizaje automático en Apache Spark.

Estas API te permiten extraer o transformar características, construir canalizaciones (para entrenar y evaluar) y persistir en los modelos (para guardarlos y recargarlos) durante la implementación. Otras utilidades incluyen el uso de operaciones comunes de álgebra lineal y estadística. MLlib incluye otras primitivas ML de bajo nivel, incluida una optimización genérica de descenso de gradiente. El siguiente fragmento de código Python encapsula las operaciones básicas que un científico de datos puede realizar al construir un modelo (en los Capítulos 10 y 11 se tratarán ejemplos más extensos):

# In Python
from pyspark.ml.classification import LogisticRegression
...
training = spark.read.csv("s3://...")
test = spark.read.csv("s3://...")

# Load training data
lr = LogisticRegression(maxIter=10, regParam=0.3, elasticNetParam=0.8)

# Fit the model
lrModel = lr.fit(training)

# Predict
lrModel.transform(test)
...

Streaming estructurado Spark

Apache Spark 2.0 introdujo un modelo experimental de Streaming Continuo y APIs de Streaming Estructurado, construidas sobre el motor SQL de Spark y APIs basadas en DataFrame. En Spark 2.2, el Streaming Estructurado ya estaba disponible de forma general, lo que significaba que los desarrolladores podían utilizarlo en sus entornos de producción.

Necesario para que los desarrolladores de big data combinen y reaccionen en tiempo real tanto a los datos estáticos como a los datos de flujo procedentes de motores como Apache Kafka y otras fuentes de flujo, el nuevo modelo ve un flujo como una tabla en continuo crecimiento, con nuevas filas de datos añadidas al final. Los desarrolladores pueden tratarla simplemente como una tabla estructurada y realizar consultas sobre ella como lo harían con una tabla estática.

Bajo el modelo de Streaming Estructurado, el motor central SQL de Spark maneja todos los aspectos de la tolerancia a fallos y la semántica de datos tardíos, lo que permite a los desarrolladores centrarse en escribir aplicaciones de streaming con relativa facilidad. Este nuevo modelo obvió el antiguo modelo DStreams de la serie 1.x de Spark, del que hablaremos con más detalle en el Capítulo 8. Además, Spark 2.x y Spark 3.0 ampliaron la gama de fuentes de datos en streaming para incluir Apache Kafka, Kinesis y el almacenamiento basado en HDFS o en la nube.

El siguiente fragmento de código muestra la anatomía típica de una aplicación de Streaming Estructurado. Lee desde un socket localhost y escribe los resultados del recuento de palabras en un tema de Apache Kafka:

# In Python
# Read a stream from a local host
from pyspark.sql.functions import explode, split
lines = (spark 
  .readStream
  .format("socket")
  .option("host", "localhost")
  .option("port", 9999)
  .load())

# Perform transformation
# Split the lines into words
words = lines.select(explode(split(lines.value, " ")).alias("word"))

# Generate running word count
word_counts = words.groupBy("word").count()

# Write out to the stream to Kafka
query = (word_counts
  .writeStream 
  .format("kafka") 
  .option("topic", "output"))

GraphX

Como su nombre indica, GraphX es una biblioteca para manipular grafos (por ejemplo, grafos de redes sociales, rutas y puntos de conexión, o grafos de topología de redes) y realizar cálculos paralelos de grafos. Ofrece los algoritmos estándar de grafos para análisis, conexiones y recorridos, aportados por los usuarios de la comunidad: los algoritmos disponibles incluyen PageRank, Componentes Conectados y Recuento de Triángulos.1

Este fragmento de código muestra un ejemplo sencillo de cómo unir dos gráficos utilizando las API GraphX:

// In Scala
val graph = Graph(vertices, edges)
messages = spark.textFile("hdfs://...")
val graph2 = graph.joinVertices(messages) {
  (id, vertex, msg) => ...
}

Ejecución distribuida de Apache Spark

Si has leído hasta aquí, ya sabes que Spark es un motor de procesamiento de datos distribuido cuyos componentes trabajan en colaboración en un clúster de máquinas. Antes de que exploremos la programación con Spark en los siguientes capítulos de este libro, necesitas comprender cómo funcionan juntos y se comunican todos los componentes de la arquitectura distribuida de Spark, y qué modos de implementación están disponibles.

Empecemos por examinar cada uno de los componentes individuales que se muestran en la Figura 1-4 y cómo encajan en la arquitectura. A un alto nivel en la arquitectura Spark, una aplicación Spark consiste en un programa controlador que es responsable de orquestar las operaciones paralelas en el clúster Spark. El controlador accede a los componentes distribuidos en el clúster -los ejecutores Spark y el gestor del clúster- a través de SparkSession.

Apache Spark components and architecture
Figura 1-4. Componentes y arquitectura de Apache Spark

Conductor de chispa

Como parte de la aplicación Spark responsable de instanciar un SparkSession, el controlador Spark tiene múltiples funciones: se comunica con el gestor del clúster; solicita recursos (CPU, memoria, etc.) al gestor del clúster para los ejecutores de Spark (JVMs); y transforma todas las operaciones Spark en cálculos DAG, los programa y distribuye su ejecución como tareas entre los ejecutores Spark. Una vez asignados los recursos, se comunica directamente con los ejecutores.

SparkSession

En Spark 2.0, SparkSession se convirtió en un conducto unificado para todas las operaciones y datos de Spark. No sólo subsumió los anteriores puntos de entrada a Spark, como SparkContext, SQLContext, HiveContext, SparkConf, y StreamingContext, sino que también simplificó y facilitó el trabajo con Spark.

Nota

Aunque en Spark 2.x el SparkSession subsume todos los demás contextos, puedes seguir accediendo a los contextos individuales y a sus respectivos métodos. De este modo, la comunidad mantuvo la compatibilidad con versiones anteriores. Es decir, tu antiguo código 1.x con SparkContext o SQLContext seguirá funcionando.

A través de este único conducto, puedes crear parámetros de tiempo de ejecución de la JVM, definir DataFrames y Datasets, leer de fuentes de datos, acceder a metadatos del catálogo y emitir consultas SQL de Spark . SparkSession proporciona un único punto de entrada unificado a toda la funcionalidad de Spark .

En una aplicación Spark independiente, puedes crear un SparkSession utilizando una de las API de alto nivel en el lenguaje de programación de tu elección. En el intérprete de comandos de Spark (más sobre esto en el próximo capítulo) se crea el SparkSession por ti, y puedes acceder a él a través de una variable global llamada spark o sc.

Mientras que en Spark 1.x habrías tenido que crear contextos individuales (para streaming, SQL, etc.), introduciendo código repetitivo adicional, en una aplicación Spark 2.x puedes crear un SparkSession por JVM y utilizarlo para realizar una serie de operaciones Spark.

Veamos un ejemplo:

// In Scala
import org.apache.spark.sql.SparkSession

// Build SparkSession
val spark = SparkSession
  .builder
  .appName("LearnSpark")
  .config("spark.sql.shuffle.partitions", 6)
  .getOrCreate()
...
// Use the session to read JSON 
val people = spark.read.json("...")
...
// Use the session to issue a SQL query
val resultsDF = spark.sql("SELECT city, pop, state, zip FROM table_name")

Gestor de clústeres

El gestor de clústeres se encarga de gestionar y asignar recursos para el clúster de nodos en el que se ejecuta tu aplicación Spark. Actualmente, Spark admite cuatro gestores de clúster: el gestor de clúster autónomo incorporado, Apache Hadoop YARN, Apache Mesos y Kubernetes.

Ejecutor Spark

Un ejecutor Spark se ejecuta en cada nodo trabajador del clúster. Los ejecutores se comunican con el programa controlador y son responsables de ejecutar las tareas en los trabajadores. En la mayoría de los modos de Implementaciones, sólo se ejecuta un único ejecutor por nodo.

Modos de Implementación

Una característica atractiva de Spark es su compatibilidad con innumerables modos de implementación, lo que permite que Spark se ejecute en diferentes configuraciones y entornos. Dado que el gestor de clústeres es agnóstico respecto a dónde se ejecuta (siempre que pueda gestionar los ejecutores de Spark y satisfacer las solicitudes de recursos), Spark puede desplegarse en algunos de los entornos más populares -como Apache Hadoop YARN y Kubernetes- y funcionar en diferentes modos. La Tabla 1-1 resume los modos de implementación disponibles.

Tabla 1-1. Hoja de trucos para los modos de implementación de Spark
Modo Conductor de chispa Ejecutor Spark Gestor de clústeres
Local Se ejecuta en una única JVM, como un portátil o un único nodo Se ejecuta en la misma JVM que el controlador Se ejecuta en el mismo host
Independiente Puede ejecutarse en cualquier nodo del clúster Cada nodo del clúster lanzará su propia JVM ejecutora Puede asignarse arbitrariamente a cualquier host del clúster
YARN (cliente) Se ejecuta en un cliente, no forma parte del clúster Contenedor del NodeManager de YARN El Gestor de Recursos de YARN trabaja con el Maestro de Aplicaciones de YARN para asignar los contenedores en NodeManagers para los ejecutores
YARN (clúster) Se ejecuta con el Maestro de Aplicaciones YARN Igual que el modo cliente YARN Igual que el modo cliente YARN
Kubernetes Se ejecuta en un pod de Kubernetes Cada trabajador se ejecuta dentro de su propio pod Maestro Kubernetes

Datos distribuidos y particiones

Los datos físicos reales se distribuyen a través del almacenamiento como particiones que residen en HDFS o en el almacenamiento en la nube (ver Figura 1-5). Aunque los datos se distribuyen como particiones en el clúster físico, Spark trata cada partición como una abstracción lógica de datos de alto nivel, como un DataFrame en memoria. Aunque esto no siempre es posible, a cada ejecutor Spark se le asigna preferiblemente una tarea que requiere que lea la partición más cercana a él en la red, observando la localidad de datos.

Data is distributed across physical machines
Figura 1-5. Los datos están distribuidos en máquinas físicas

El particionamiento permite un paralelismo eficiente. Un esquema distribuido de división de los datos en trozos o particiones permite a los ejecutores de Spark procesar sólo los datos que están cerca de ellos, minimizando el ancho de banda de la red. Es decir, a cada núcleo del ejecutor se le asigna su propia partición de datos en la que trabajar (ver Figura 1-6).

Each executor’s core gets a partition of data to work on
Figura 1-6. Cada núcleo ejecutor obtiene una partición de datos sobre la que trabajar

Por ejemplo, este fragmento de código dividirá los datos físicos almacenados en los clusters en ocho particiones, y cada ejecutor obtendrá una o más particiones para leer en su memoria:

# In Python
log_df = spark.read.text("path_to_large_text_file").repartition(8)
print(log_df.rdd.getNumPartitions())

Y este código creará un DataFrame de 10.000 enteros distribuidos en ocho particiones en memoria:

# In Python
df = spark.range(0, 10000, 1, 8)
print(df.rdd.getNumPartitions())

Ambos fragmentos de código imprimirán 8.

En los Capítulos 3 y 7, hablaremos de cómo afinar y cambiar la configuración del particionamiento para conseguir el máximo paralelismo en función de cuántos núcleos tengas en tus ejecutores.

La experiencia del desarrollador

De todas las delicias de los desarrolladores, ninguna es más atractiva que un conjunto de API componibles que aumentan la productividad y son fáciles de usar, intuitivas y expresivas. Uno de los principales atractivos de Apache Spark para los desarrolladores han sido sus API fáciles de usar para operar con conjuntos de datos pequeños y grandes, en todos los lenguajes: Scala, Java, Python, SQL y R.

Una de las principales motivaciones de Spark 2.x fue unificar y simplificar el marco limitando el número de conceptos con los que tienen que lidiar los desarrolladores. Spark 2.x introdujo API de abstracción de alto nivel como construcciones de lenguaje específicas del dominio, lo que hizo que programar Spark fuera altamente expresivo y una experiencia agradable para el desarrollador. Expresas lo que quieres que calcule la tarea u operación, no cómo calcularlo, y dejas que Spark determine la mejor forma de hacerlo por ti. Trataremos estas API estructuradas en el Capítulo 3, pero antes echemos un vistazo a quiénes son los desarrolladores de Spark.

¿Quién utiliza Spark y para qué?

No es sorprendente que la mayoría de los desarrolladores que trabajan con big data sean ingenieros de datos, científicos de datos o ingenieros de aprendizaje automático. Se sienten atraídos por Spark porque les permite crear una serie de aplicaciones utilizando un único motor, con lenguajes de programación familiares.

Por supuesto, los desarrolladores pueden llevar muchos sombreros y a veces realizar tanto tareas de ciencia de datos como de ingeniería de datos, especialmente en empresas de nueva creación o grupos de ingeniería más pequeños. Entre todas estas tareas, sin embargo, los datos -masivas cantidades de datos- son la base.

Tareas de ciencia de datos

Como disciplina que ha cobrado importancia en la era de los grandes datos, la ciencia de los datos consiste en utilizar los datos para contar historias. Pero antes de poder narrar las historias, los científicos de datos tienen que limpiar los datos, explorarlos para descubrir patrones y construir modelos para predecir o sugerir resultados. Algunas de estas tareas requieren conocimientos de estadística, matemáticas, informática y programación.

La mayoría de los científicos de datos dominan el uso de herramientas analíticas como SQL, se sienten cómodos con bibliotecas como NumPy y pandas, y están familiarizados con lenguajes de programación como R y Python. Pero también deben saber cómo manejar o transformar los datos, y cómo utilizar algoritmos establecidos de clasificación, regresión o agrupación para construir modelos. A menudo sus tareas son iterativas, interactivas o ad hoc, o experimentales para afirmar sus hipótesis.

Afortunadamente, Spark admite estas diferentes herramientas. MLlib de Spark ofrece un conjunto común de algoritmos de aprendizaje automático para construir pipelines de modelos, utilizando estimadores de alto nivel, transformadores y featurizadores de datos. Spark SQL y el shell de Spark facilitan la exploración interactiva y ad hoc de los datos.

Además, Spark permite a los científicos de datos abordar grandes conjuntos de datos y escalar el entrenamiento y la evaluación de sus modelos. Apache Spark 2.4 introdujo un nuevo programador de bandas, como parte del Proyecto Hydrogen, para adaptarse a las necesidades de tolerancia a fallos de la formación y programación de modelos de aprendizaje profundo de forma distribuida, y Spark 3.0 ha introducido la capacidad de admitir la recopilación de recursos de GPU en los modos de implementación autónomo, YARN y Kubernetes. Esto significa que los desarrolladores cuyas tareas exigen técnicas de aprendizaje profundo pueden utilizar Spark.

Tareas de ingeniería de datos

Después de construir sus modelos, los científicos de datos a menudo necesitan trabajar con otros miembros del equipo, que pueden ser responsables de la implementación de los modelos. O puede que necesiten colaborar estrechamente con otros para construir y transformar datos brutos y sucios en datos limpios que sean fácilmente consumibles o utilizables por otros científicos de datos. Por ejemplo, un modelo de clasificación o agrupación no existe de forma aislada; funciona junto con otros componentes, como una aplicación web o un motor de streaming como Apache Kafka, o como parte de una canalización de datos mayor. Esta canalización la construyen a menudo los ingenieros de datos.

Los ingenieros de datos tienen un sólido conocimiento de los principios y metodologías de la ingeniería de software, y poseen habilidades para crear canalizaciones de datos escalables para un caso de uso empresarial concreto. Los conductos de datos permiten transformaciones de extremo a extremo de los datos brutos procedentes de innumerables fuentes: los datos se limpian para que puedan ser consumidos por los desarrolladores, almacenados en la nube o en NoSQL o RDBMS para la generación de informes, o accesibles a los analistas de datos a través de herramientas de inteligencia empresarial.

Spark 2.x introdujo un modelo de streaming evolutivo denominado aplicaciones continuas con Streaming Estructurado (tratado en detalle en el Capítulo 8). Con las API de Streaming Estructurado, los ingenieros de datos pueden construir canalizaciones de datos complejas que les permitan ETL datos de fuentes de datos tanto en tiempo real como estáticas.

Los ingenieros de datos utilizan Spark porque proporciona una forma sencilla de paralelizar los cálculos y oculta toda la complejidad de la distribución y la tolerancia a fallos. Esto les deja libres para centrarse en el uso de API de alto nivel basadas en DataFrame y consultas en lenguaje específico del dominio (DSL) para hacer ETL, leer y combinar datos de múltiples fuentes.

Las mejoras de rendimiento en Spark 2.x y Spark 3.0, debidas al optimizador Catalyst para SQL y a Tungsten para la generación de código compacto, han facilitado mucho la vida a los ingenieros de datos. Pueden elegir utilizar cualquiera de las tres API de Spark -RDDs, DataFrames o Datasets- que se adapte a la tarea que tengan entre manos, y aprovechar las ventajas de Spark.

Adopción y expansión comunitarias

No es sorprendente que Apache Spark haya tocado una fibra sensible en la comunidad de código abierto, especialmente entre los ingenieros de datos y los científicos de datos. Su filosofía de diseño y su inclusión como proyecto de la Fundación del Software Apache han suscitado un inmenso interés entre la comunidad de desarrolladores.

En la actualidad, existen más de 600 grupos de Meetup de Apache Spark en todo el mundo, con cerca de medio millón de miembros. Cada semana, alguien en el mundo está dando una charla en una reunión o conferencia o compartiendo una entrada de blog sobre cómo utilizar Spark para construir canalizaciones de datos. La Cumbre Spark + AI es la mayor conferencia dedicada al uso de Spark para el aprendizaje automático, la ingeniería de datos y la ciencia de datos en muchos sectores verticales.

Desde la primera versión 1.0 de Spark en 2014 ha habido muchas versiones menores y mayores, y la versión mayor más reciente de Spark 3.0 llegará en 2020. Este libro cubrirá aspectos de Spark 2.x y Spark 3.0. En el momento de su publicación, la comunidad habrá lanzado Spark 3.0, y la mayor parte del código de este libro se ha probado con Spark 3.0-preview2.

A lo largo de sus versiones, Spark ha seguido atrayendo colaboradores de todo el mundo y de numerosas organizaciones. En la actualidad, Spark cuenta con cerca de 1.500 colaboradores, más de 100 versiones, 21.000 forks y unos 27.000 commits en GitHub, como muestra la Figura 1-7. Y esperamos que cuando termines este libro, tú también te sientas impulsado a contribuir.

The state of Apache Spark on GitHub (source: https://github.com/apache/spark)
Figura 1-7. El estado de Apache Spark en GitHub (fuente: https://github.com/apache/spark)

Ahora podemos centrar nuestra atención en lo divertido de aprender: dónde y cómo empezar a utilizar Spark. En el próximo capítulo, te mostraremos cómo ponerte en marcha con Spark en tres sencillos pasos .

1 Aportada a la comunidad por Databricks como proyecto de código abierto, GraphFrames es una biblioteca general de procesamiento de grafos similar a GraphX de Apache Spark, pero que utiliza API basadas en DataFrame.

Get Aprender Spark, 2ª Edición now with the O’Reilly learning platform.

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