Lunedì 9 novembre 2026
L’edizione di oggi
Millebit
AI · Software · QA · Sicurezza
Il segnale, non il rumore.

Gli articoli di Millebit sono scritti con l’intelligenza artificiale a partire da fonti citate in ogni pezzo. Come lavoriamo

Sviluppo software

Forzare il broadcast join in Spark risolve lo shuffle ma porta a OOM cronici

Testo scritto con l’intelligenza artificiale a partire da una fonte citata, senza revisione umana prima della pubblicazione.

Copiare una tabella in memoria su ogni executor funziona all'inizio, poi la crescita dei dati e la deserializzazione trasformano il rimedio in una bomba a orologeria.

Illustrazione generata con l’intelligenza artificiale

Forzare un broadcast join per tagliare lo shuffle in Spark accelera il primo giorno, poi apre la strada a OutOfMemoryError ricorrenti1.

Caso tipico: una tabella transazioni con miliardi di righe si unisce a una dimensione a variazione lenta con pochi milioni di righe. La Spark UI mostra la fase di Join rallentata dallo shuffle. Un ingegnere impone il broadcast della dimensione. All’inizio il job corre. Quando la dimensione cresce, l’heap degli executor cede, la pipeline diventa instabile1.

I segnali in produzione sono precisi. Il job resta in esecuzione a lungo, gli executor si bloccano a intermittenza, compaiono ExecutorLostFailure o Heartbeat timed out. Driver o executor si fermano con java.lang.OutOfMemoryError: Java heap space. Nella scheda Executors, il GC Time schizza oltre il Task Time. Aumentare i nodi non aiuta se un singolo task prova a distribuire in broadcast una tabella più grande della memoria per executor1.

La soglia di auto-broadcast è un’indicazione, non una garanzia. Spark valuta spark.sql.autoBroadcastJoinThreshold, in genere 10 MB. Ma il peso in memoria non coincide con la dimensione a disco. Un Parquet da 10 MB può gonfiarsi a 50-100 MB quando Spark deserializza in InternalRow. Per verificarlo, apri il piano SQL nella Spark UI, sezione Details, e cerca l’operatore BroadcastExchange. Se la stima sfiora la soglia, stai camminando sul bordo. Puoi disattivare la funzione in sessione per capire l’impatto: spark.conf.set("spark.sql.autoBroadcastJoinThreshold", "-1") Oppure imporla solo quando sai che il dataset è davvero piccolo: from pyspark.sql.functions import broadcast df_large.join(broadcast(df_small), "id")1

Altro errore classico: trasmettere il lato sbagliato del join. Il dataset in broadcast viene copiato su ogni executor. Se imposti in broadcast il tavolo grande, cloni un volume enorme su tutti i nodi. Controlla l’explain: nel BroadcastHashJoin il lato “Build” deve essere quello ridotto. Se serve, indica in modo esplicito quale DataFrame trasmettere: from pyspark.sql.functions import broadcast result = large_df.join(broadcast(small_df), "join_key")1

Lo skew dei dati può trarre in inganno. Una partizione macina gigabyte, le altre processano megabyte. Lo vedi in Spark UI da Task Deserialization Time e Shuffle Read Size. Il broadcast non elimina lo squilibrio, sposta la pressione sulla memoria degli executor. Rimedio proposto: salting. Aggiungi un prefisso casuale alla chiave di join della tabella grande e replica la chiave nella tabella piccola, così dividi la partizione calda in più segmenti gestibili1.

Attenzione alle catene di broadcast. Pattern come A.join(broadcast(B)).join(broadcast(C)) possono restare entro i limiti singolarmente, ma sommati saturano l’heap. Se il piano fisico mostra più BroadcastHashJoin nella stessa stage, confronta l’ingombro totale degli oggetti trasmessi con spark.executor.memory. Alternative: pre-aggregare o mettere in cache l’esito del primo join quando il volume lo consente, oppure scegliere SortMergeJoin per tenere prevedibile l’uso di memoria1.

Meglio prevenire con salvaguardie di processo. Integra gli explain plan in CI/CD e fallisci i test se un SortMergeJoin diventa all’improvviso un BroadcastHashJoin. Mantieni conservativa la soglia in produzione e alzala solo per job specifici. Rivedi spark.memory.fraction secondo il carico di join per evitare OOM in fase di shuffle. Valuta spark.sql.broadcastTimeout: se servono più di cinque minuti per distribuire il dataset, forse non va trasmesso in broadcast1.

In fase di capacity, ricordalo: se un task prova a trasmettere un oggetto troppo grande, aggiungere nodi non risolve il collo di bottiglia locale alla memoria per executor1.

Fonti

  1. Stop Overusing Broadcast Joins to Fix Spark Shuffles dev.to · 9 novembre 2026
Come è nato questo articolo Scritto da un sistema di intelligenza artificiale, con controlli automatici

Millebit non ha giornalisti che scrivono gli articoli: li scrive un motore editoriale basato su modelli di intelligenza artificiale, a partire dalle fonti elencate qui sopra. Prima di uscire, ogni testo passa questi controlli:

  • Rilettura. Un passaggio toglie i tic della prosa generata da un modello, come i contrasti costruiti e le frasi che annunciano invece di informare, senza toccare fatti e fonti.
  • Fedeltà alle fonti. Un passaggio dedicato toglie ogni affermazione che le fonti non sostengono. Valutazione su questo pezzo: 9,5/10.
  • Anti-cliché. Un filtro scarta frasi fatte, enfasi e tic della prosa artificiale: superato.
  • Giudizio editoriale. Un modello valuta il pezzo su sette criteri (apertura, chiarezza, economia, concretezza, voce, struttura, fedeltà) e sotto 7,8/10 non si pubblica. Questo articolo ha preso 8,3/10 dopo 2 revisioni.

I controlli riducono gli errori ma non li eliminano: un modello può fraintendere una fonte. Per le decisioni importanti, risali alla fonte originale. Se trovi un errore, segnalacelo.

Come funziona Millebit, nel dettaglio →

Hai trovato un errore? Segnalacelo
Tipo di segnalazione

Leggiamo ogni segnalazione. Le correzioni fatte sono elencate in Correzioni. L’email, se la lasci, serve solo a risponderti.