¿Qué son los RDD?

Resilient Distributed Datasets (o RDD) son colecciones inmutables, o de solo lectura, de datos que se pueden dividir y distribuir para ser operadas en paralelo. RDD es el corazón de Apache Spark y el mecanismo que le permite ejecutar procesos en memoria de forma distribuida, eficaz y tolerante a fallos.

La diferencia entre Apache Spark y Apache Hadoop MapReduce es la capacidad de ejecutar procesos en memoria. Debido a las limitaciones en tamaño de la memoria en los servidores actuales, se diseño, en la Universidad de Berkeley, esta forma de abstraer la memoria de todos los servidores en un cluster.

En este post explicaré alguna de las características principales. Más adelante, entraré en detalles de como están diseñados internamente los RDD.

¿Cómo funcionan?

Creación

Los RDD solo pueden crearse de forma determinista a partir de datos estables en un sistema de almacenamiento, como HDFS o S3, u otros RDD. Una vez creado, los RDD son inmutables y la única forma de modificarlos es creando nuevos RDD mediante transformaciones, como se verá en el apartado de Operaciones.

val csvDataRDD = sc.textFile("hdfs://mydata.csv")
csvDataRDD: org.apache.spark.rdd.RDD[String] = ParallelCollectionRDD[0]

Otra forma de crear un RDD, aunque quizás sea menos útil, es a partir de una colección de datos, como una lista o un mapa.

val numbersRDD = sc.parallelize(List(1, 2, 3, 4, 5, 6))
numbersRDD: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[0]

Operaciones

RDD permite dos tipos de operaciones: transformaciones y acciones.

Las transformaciones operan sobre los datos para construir otros RDD. Mediante transformaciones vamos refinando los datos. Los tipos de transformaciones disponibles dependerá del tipo de RDD, si almacena, o no, tipos clave->valor. Algunas transformaciones básicas son:

  • map(): Aplica una función a cada elemento en el RDD y devuelve un RDD con los resultados.
  • flatMap(): Aplica una función, que devuelve una colección de datos, a cada elemento en el RDD y devuelve un RDD con todos los resultados de cada iteración unidos en una misma colección.
  • filter(): Devuelve un RDD con los elementos que cumplen una condición.
  • distinct(): Elimina valores duplicados.
  • union(): Crea un RDD con la unión de dos RDD.
  • intersection(): Crea un RDD con los elementos comunes en dos RDD.
  • substract(): Elimina los elementos de un RDD contenidos en otro RDD.
  • cartesian(): Producto cartesiano con los elementos de dos RDD.

Por ejemplo, si quisiera procesar un archivo CSV cuyos valores están separados por el carácter “;”, podría construir un nuevo RDD donde cada linea del archivo CSV sea una lista con todos los valores en dicha linea.

val csvDataRDD = sc.textFile("hdfs://mydata.csv")
val valuesRDD = csvDataRDD.map(line => line.split(";"))

Las acciones operan sobre los datos para producir un valor o exportar datos a un sistema de almacenamiento. Igualmente las acciones dependerán del tipo de RDD. Algunas acciones básicas son:

  • collect(): Devuelve todos los elementos de un RDD.
  • count(): Devuelve el número de elementos en un RDD.
  • countByValue(): Devuelve el número de veces que cada elemento aparece en un RDD.
  • take(n): Devuelve n elementos de un RDD.
  • reduce(): Combina todos los elementos en un RDD y devuelve un valor.
  • foreach(): Aplica una función a cada elemento de un RDD.

Como los RDD trabajan en particiones, todas las transformaciones y acciones podrán ser ejecutadas en paralelo.

Evaluación bajo demanda (Lazy evaluation)

Los RDD están diseñados para ser evaluados bajo demanda. Esto quiere decir que las transformaciones no se evalúan hasta que se invoca una acción. En realidad, cuando se invocan transformaciones en un RDD, lo que se está indicando es un plan de ejecución en lugar de ejecutar las transformaciones. Con esto se consigue que los RDD sepan como han sido creados, y no únicamente los datos que contienen. Esto es de gran utilidad como veremos a continuación.

Persistencia

Las transformaciones son en general procesos muy costosos, porque se transforman muchos datos. Cada vez que se ejecuta una acción se ejecutan todas las transformaciones. Como los datos que se transforman no suelen cambiar entre ejecuciones de acciones, es posible persistir los elementos de un RDD para evitar ejecutar las transformaciones una y otra vez. Solo en caso de error, el RDD volvería a ejecutar las transformaciones, tal y como se verá en el siguiente apartado.

