Spark: Operaciones básicas con RDD’s

Después de más de un año publicando entradas en Scalera creo que ya ha llegado el momento de tocar un poco una de las herramientas más importantes del ecosistema Scala. Por supuesto, me refiero a Spark.

Spark es un framework que permite paralelizar colecciones y su procesamiento. Este procesamiento pueden ser tareas batch tipo Map-Reduce sobre multitud de orígenes de datos (HDFS, Cassandra, ElasticSearch, …), procesamiento en streaming de ciertos flujos de datos (Kafka, Flume, …); o realizar consultas con distintas fuentes de datos de manera unificada con un lenguaje de consulta tipo SQL.
Pero hoy no vamos a abarcar tanto. Hoy vamos a conocer el tipo de dato básico que se utiliza en Spark: el tipo RDD.

¿Qué es un RDD?

El tipo RDD (Resilient Distributed Dataset) se parece mucho a otras colecciones de Scala. Sin embargo es importante conocer alguna de sus características principales:

  • Es una colección distribuida. Esto quiere decir que está particionada entre los distintos workers de Spark.
  • Son inmutables: cuando transformamos un nuevo RDD realmente estamos creando uno nuevo.
  • Su evaluación es perezosa. Con los RDD’s estamos definiendo un flujo de información, pero no se ejecuta en el momento de definición, sino en el momento en el que se evalúe aplicando una acción sobre el RDD.funny-meme-super-lazy1

Además, es importate saber que sobre los RDD’s se pueden realizar dos tipos de operaciones: transformaciones y acciones.

Genial, ¿pero cómo los creo?

Existen varias formas para crear un RDD:

  • Paralelizando una colección en memoria, por ejemplo, una lista de valores. Para ello utilizamos el método parallelize del SparkContext.
    val newRDD: RDD[Int] = sc.parallelize(List(1, 2, 3, 4))
    
  • A partir de una fuente de almacenamiento utilizando, por ejemplo, la función textFile del SparkContext.
    val newRDD: RDD[Int] = sc.textFile("myValues.txt")
    
  • Transformando un RDD aplicando una transformación para crear un nuevo RDD a partir de otro.
    val newRDD: RDD[String] = intValues.map(_.toString)
    

¿Y eso de las transformaciones …?

Las transformaciones definirán como cambiará el flujo de información generando un nuevo RDD. Con estas transformaciones no se está evaluando el RDD, sino creando uno nuevo. Algunas de las transformaciones son:

  • map: aplica una función a cada elemento de la colección:
    intValues.map(_.toString) // RDD[String]
    
  • filter: selecciona el subconjunto de elementos que cumplen una determinada expresión booleana:
    intValues.filter(_.isOdd)// RDD[Int]
    
  • flatMap: además de realizar una función map, aplica un método flatten:
    textFile.map(_.split(" ")) //RDD[Array[String]] but ...
    textFile.flatMap(_.split(" ")) //RDD[String]
    

Habías dicho acciones, ¿no?

Las acciones nos permitirán evaluar un RDD y devolver un resultado. De esta forma se ejecuta todo el flujo de datos definido. ¿Algún ejemplo? Aquí van algunos:

  • count: nos devuelve el número total de elementos:
    sc.parallelize(List(1, 2, 3, 4)).count //4
    
  • collect: nos vuelca toda la colección distribuida en un array en memoria:
    sc.parallelize(List(1, 2, 3, 4)).collect // Array(1, 2, 3, 4)
    

    Ojo, cuidao. Si el RDD es muy grande, podemos tener problemas al volcar toda la colección en memoria.

  • saveAsTextFile: nos vuelca la información en un fichero de texto:
    intValues.saveAsTextFile("results.txt")
    

 

¿Y esto como lo puedo usar con Spark? Dentro de una semana lo veremos con un caso práctico que aunará Twitter con Spark Streaming para hacer analitycs de una forma fácil, sencilla y para toda la familia. De esta forma podremos sacarle todo el partido que queramos y sentirnos poderosos y geeks al mismo tiempo.

aad69873-6bcc-4a0d-84eb-abe375f34c6c

Deja un comentario