
Delta Lake: ACID su data lake
Delta Lake: transazioni ACID, time travel e schema enforcement su S3.
Cosa imparerai
- Comprendere il problema analitico e il contesto decisionale
- Applicare esempi, metriche e controlli a casi reali
Collegamenti
import pandas as pd
Delta Lake: ACID su data lake
Un data lake fatto di soli file Parquet fatica a gestire update, rollback, concorrenza e correzioni storiche. Delta Lake risponde proprio a questi limiti: aggiunge un transaction log, il versioning e garanzie ACID che trasformano file distribuiti in tabelle affidabili. Conviene leggere la lezione tenendo a mente quando serve davvero, cioè quando devi correggere dati, garantire letture coerenti e ricostruire versioni passate senza che ogni fix diventi un intervento manuale rischioso.
Il problema da risolvere
Il caso tipico: una tabella ordini riceve correzioni tardive e cancellazioni GDPR mentre più job leggono e scrivono nello stesso intervallo. Senza un transaction log ACID, un lettore può vedere uno stato a metà, una scrittura può sovrascrivere quella di un altro job e la storia delle modifiche va persa. Delta evita letture incoerenti, overwrite manuali e perdita di storia.
La domanda guida non è “cos’è Delta Lake?” ma “quale operazione diventa meno rischiosa se la tabella è transazionale?”. Update puntuali, rollback dopo un caricamento sbagliato, audit di cosa conteneva la tabella un mese fa: sono questi i momenti in cui il file lake tradizionale mostra i suoi limiti.
Come ragionare sulla scelta
Prima di adottare Delta conviene fissare l’unità su cui ragioni e il segnale che osservi, così la decisione resta discutibile invece di affidata alla moda del momento.
| Elemento | Definizione operativa | Controllo minimo |
|---|---|---|
| Unità di analisi | Oggetto su cui misuri l’affidabilità | Tabella, partizione, versione |
| Segnale | Cosa osservi per decidere | Frequenza di update, concorrenza, obblighi di correzione |
| Baseline | Rispetto a cosa giudichi | Comportamento attuale con soli file Parquet |
| Rischio residuo | Errore che resta dopo l’adozione | Lock-in sull’ecosistema sbagliato |
La scelta è solida quando un altro ingegnere può ricostruire il ragionamento, criticare le assunzioni e arrivare alla stessa conclusione partendo dagli stessi vincoli.
Delta vs Iceberg
La domanda pratica più frequente è quale dei due table format adottare. La differenza principale è l’ecosistema: Delta nasce dentro Databricks e Spark, Iceberg è pensato per più motori.
| Delta Lake | Iceberg | |
|---|---|---|
| Creatore | Databricks | Netflix → Apache |
| Ecosistema primario | Spark, Databricks | Multi-engine (Trino, Presto, Flink, Spark) |
| Compaction | OPTIMIZE command | Manuale o via tool esterni |
| Z-ordering | Sì (multi-dimensionale) | No nativo |
| Change Data Feed | Sì | Sì (via snapshot diffs) |
Se lavori in Databricks, Delta Lake è la scelta naturale. Se sei multi-cloud o multi-engine, Iceberg è più portabile.
Operazioni Delta Lake essenziali
Tre operazioni coprono la maggior parte del lavoro quotidiano: l’upsert via merge, il time travel per leggere uno stato passato e l’OPTIMIZE con Z-ordering per tenere i file compatti e veloci da scansionare.
# Scrittura con merge (upsert)
from delta.tables import DeltaTable
delta_table = DeltaTable.forPath(spark, "s3://lake/orders")
delta_table.alias("t").merge(
new_orders.alias("s"), "t.order_id = s.order_id"
).whenMatchedUpdateAll().whenNotMatchedInsertAll().execute()
# Time travel: leggi tabella com'era 7 giorni fa
df = spark.read.format("delta").option("versionAsOf", 7).load("s3://lake/orders")
# Compaction e Z-ordering
spark.sql("OPTIMIZE orders ZORDER BY (customer_id, order_date)")
Schema evolution in Delta Lake
Delta Lake gestisce sia l’evoluzione automatica dello schema, per esempio l’aggiunta di colonne, sia lo schema enforcement, che blocca le scritture con colonne non previste. Il comportamento si configura con mergeSchema: lo lasci attivo quando una sorgente legittima cambia spesso, lo tieni spento quando vuoi che uno schema imprevisto fallisca subito invece di corrompere silenziosamente la tabella.
Delta come parte di un sistema
In un progetto reale Delta non vive isolato. Fa parte di un sistema fatto di decisioni, dati disponibili, vincoli tecnici e incentivi organizzativi. Il rischio del principiante è trattarlo come una definizione da memorizzare; la pratica professionale è capire quale problema risolve e cosa succede quando le assunzioni saltano, per esempio quando scegli Delta in un contesto multi-engine in cui Iceberg sarebbe stato più adatto.
La prima domanda non è “quale comando uso?” ma quale decisione dipende dall’affidabilità della tabella. Un transaction log ha valore solo se riduce incertezza su una scelta concreta: poter correggere un dato senza fermare i lettori, poter dimostrare cosa conteneva la tabella a una certa data, poter fare rollback dopo un errore. Un modo concreto di impostare il lavoro:
- definire il problema in linguaggio business;
- identificare l’unità di analisi corretta: tabella, partizione, versione;
- controllare se i dati misurano davvero il fenomeno o solo una sua ombra;
- costruire una metrica interpretabile;
- segmentare per evitare che la media nasconda pattern opposti;
- trasformare il risultato in una raccomandazione verificabile.
Caso reale: Netflix e la disciplina delle metriche
Netflix è un esempio utile perché ha costruito molte decisioni di prodotto intorno a segnali comportamentali osservabili: completamento degli episodi, tempo di ricerca prima della riproduzione, abbandono dopo pochi minuti, ritorno nella settimana successiva, efficacia delle raccomandazioni. Non a caso Iceberg nasce proprio in Netflix. Il punto non è copiare l’azienda, ma trattare il dato come infrastruttura decisionale e non come ornamento.
Quando si valuta una modifica, per esempio una nuova riga di raccomandazioni o un diverso algoritmo di ranking, non si misura solo il click immediato ma anche se l’utente segue il contenuto e se torna nei giorni successivi. Lo stesso vale per Delta: le garanzie ACID hanno senso solo se servono un outcome, cioè se rendono affidabili i dati su cui poggiano quelle decisioni.
Esempio SQL: costruire una vista di controllo
Il pattern seguente è volutamente generico ma eseguibile nella maggior parte dei warehouse moderni. Crea una base analitica con metrica, segmento e finestra temporale, così da confrontare periodi e gruppi senza riscrivere la logica ogni volta.
WITH base_events AS (
SELECT
user_id,
account_id,
event_type,
event_time,
DATE_TRUNC('week', event_time) AS week,
source,
device_type
FROM events
WHERE event_time >= CURRENT_DATE - INTERVAL '180 days'
AND user_id IS NOT NULL
),
weekly_user_metrics AS (
SELECT
week,
user_id,
COALESCE(source, 'unknown') AS source,
COALESCE(device_type, 'unknown') AS device_type,
COUNT(*) AS total_events,
COUNT(DISTINCT DATE(event_time)) AS active_days,
COUNT(DISTINCT event_type) AS event_diversity,
MAX(CASE WHEN event_type IN ('purchase', 'subscribe', 'activation') THEN 1 ELSE 0 END) AS reached_key_outcome
FROM base_events
GROUP BY week, user_id, source, device_type
)
SELECT
week,
source,
device_type,
COUNT(DISTINCT user_id) AS users,
ROUND(AVG(active_days), 2) AS avg_active_days,
ROUND(AVG(event_diversity), 2) AS avg_event_diversity,
ROUND(AVG(reached_key_outcome) * 100, 2) AS key_outcome_rate
FROM weekly_user_metrics
GROUP BY week, source, device_type
ORDER BY week, source, device_type;
La query crea una superficie di osservazione (trend, segmenti, differenze tra canali) da cui formulare ipotesi più precise. Su una tabella Delta puoi rieseguirla con versionAsOf per confrontare due stati nel tempo.
Esempio Python: controllare stabilità e anomalie
Una metrica utile deve essere stabile abbastanza da orientare decisioni e sensibile abbastanza da segnalare cambiamenti reali. In Python si possono individuare variazioni anomale settimana su settimana.
# df contiene: week, segment, users, key_outcome_rate
# key_outcome_rate espresso in percentuale, es. 12.4
df = df.sort_values(['segment', 'week']).copy()
df['previous_rate'] = df.groupby('segment')['key_outcome_rate'].shift(1)
df['wow_change_pp'] = df['key_outcome_rate'] - df['previous_rate']
df['rolling_mean'] = df.groupby('segment')['key_outcome_rate'].transform(
lambda s: s.rolling(4, min_periods=2).mean()
)
df['rolling_std'] = df.groupby('segment')['key_outcome_rate'].transform(
lambda s: s.rolling(4, min_periods=2).std()
)
df['z_score'] = (df['key_outcome_rate'] - df['rolling_mean']) / df['rolling_std']
anomalies = df[df['z_score'].abs() >= 2].sort_values('z_score')
print(anomalies[['week', 'segment', 'key_outcome_rate', 'wow_change_pp', 'z_score']])
Il valore del controllo è pratico: evita di reagire a ogni oscillazione casuale e segnala quando una variazione merita un’indagine. In azienda alimenta alert, review settimanali e retrospettive.
Errori comuni da evitare
Il primo errore è adottare Delta per moda quando il contesto è multi-engine: in quel caso Iceberg evita un lock-in che pagheresti caro più avanti. Il secondo è disattivare lo schema enforcement per comodità, lasciando che scritture impreviste corrompano la tabella senza che nessuno se ne accorga. Il terzo è dimenticare la compaction: senza OPTIMIZE periodico la tabella si frammenta in tanti piccoli file e le query rallentano.
Per ridurre questi rischi ogni adozione dovrebbe avere almeno tre controlli: una scelta motivata del table format rispetto all’ecosistema, una policy esplicita su mergeSchema, e una manutenzione pianificata di compaction e Z-ordering.
Riepilogo
Delta Lake diventa utile quando rende affidabile un’operazione rischiosa, non quando aggiunge terminologia. Il merge gestisce gli upsert, il time travel ricostruisce stati passati, OPTIMIZE e Z-ordering tengono le query veloci, lo schema enforcement protegge la tabella da scritture impreviste. La scelta tra Delta e Iceberg si decide sull’ecosistema, non sulla simpatia per un vendor, e una buona adozione finisce con una procedura chiara di correzione, rollback e manutenzione invece che con un semplice “ora abbiamo le transazioni ACID”.
Percorso collegato
Lezioni da leggere insieme
Questi collegamenti portano la lezione dentro il resto del corso: basi da riprendere, passaggi successivi e connessioni tematiche tra moduli.