Salta el contingut

Ecosistema Apache Spark

Introducció

Apache Spark va néixer el 2009 al laboratori AMPLab de la Universitat de Berkeley com a resposta a les limitacions de MapReduce. La idea central era deceptivament simple: i si en lloc d'escriure i llegir d'HDFS entre cada pas del pipeline, mantinguéssim les dades en memòria RAM? El resultat va ser un sistema de processament distribuït fins a 100 vegades més ràpid que MapReduce per a algoritmes iteratius.

El 2025, Apache Spark 3.5 és l'estàndard de facto per al processament de Big Data. El 90% de les ofertes de feina de Data Engineer mencionen Spark. Databricks, la empresa fundada pels creadors de Spark, va assolir una valoració de 43.000 milions de dòlars. PySpark (l'API Python de Spark) domina àmpliament sobre Scala.


Arquitectura de Spark

graph TB
    subgraph Client
        DR[Driver Program\n- SparkContext/SparkSession\n- DAG Scheduler\n- Task Scheduler]
    end

    subgraph ClusterManager["Cluster Manager (YARN / Kubernetes / Standalone)"]
        CM[Gestor de recursos\nAssigna contenidors]
    end

    subgraph Workers
        subgraph Worker1["Worker Node 1"]
            EX1[Executor 1\n- Tasks\n- Memoria cache\n- Disk spill]
        end
        subgraph Worker2["Worker Node 2"]
            EX2[Executor 2\n- Tasks\n- Memoria cache\n- Disk spill]
        end
        subgraph Worker3["Worker Node 3"]
            EX3[Executor 3\n- Tasks\n- Memoria cache\n- Disk spill]
        end
    end

    DR -->|demana recursos| CM
    CM -->|assigna| EX1
    CM -->|assigna| EX2
    CM -->|assigna| EX3
    DR -->|envia tasks| EX1
    DR -->|envia tasks| EX2
    DR -->|envia tasks| EX3
    EX1 -->|resultat final| DR

Driver: el procés principal de l'aplicació Spark. Crea la SparkSession, construeix el pla d'execució (DAG) i coordina els executors. Si el Driver mor, l'aplicació falla.

Executors: els processos que executen les tasques reals. Cada executor té una memòria assignada i un conjunt de CPU cores. Llegeixen i escriuen les dades, realitzen les transformacions i comuniquen els resultats al Driver.

Cluster Manager: el sistema que gestiona els recursos del clúster. Spark suporta YARN (Hadoop), Kubernetes, Mesos i el seu propi Standalone mode.


Components de l'ecosistema Spark

graph TD
    SPARK[Apache Spark Core]
    SQL[Spark SQL\nDataFrames / Datasets]
    STREAM[Structured Streaming]
    MLLIB[MLlib\nMachine Learning]
    GRAPH[GraphX\nProcessament de Grafs]

    SPARK --> SQL
    SPARK --> STREAM
    SPARK --> MLLIB
    SPARK --> GRAPH
Component Descripció Cas d'ús típic
Spark Core Motor d'execució distribuïda, RDDs Base de tots els components
Spark SQL Consultes SQL sobre DataFrames ETL, analytics, BI
Structured Streaming Processament en temps real Pipelines de streaming, Kafka
MLlib Algoritmes ML distribuïts Models sobre grans volums
GraphX Algoritmes de grafs distribuïts Xarxes socials, recomanació

MLlib: Machine Learning distribuït

MLlib és la biblioteca de Machine Learning distribuïda de Spark. La seva Pipeline API segueix el patró de scikit-learn:

from pyspark.ml import Pipeline
from pyspark.ml.feature import Tokenizer, StopWordsRemover, HashingTF, IDF, StringIndexer
from pyspark.ml.classification import LogisticRegression
from pyspark.ml.evaluation import MulticlassClassificationEvaluator

# Exemple: classificació de ressenyes de productes
df_ressenyes = spark.createDataFrame([
    ("El producte és excel·lent, molt recomanable!", "positiu"),
    ("Terribles. No compreu mai aquest producte.", "negatiu"),
    ("Qualitat mediocre, no val el preu que demanen.", "negatiu"),
    ("Impressionant! Supera totes les expectatives.", "positiu"),
], ["text", "etiqueta"])

# Pipeline de NLP
tokenizer = Tokenizer(inputCol="text", outputCol="paraules")
remover = StopWordsRemover(
    inputCol="paraules",
    outputCol="paraules_netes",
    stopWords=StopWordsRemover.loadDefaultStopWords("catalan")
)
hashingTF = HashingTF(inputCol="paraules_netes", outputCol="tf", numFeatures=10000)
idf = IDF(inputCol="tf", outputCol="features")
indexer = StringIndexer(inputCol="etiqueta", outputCol="label")
lr = LogisticRegression(maxIter=100, regParam=0.01)

