Dopo aver parlado di map-reduce e YARN, è d’obbligo un articolo anche sul misterioso Apache Spark, quello che “fa le cose più veloci di Hadoop”. Dato che questa aura di mistero che circonda le ultime tecnologie di datawarehouse distribuito (si, le cose le fa più veloci, ma esattamente cosa fa???) non mi piace, vediamo come funziona.

Spark è stato progettato e implementato da un gruppo di ricercatori dell’università di Berkeley (a San Francisco, aridaje..) nel 2010. Il funzionamento è descritto in un loro paper in modo abbastanza preciso e relativamente comprensibile.

In sostanza, costoro si sono chiesti come potevano migliorare le performance dei sistemi distribuiti map-reduce, dato che:

map-reduce è lento , perchè ad ogni job legge i dati da HDFS (se è hadoop), carica da disco, mappa, riduce, scrive i dati su disco

, perchè ad ogni job legge i dati da HDFS (se è hadoop), map-reduce è poco adatto a fare elaborazioni complesse e ripetute su dataset , perchè in una sequenza di job c’è una lettura e una scrittura su sisco ad ogni job

, perchè in una sequenza di job c’è una lettura e una scrittura su sisco ad ogni job map reduce non è adatto a operazioni di tipo iterativo (cicli di alalisi e aggiornamenti consecutivi sullo stesso set di dati, tipici quando ad ogni aggiornamento si deve riprendere in mano il precedente set di dati elaborati e aggiornarlo di nuovo)

Hanno quindi pensato di progettare un nuovo sistema di elaborazione distribuita che:

Consentisse di specificare sequenze complete di operazioni, anche iterative, in un solo job, riducendo la complessità del codice dell’applicazione di elaborazione Permettesse di mantenere i dati in memoria fino al termine dell’intera sequenza di elaborazione, evitando di leggere e scrivere su disco (a meno che non fosse lo stesso sviluppatore a richiederlo) Garantisse la stessa capacità di Hadoop di resistere a fault di nodi del cluster e quindi perdita di parte dei dati e di assegnare l’elaborazione di un blocco di dati allo stesso nodo dove i dati erano immagazzinati (data locality)

Hanno quindi sviluppato il concetto di Resilient Distributed Dataset (RDD) che è la teoria alla base del sistema Spark. Un RDD è in sostanza un set di dati suddiviso in partizioni (per capirsi: un a tabella chiave-valore spezzata in tante sotto-tabelle o un file spezzato in tanti segmenti) che ha alcune proprietà chiave per il suo funzionamento:

Ogni RDD è immutabile , cioè una volta creato non lo si può cambiare, se non creandone un altro mediante una trasformazione

, cioè una volta creato non lo si può cambiare, se non creandone un altro mediante una trasformazione Ogni RDD può solo essere creato inizialmente a partire dai dati su disco (presi da HDFS) oppure è dopo a partire da altri RDD Le trasformazioni possibili per creare nuovi RDD sono poche, deterministiche e ripetibili : si può mapparli (cioè trasformarli da un array di chiave-valore a un altro array di chiave-valore), filtrarli (partire da un array e crearne un altro filtrando i dati), unire due RDD Questo approccio serve perchè un singolo pezzo (partizione) del RDD possa essere ricostruito a partire dalla sequenza di trasformazioni che lo hanno generato

Ogni RDD può restare in memoria oppure essere materializzato su disco , a scelta del programmatore (RDD in memoria ma inutilizzati da tempo vengono comunque automaticamente scaricati su disco dal processo di gestione che gira sul worker node)

, a scelta del programmatore (RDD in memoria ma inutilizzati da tempo vengono comunque automaticamente scaricati su disco dal processo di gestione che gira sul worker node) Ogni RDD è descritto da un set completo di metadati che consentono la ricostruzione di una delle sue partizioni in caso di fault: dove si trovano le partizioni, quali sono gli RDD padre, quale è la sequenza di trasformazioni, detta LINEAGE, che lo hanno generato

Spark nasce come un sistema per creare e gestire job di analisi basati su trasformazioni di RDD. Dato che gli RDD nascono e vivono in memoria, l’esecuzione di lavori iterativi o che trasformano più volte un set di dati, sono immensamente più rapide di una sequanza di map-reduce. 10, anche 100 volte più veloci, perchè il disco non viene mai (o quasi mai) impiegato nell’elaborazione.

