Vai al contenuto principale
Delta Lake: ACID su data lake - immagine ufficiale della lezione su GinnyTech, creata da AD

Delta Lake: ACID su data lake

Delta Lake: transazioni ACID, time travel e schema enforcement su S3.

AD
Creato daAndrii Dyshkantiuk
Lezione 109 / 236Livello: AvanzatoDurata: 22 minPrerequisiti: 1

Cosa imparerai

  • Comprendere il problema analitico e il contesto decisionale
  • Applicare esempi, metriche e controlli a casi reali

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.

ElementoDefinizione operativaControllo minimo
Unità di analisiOggetto su cui misuri l’affidabilitàTabella, partizione, versione
SegnaleCosa osservi per decidereFrequenza di update, concorrenza, obblighi di correzione
BaselineRispetto a cosa giudichiComportamento attuale con soli file Parquet
Rischio residuoErrore che resta dopo l’adozioneLock-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 LakeIceberg
CreatoreDatabricksNetflix → Apache
Ecosistema primarioSpark, DatabricksMulti-engine (Trino, Presto, Flink, Spark)
CompactionOPTIMIZE commandManuale o via tool esterni
Z-orderingSì (multi-dimensionale)No nativo
Change Data FeedSì (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:

  1. definire il problema in linguaggio business;
  2. identificare l’unità di analisi corretta: tabella, partizione, versione;
  3. controllare se i dati misurano davvero il fenomeno o solo una sua ombra;
  4. costruire una metrica interpretabile;
  5. segmentare per evitare che la media nasconda pattern opposti;
  6. 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.

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”.