Los RDD se persisten, normalmente, en memoria. Aunque es posible volcar los elementos a otros sistemas de almacenamiento, como S3, HDFS, etc. En el ejemplo siguiente, si no ejecutásemos persist() se ejecutarían las transformaciones map()filter() cada vez que ejecutamos la acción count(). Sin embargo, al persistir filteredValuesRDD, se utilizan directamente los elementos ya computados, sin necesidad de volver a ejecutar todas las transformaciones.

val csvDataRDD = sc.textFile("hdfs://mydata.csv")
val valuesRDD = csvDataRDD.map(line => line.split(";"))
val filteredValuesRDD = valuesRDD.filter(line => line[0] == "demo")
filteredValuesRDD.persist()
filteredValuesRDD.count()
filteredValuesRDD.count()

Lineage

Los RDD guardan información sobre como han sido transformados para, en el caso de error o pérdida de una partición, poder recomponerse a partir de los datos almacenados.

¿Qué es Apache Spark?

Una nueva tecnología se está alzando en el mercado Big Data: Apache Spark. Creada en la UC Berkeley en 2009 y donada a la fundación Apache en 2013, Apache Spark viene a mejorar la capacidad de procesamiento de datos de Apache Hadoop MapReduce que, actualmente, es la tecnología por excelencia a la hora de procesar millones de datos. Son muchas las empresas que están invirtiendo en Hadoop y de ahí su reputación. Uno de los inconvenientes de Hadoop (me refiero a lo largo del artículo a Hadoop MapReduce concretamente) es que no tiene la capacidad de procesar datos en tiempo real, sino que ha sido diseñado para procesar trabajos en lotes. Estos trabajos hacen uso intensivo del sistema de archivos (via HDFS), que implican numerosas operaciones de entrada/salida al disco, con su correspondiente penalización en el tiempo de procesamiento. Aunque esto no suele ser un problema, en vista a su basta aceptación en el mundo empresarial, si supone una limitación en ciertos casos de uso.

Apache Spark

Spark viene a mejorar lo presente (hablando siempre de Hadoop MapReduce) usando principalmente la memoria RAM, y apoyándose en el sistema de archivos solo cuando sea necesario. De esta forma, Spark ofrece procesamiento por lotes y en streaming. Es decir, en tiempo real.

¿Va a enterrar Spark a Hadoop MapReduce? No. Al menos a corto plazo. Hadoop es todo un ecosistema muy maduro y utilizado por muchas empresas. Aunque quizás algún día ambos proyectos se encuentren en el camino.

Mientras tanto, es tal el interés que ha levantado Spark, que muchas de las empresas que ofrecen soluciones para Big Data, como Cloudera concretamente con sus distribuciones de Hadoop, que han llegado a acuerdos para trabajar con Databricks, una de las empresas detrás de Apache Spark.

Morir en Marte por la prosperidad del ser humano

Estreno éste nuevo blog personal para tratar alguna de mis inquietudes: ciencia y tecnología. Aunque ésta primera entrada pueda tener un trasfondo filosófico, no deja de ser algo a tener en cuenta para los no tan lejanos viajes para la conquista del espacio.

El próximo 16 de febrero se conocerán los afortunados que formarán parte del programa Mars One, cuyo objetivo es colonizar Marte en un futuro no muy lejano. A partir de 2024 se realizará una expedición cada dos años. Más de 600.000 personas se han presentado como candidatos y unas 600 son las finalistas. Los elegidos viajarán a Marte sin retorno y con la muerte asegurada, de alguna u otra forma (aterrizaje, radiación solar, hambre…), en la soledad del planeta rojo.

El prestigioso periódico The Guardian publica el siguiente video en su canal de Youtube, dónde se entrevista a tres posibles tripulantes de la misión Mars One. Un físico del Reino Unido, un médico de Mozambique y una mujer iraquí que escapó a los Estados Unidos de América.

Seguro que hay miles de razones para querer formar parte de la expedición y viajar sin retorno a Marte. Ya sea por dejar un legado, como comenta el chico del video, o por que no encuentran su lugar en éste planeta. Pero lo que me hace pensar, independientemente de la viabilidad del proyecto, es si estamos muy arraigados a la Tierra, la familia y los sentimientos que solemos ver que hay más que perder que ganar en todo esto. Al fin y al cabo, nos guste o no, la realidad es que nuestro paso por la Tierra tiene fecha de caducidad. El día que nos toque morir lo haremos causando el mismo daño a la gente que nos quiere y, posiblemente, de forma inesperada sin poder despedirnos de ellos.

¿Es ésta una buena razón para morir?