Spark è stato sviluppato in circa 20mila righe (rispetto a Hadoop che ne ha più di 100mila) e funziona usando HDFS o Hbase come file system distribuito e Apache Mesos come piattaforma di cluster management. La attuale versione supporta però anche il deployment su YARN (che alla fine è una piattaforma di cluster management…).

Manco a dirlo, l’architettura di Spark si compone di un master scheduler e di tanti worker che eseguono il calcolo distribuito (architettura stra-standard di un sistema di calcolo distribuito).

Il funzionamento complessivo di Spark avviene in questo modo:

Lo sviluppatore scrive la propria applicazione (in Scala…) definendo il set di trasformazioni e calcoli che desidera fare sul set di dati. Ad esempio una elaborazione su file di log in cui l’obiettivo è contare quanti errori relativi a un certo tipo di applicazione si sono verificati in un determinato arco di tempo. Nel programma possono esserci trasformazioni (generano un RDD) o azioni (fanno calcoli sul RDD e restituiscono il risultato), organizzate in sequenze o anche in cicli iterativi. Nel caso dei log ci saranno mappature (cioè caricamenti in array), filtraggi (selezione solo dei log di errore e poi di quelli in un certo intervallo di tempo) e una azione di conteggio sul dataset finale a valle del filtraggio.

definendo il set di trasformazioni e calcoli che desidera fare sul set di dati. Ad esempio una elaborazione su file di log in cui l’obiettivo è contare quanti errori relativi a un certo tipo di applicazione si sono verificati in un determinato arco di tempo. Nel programma possono esserci trasformazioni (generano un RDD) o azioni (fanno calcoli sul RDD e restituiscono il risultato), organizzate in sequenze o anche in cicli iterativi. Nel caso dei log ci saranno mappature (cioè caricamenti in array), filtraggi (selezione solo dei log di errore e poi di quelli in un certo intervallo di tempo) e una azione di conteggio sul dataset finale a valle del filtraggio. Il programma viene passato allo scheduler di Spark che sulla base del programma stesso costruisce il grafo delle trasformazioni degli RDD che vengono richieste

che sulla base del programma stesso In base al grafo ottenuto (che è un Directed Acyclic Graph o DAG , cioè un grafo senza cicli in cui ogni nodo è una partizione di un RDD e ogni vertice è una trasformazione), lo scheduler determina il miglior modo possibile per distribuire i lavori di trasformazione sui nodi . Deve tenere conto di due possibili tipi di dipendenze tra RDD consecutivi: Dipendenza “stretta” (narrow): il RDD figlio dipende 1-1 dal RDD padre. In questo caso i job sulle partizioni possono essere eseguiti anche in parallelo Dipendenza “ampia” (wide): il RDD figlio è una combinazione n-1 di più RDD padri. Succede tipicamente con le operazioni di reduce, in cui n set di sorgente vengono combinati in un numero minore di set di destinazione. In questo caso lo scheduler deve attendere che tutti gli RDD padre siano stati creati prima di schedulare le trasformazioni

, cioè un grafo senza cicli in cui ogni nodo è una partizione di un RDD e ogni vertice è una trasformazione), lo scheduler . Deve tenere conto di due possibili tipi di dipendenze tra RDD consecutivi: Al termine dell’esecuzione lo scheduler ritorna il risultato finale al client che ha lanciato il programma (analogamente a map-reduce, ritorna il puntatore al file con il risultato, che sarà stato materializzato su disco)

Ovviamente gli sviluppatori di Spark fanno notare nel loro paper che Spark è stao pensato per dare il meglio nelle elaborazioni batch, mentre non funziona in sistemi distribuiti che richiedono frequenti update granulari sui set di dati archiviati. In quel caso, meglio evitare di usare Spark o hadoop e invece usare un normale sistema di database o un sistema di distributed shared memory, che sono invece pensati per questo.

Il che fa intuire perchè certi oggetti come Hive o Shark SQL non devono trarre in inganno e far pensare che Spark o Hadoop possano essere usati in alternativa a un DB SQL. In realtà queste interfacce SQL-like sono pensate per tutti coloro che (sbagliando) si rifiutano di imparare i nuovi paradigmi di estrazione dei dati abilitati da queste piattaforme e si ostinano imperterriti a usare SQL perchè è l’unica cosa che conoscono. Solo che non ci si può fare tutto, ma solo delle query di estrazione “massive”. Niente update o insert granulari tipiche di un sistema transazionale, quelle avrebbero una performance pessima. Alla fine, Hadoop o Spark sono pur sempre dei sistemi di datawarehouse.