DEV Community

Cover image for Aprendiendo Spark: #2 Hola mundo
Dani Sancas
Dani Sancas

Posted on

Aprendiendo Spark: #2 Hola mundo

¡Saludos, camaradas! 👋

En este artículo vamos a analizar el código del "Hola mundo" que expusimos en el artículo anterior. Antes de nada, vamos a recordarlo:

from pyspark.sql import SparkSession

# Iniciamos Spark de manera local
spark = (SparkSession
         .builder
         .master("local[*]")
         .appName("Hola Mundo")
         .getOrCreate())

# Paralelizamos una lista del 0 al 9 (incluido)
# Computamos la suma de los números
# Obtenemos el resultado en una variable
result = (spark
          .sparkContext
          .parallelize(range(10))
          .reduce(lambda x, y: x + y))

# Imprimimos el resultado
print(f"La suma es {result}")
Enter fullscreen mode Exit fullscreen mode

Al ejecutarlo a través del IDE o mediante una terminal escribiendo python hola_mundo.py veremos el siguiente output tras una serie de warnings:

La suma es 45

Process finished with exit code 0
Enter fullscreen mode Exit fullscreen mode

Vale, muy bonito todo pero, ¿qué es cada cosa?

Creando la sesión de Spark

Para continuar vamos a dar una vuelta por nuestro código anotando los tipos de nuestras variables.

Friendly reminder: Anotar con tipos en Python es meramente informativo de cara a quien desarrolla el código, no tiene el efecto que pueda tener en lenguajes como Java.

Vamos a anotar con su tipo la variable spark y también vamos a poner un comentario en cada una de las llamadas encadenadas durante la creación de dicho objeto, para que veamos de qué tipo es cada una.

spark: SparkSession = (SparkSession             # SparkSession
                       .builder                 # Builder
                       .master("local[*]")      # Builder
                       .appName("Hola Mundo")   # Builder
                       .getOrCreate())          # SparkSession

Enter fullscreen mode Exit fullscreen mode

Todo empieza con la referencia a la clase SparkSession, ésta nos permite crear un objeto Builder al cual le iremos indicando qué configuración queremos.

En primer lugar, indicaremos que el master() es local usando todos los cores que dispongamos. Esto es típico para hacer pruebas en local, cuando no disponemos de un clúster donde ejecutar código productivo (y de momento nos sirve perfectamente).

En segundo lugar especificamos el nombre de nuestra ejecución mediante appName(). Como no podía ser de otra manera, se llama "Hola mundo" (cuánta imaginación, ¿verdad? 🙄).

Tanto las llamadas a master() como a appName() devuelven un objeto Builder, que indica que está a medio construir, nos faltaría un paso más.

Por último le indicamos a Spark que nos devuelva (en caso de existir) o que nos cree (en caso contrario) una SparkSession con la que podamos hacer computación distribuida.

La SparkSession que nos devuelve la plasmamos en la variable spark para que podamos utilizarla más adelante.

¡Llegados a este punto ya podemos empezar a hacer computación distribuida!

Pinto y coloreo mis primeras operaciones distribuidas 🤓

Ahora vamos a hacer lo mismo con el segundo bloque de código, anotando el tipo de la variable result y comentando cada paso.

result: int = (spark                          # SparkSession
               .sparkContext                  # SparkContext
               .parallelize(range(10))        # RDD[int]
               .reduce(lambda x, y: x + y))   # int
Enter fullscreen mode Exit fullscreen mode

Primero partimos de la variable spark creada previamente. Y a partir de ella obtenemos un objeto SparkContext. Podemos entender este objeto como un helper de Spark para realizar ciertas maniobras. En este caso nos facilita la creación de un RDD a través de su método parallelize, que toma una lista como argumento.

Con parallelize() tomamos una lista clásica (un array de toda la vida, si queréis verlo así) y crea un RDD a partir de ella, del mismo tipo de la lista. Nosotros le hemos pasado la lista [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] resultante de invocar range(10), que es de tipo int. Por lo tanto el RDD será también de tipo int.

Explícame un poco los RDDs, porfa 🥺

Los RDDs son las unidades básicas de Spark a partir de las cuales podemos hacer computación distribuida. En próximos artículos entraremos en más profundidad, de momento nos sirve pensar en ellos como listas cuyo contenido está troceado y repartido por diferentes servidores de Spark.

De esta manera, evitamos cargar a un único servidor con todo el trabajo, ya que todos los nodos que tengamos trabajarán a la par. ¿Y cuál es ese trabajo tan tedioso que va a requerir computación distribuida? 🤔

¡Nada más y nada menos que la acción reduce()! 🤩

Importante: Este reduce() no es el del módulo functools pero se comporta parecido, solo que de manera distribuida.

Acción reduce()

Vale, ¿entonces qué hace exactamente el reduce() de un RDD?

Esta función coge una lista distribuida (RDD) y va combinando sus valores mediante la función que le indiquemos. En este caso ha sido una simple función anónima que suma 2 números que le pase reduce().

Si no estáis familiarizados con funciones combinatorias de programación funcional, os dejo una breve explicación del reduce() del módulo functools. Recordad que no es lo mismo, pero nos sirve para hacernos una idea general de cuál es su mecánica.

Así pues, lo que hará es sumar todos los valores de la lista y devolverá un resultado, un simple int de toda la vida. En este proceso intervendrían todos nuestros servidores de Spark, comunicándose entre ellos para ir sumando los diferentes valores, hasta tener completada la suma de todos ellos y devolver el resultado.

Una vez tenemos ese número en nuestro poder, lo imprimimos por pantalla para conocer el resultado de tamaña operación. ¡Buen trabajo! 😎

Espero que os haya sido útil este artículo. En el próximo hablaremos en más profundidad de las operaciones de Spark, que se dividen en transformaciones y acciones.

Es muy importante entender bien el rol y efectos que las diferencian, ¡así que os espero en el próximo artículo! 🤗

¡Nos vemos, equipo! 🙌

Top comments (0)