Benvenuto alla prima lezione del corso di PySpark. Se hai fatto un po’ di lavoro coi dati per qualche tempo, hai visto succedere la stessa cosa a una mezza dozzina di colleghi: aprono un CSV in Pandas, il kernel mastica 16 GB di RAM, la ventola del portatile inizia a suonare come un piccolo aereo, il kernel muore, e loro postano in Slack chiedendo se sia il caso di “usare Spark”. Quel momento, quello in cui Pandas finisce la pazienza e serve un martello più grosso, è di cosa parla questo corso. Sessanta lezioni, si parte dalla teoria e si arriva a fare tuning di job di produzione veri contro l’object storage. Alla fine saprai cos’è Spark, quando ricorrere a lui, quando non ricorrere a lui, e come scrivere PySpark che non ti faccia fare brutte figure in code review.
Prima di toccare codice, però, dobbiamo parlare della frase che ha messo Spark sulla mappa per prima cosa: big data. Il termine è stato così sistematicamente massacrato dai reparti marketing dal 2012 in avanti che la maggior parte degli ingegneri con cui lavoro ha un leggero riflesso di fastidio quando lo sente. È giusto. La maggior parte delle cose vendute come “big data” si rivela un CSV da 4 GB su cui qualcuno si è dimenticato di mettere un dtype=. Ma l’idea di fondo è reale, i problemi di ingegneria sono reali, e il motivo per cui Spark esiste è reale. Quindi mettiamo a fuoco i termini.
Cosa significa davvero “big”
Big data sono dati che non ci stanno, o che non si processano, su un solo computer in un tempo ragionevole. Tutto qui. Non c’è un numero magico di gigabyte o di righe oltre il quale i dati diventano Big con la B maiuscola. È una relazione tra tre cose: quanti dati hai, cosa stai cercando di farci, e che hardware hai a disposizione.
Un file Parquet da 200 GB non è big data su un’istanza EC2 da 96 core con 768 GB di RAM e un disco NVMe veloce. Puoi tirartelo tutto in memoria e chiuderla lì. Lo stesso file da 200 GB è big data su un MacBook con 16 GB di RAM, dove non riesci nemmeno ad aprirlo senza paginare su disco e ridurti a un crawl. Stessi dati, “big” diverso.
La definizione classica, quella che il tuo manager probabilmente ha imparato da una slide McKinsey verso il 2014, sono le quattro V: Volume, Velocity, Variety e Veracity. Conviene saperle perché qualcuno te le citerà a un colloquio, ma conviene anche sapere che due di esse sono reali e due sono lì soprattutto a riempire una slide.
Volume è quanti. Questo conta. La quantità di dati che stai processando cambia davvero quali strumenti puoi usare. Cinque gigabyte sono un problema da Pandas. Cinque terabyte sono un problema da Spark. Cinque petabyte sono un problema da “dobbiamo parlare del tuo data warehouse”. Il Volume è la V che ti sveglia alle 3 di notte quando un job esaurisce il disco.
Velocity è quanto velocemente arrivano. Anche questa conta, soprattutto nel 2026 in cui lo streaming è diventato il default invece dell’eccezione. Un job che processa un dump giornaliero statico è una forma di problema; un job che deve stare al passo con 100.000 eventi al secondo da un topic Kafka è una forma di problema fondamentalmente diversa. Spark ha lo Structured Streaming, lo vedremo nel modulo 9, ed esiste proprio perché il batch da solo ha smesso di bastare ormai un decennio fa.
Variety è quanto sono diversi i formati: JSON qui, Parquet là, un export SQL da un’altra parte, un feed XML perché il payment provider di qualcuno è fermo al 2009. Questa è reale, ma non è una vera difficoltà tecnica come Volume e Velocity. È perlopiù un problema “devi scrivere più parser”, e qualunque motore distribuito ragionevole ti permette di attaccare un connettore o scrivere una UDF. Variety è nella lista perché regge bene un’allitterazione su slide.
Veracity è quanto sono affidabili i dati. Questa, con rispetto, non è una proprietà dei dati; è una proprietà della pipeline dei dati. Se il tuo CRM produce spazzatura, nessuna quantità di Spark, Snowflake o geometria sacra la sistemerà. La data quality è un problema reale, ma è un problema di processo e di tooling, non un problema di “mi serve un motore distribuito”. Il fatto che questa V esista è perlopiù perché qualcuno aveva bisogno di una quarta V per arrotondare la regola del tre in una regola del quattro.
Quindi la versione onesta: Volume e Velocity sono perché ricorri al calcolo distribuito. Variety e Veracity sono perché hai un lavoro dopo che ci hai ricorso.
Le soglie, all’incirca
Ecco le regole empiriche che uso io, e che la maggior parte dei data engineer di cui mi fido ti citerebbe a pranzo se gli chiedessi. Nessuna di queste è una legge della fisica; cambiano con l’hardware, con i prezzi del cloud, con com’è fatto il tuo dataset, e con quanta pazienza hai. Ma sono un primo taglio utile.
Fino a circa 10 GB: Pandas va bene. Polars è più veloce e usa meno memoria e probabilmente dovresti usare quello, ma Pandas funziona. DuckDB va bene. SQLite va bene. Un Postgres single-node va bene. Niente di tutto questo è un problema da Spark. Se qualcuno ricorre a un motore distribuito per processare un CSV da 4 GB, sta per passare tre giorni a scrivere infrastruttura per qualcosa che un df = pd.read_csv(...) avrebbe gestito in dieci minuti.
Da circa 10 GB a circa 100 GB: la zona di mezzo scomoda. Un portatile moderno con 32 o 64 GB di RAM tecnicamente regge questo range in Pandas se stai attento ai dtype e non ti dispiace che la macchina sia inutilizzabile mentre gira. Polars la gestisce con grazia. DuckDB la gestisce in modo brillante: è costruita esattamente per questo range. Una VM cloud potente la gestisce senza fare una piega. Puoi usare Spark qui e funzionerà, ma stai pagando la tassa dei sistemi distribuiti (tempo di startup del cluster, overhead di shuffle, debug più complicato) per non molto beneficio. Questo è il range in cui la risposta giusta è sempre più “DuckDB su un single node grasso”, e dove il marketing di Spark si è storicamente sopravvalutato.
Da circa 100 GB a circa 1 TB: qui Spark inizia a guadagnarsi lo stipendio. Puoi ancora schiacciarlo su una macchina potente se hai il budget, e tool come DuckDB tengono fino a un certo punto, ma il parallelismo su più macchine inizia a essere genuinamente più veloce, soprattutto per i join su tabelle larghe e le aggregazioni che escono nei warehouse veri. Spark è un default perfettamente ragionevole in questo range. Lo è anche Snowflake. Lo è anche BigQuery. Scegli in base a cosa il tuo team già gestisce.
Da circa 1 TB in su: una macchina sola è senza speranza, o quantomeno catastroficamente costosa. Un motore distribuito è l’unica risposta sensata. Spark, Trino, Snowflake, BigQuery, Athena, Synapse: prodotti diversi, stessa forma fondamentale: spezzi i dati su molte macchine, processi in parallelo, combini i risultati. Questo è il terreno di casa di questo corso.
Da circa 100 TB in su: ora sei in territorio dove i problemi interessanti non sono “riesco a far girare questa query” ma “riesco a far girare questa query senza far fallire l’azienda”. Ottimizzazione dei costi, layout dei dati, partition pruning, predicate pushdown, broadcast join: tutto il materiale della seconda metà del corso diventa il lavoro vero. Chiunque può lanciare un SELECT * su un petabyte. Lanciarlo per 4 euro invece che per 4.000 è la skill.
Perché il parallelismo è difficile
La risposta ingenua a “i miei dati non ci stanno su una macchina sola” è “usa più macchine”. La risposta tecnica è che è approssimativamente corretto ma enormemente più complicato di come suona. Il motivo per cui i motori di calcolo distribuito esistono come un loro campo di ingegneria è che tutto quanto segue deve funzionare, e deve funzionare in presenza di guasti, perché l’astrazione sia utile.
Spezzare i dati. Devi decidere, per ogni job, come tagliare l’input in pezzi abbastanza piccoli da starci sui singoli worker. Sembra banale finché non ti ricordi che i dati vivono su S3, o HDFS, o Kafka, o tutti e tre insieme, e gli split devono allinearsi con il modo in cui lo storage stesso è disposto. Spark spende una frazione significativa del proprio codice sorgente a capire come spezzare gli input in “partition”.
Coordinare il lavoro. Una volta che hai cento worker, qualcuno deve dire a ognuno cosa fare, in che ordine, e cosa fare quando uno di loro impiega dieci volte di più degli altri (il temuto “straggler”). Quel qualcuno è uno scheduler, e scriverne uno che funziona alla scala è una piccola carriera. Il processo driver di Spark è esattamente questo.
Lo shuffling. La maggior parte delle computazioni non banali (join, group-by, sort) richiedono che i dati vengano spostati tra macchine perché tutti i record con la stessa chiave finiscano sullo stesso worker. Si chiama shuffle, ed è la singola operazione più costosa in un motore distribuito. Spenderemo molte lezioni più avanti nel corso su come evitare gli shuffle, minimizzare gli shuffle e sopravvivere agli shuffle che non puoi evitare.
Combinare i risultati. Dopo che tutti i worker hanno fatto il loro pezzo, qualcuno deve assemblare la risposta finale. A volte è banale (concatenare partition). A volte non lo è (un sort globale su cento macchine è un algoritmo genuinamente interessante).
Gestire i guasti. Alla scala, il guasto non è un caso limite; è una condizione di stato stazionario. Con 1.000 worker che girano per un’ora, alcuni di loro falliranno: dischi rotti, intoppi di rete, istanze spot cloud che vengono riprese, OOM da una chiave skewed, e via dicendo. Il framework deve rilevare i guasti, rieseguire il lavoro perso e proseguire senza far ripartire l’intero job. Questa è la singola ragione più grande per cui non scrivi job distribuiti da zero in Python. È anche il motivo per cui un job Spark che “dovrebbe metterci 10 minuti” a volte ne mette 40: da qualche parte un worker è morto e tre stage sono stati ritentati.
Un motore di calcolo distribuito è, in sostanza, un pezzo di software che nasconde tutto quanto sopra dietro un’API che sembra uno stesse lavorando con un solo grande DataFrame. L’intera proposta di Spark è che scrivi codice come se avessi una macchina sola con RAM infinita, e il motore in silenzio gestisce lo splitting, lo scheduling, lo shuffling, il combining e il recovery per te. Quando funziona, è magia. Quando non funziona, passi una settimana a leggere log di stage e le parole “shuffle spill” entrano nel tuo vocabolario quotidiano.
Cosa arriva dopo
Le prossime cinque lezioni sono ancora teoria, niente codice ancora: parte nella lezione 7 con la nostra prima SparkSession. La lezione 2 guarda MapReduce e Hadoop, il sistema che ha dimostrato che il calcolo distribuito poteva essere reso trattabile per programmatori normali, e il modello che Spark ha ereditato e migliorato. La lezione 3 introduce Spark stesso: il paper di Berkeley del 2010, l’astrazione di dataset in memoria, e cosa significa davvero “100 volte più veloce di Hadoop” una volta che leggi le note in piccolo. La lezione 4 è l’architettura: driver, executor, cluster manager, e come un job in pratica ci scorre dentro. La lezione 5 sono le astrazioni di dataset (RDD, DataFrame, Dataset) e quale dovresti davvero usare nel 2026 (spoiler: i DataFrame). La lezione 6 chiude il modulo di teoria con PySpark contro Scala contro SQL: tre modi di parlare con lo stesso motore, e quando ognuno è la scelta giusta.
Poi nel modulo 2 installiamo Spark, scriviamo il nostro primo job e non guardiamo mai più le quattro V.
Per approfondire, il riferimento canonico per tutto in questo corso è la documentazione di Apache Spark. Mettila tra i preferiti adesso; ci torneremo.