pipeline = Pipeline(stages=[tokenizer, remover, hashingTF, idf, indexer, lr])

# Divisió train/test
train, test = df_ressenyes.randomSplit([0.8, 0.2], seed=42)

# Entrenar i avaluar
model = pipeline.fit(train)
prediccions = model.transform(test)
evaluador = MulticlassClassificationEvaluator(metricName="accuracy")
print(f"Precisió: {evaluador.evaluate(prediccions):.2%}")

PySpark vs Scala Spark el 2025

La pregunta de si usar PySpark o Spark amb Scala és recurrent. La resposta el 2025 és clara per a la majoria de casos:

Usa PySpark quan: - El teu equip domina Python (el 90% dels Data Scientists i Data Engineers) - Necessites integrar biblioteques Python (pandas, scikit-learn, PyTorch, Hugging Face) - Vols iteració ràpida i notebooks interactius - El rendiment és acceptable per al teu cas d'ús (Spark 3.x amb Pandas on Spark és molt ràpid)

Considera Scala quan: - El rendiment és crític i l'overhead Python → JVM importa (casos rars amb Spark 3.5+) - El teu equip ve d'un background Java/Scala - Construeixes extensions personalitzades de Spark (custom transformers, optimitzadors)


Configuració i tuning de Spark

# Configuració d'una SparkSession per a producció
spark = SparkSession.builder \
    .appName("Pipeline_Produccio_2025") \
    .config("spark.executor.memory", "8g") \
    .config("spark.executor.cores", "4") \
    .config("spark.executor.instances", "10") \
    .config("spark.driver.memory", "4g") \
    .config("spark.driver.maxResultSize", "2g") \
    .config("spark.sql.shuffle.partitions", "200") \
    .config("spark.sql.adaptive.enabled", "true") \
    .config("spark.sql.adaptive.coalescePartitions.enabled", "true") \
    .config("spark.serializer", "org.apache.spark.serializer.KryoSerializer") \
    .config("spark.sql.parquet.filterPushdown", "true") \
    .config("spark.sql.parquet.mergeSchema", "false") \
    .config("spark.default.parallelism", "40") \
    .getOrCreate()

Regles de tuning:

  1. Particions: spark.sql.shuffle.partitions per defecte és 200. Per a datasets petits (<10 GB), reduir a 8-50 millora el rendiment.
  2. Memòria: spark.executor.memory hauria de ser un 75% de la RAM del worker disponible per a Spark.
  3. Skew: si una partició és molt més gran, usar SKEW_HINT o AQE skew join.
  4. Cache: usar .cache() o .persist(StorageLevel.MEMORY_AND_DISK) per a DataFrames usats múltiples vegades.
  5. Broadcast: si un DataFrame és petit (<100 MB), forçar un broadcast join per evitar shuffles.
from pyspark.sql.functions import broadcast

# Forçar un broadcast join
df_resultat = df_gran \
    .join(broadcast(df_petit), on="id_categoria", how="left")

Databricks: la plataforma Spark empresarial

Databricks és la plataforma de Big Data i IA més usada a les empreses el 2025. Fundada pels creadors de Spark, ofereix una experiència molt superior al Spark "pur":

  • Databricks Runtime: versió de Spark optimitzada, típicament 5-10x més ràpida que open-source
  • Unity Catalog: governança unificada de dades, ML i IA generativa
  • Delta Sharing: compartir dades de forma segura entre organitzacions
  • Notebooks: entorn col·laboratiu en temps real (com Google Docs per a notebooks)
  • Workflows: orquestració de pipelines sense Airflow addicional
  • MLflow integrat: tracking d'experiments i desplegament de models

Databricks Community Edition

Databricks ofereix un account Community Edition gratuït a community.cloud.databricks.com que inclou un clúster de processament limitat però suficient per aprendre. Inclou exemples de datasets i notebooks de tutorial.


Miniactivitat: Explorar el Spark UI

Mentre executes qualsevol job Spark (local o en Docker), obre el navegador a http://localhost:4040. Aquí pots veure: - Tots els jobs en execució i completats - El DAG (grafo de dependències) de cada job - Les estadístiques de cada tasca (temps, bytes llegits/escrits, shuffle) - L'ús de memòria dels executors

Identifica quines transformacions causen un "shuffle" (redistribució de dades entre executors) i quines no. Els shuffles són els principals colls d'ampolla en Spark.