dbt Incremental – Il modo giusto
dbt Incremental - The right way
Dal Dolore della Carico Completo al Guadagno Incrementale (e Alcuni Errori per Strada)
Quando il mio team di GlamCorner ha iniziato a passare dai tradizionali database MySQL a ELT su un database Postgres con dbt come livello di trasformazione e modellazione, eravamo entusiasti. Abbiamo configurato progetti e profili dbt, macro dedicate per i nostri modelli e creato più data mart per soddisfare le esigenze successive. Pensavamo di aver finito – io pensavo di aver finito – finché non ci siamo imbattuti nel nostro primo ostacolo: il tempo di esecuzione del modello. In questo articolo, spiego come ho superato una delle sfide di prestazione più difficili dell’epoca adottando dbt incrementale, commettendo errori (come chi non li commette?) e imparando preziose lezioni lungo il percorso.
Il Mostro in Evoluzione
Da GlamCorner, siamo nel gioco della moda circolare. Il nostro team “back-end” gioca con scanner RFID nel magazzino, scansionando articoli come dei professionisti. Utilizziamo anche piattaforme sofisticate come Zendesk e Google Analytics per far sentire i nostri clienti particolarmente speciali. E per completare il tutto, abbiamo il nostro sistema di inventario interno – grazie ai nostri brillanti software engineer – che collega tutti i nostri sistemi front-end e back-end. È come un matrimonio perfetto. Ma man mano che cresciamo e aggiungiamo più anni di attività, il nostro database sta diventando sempre più grande. E diciamo semplicemente che il tradizionale caricamento completo della tabella sta diventando un po’ fastidioso.
Il Dolore
O capisci il dolore di “Voglio che i dati siano pronti entro le 9 del mattino” o non lo capisci.

Il team ha messo tutto l’impegno per creare un (E)xtract e (L)oad impeccabile, ci siamo riuniti e brindato. Poi un giorno, la (T)rasformazione ha deciso che “Eh, non funziona così qui” e ha deciso di aumentare il tempo di esecuzione totale da 10 a 90 minuti. Forse esagero nella parte dei 10 a 90 minuti perché sì, tutto ha la sua ragione, ma la paura che il team commerciale bussi alla tua porta alle 8.55 del mattino quando non hai nemmeno iniziato a bere la prima tazza di caffè, solo per chiedere: “Dove sono i dati più recenti?” è un incubo al lavoro ogni giorno. È come buttare via tutto il duro lavoro e io, personalmente, non posso accettare quella realtà.
- Ricerca di similitudine, Parte 7 Composizioni LSH
- Come costruire un’app Streamlit multi-pagina interconnessa
- Ottimizzare le tue strategie con approcci oltre i test A/B
Torniamo alla cosa che ho detto: tutto ha la sua ragione, e perché la fiaba che una volta richiedeva solo 10 minuti del mio tempo ora è diventata un demone di 90 minuti con le corna rosse. Per illustrare questo, prendiamo l’esempio dei dati della tabella fct_booking. Questa tabella contiene tutte le informazioni relative alle prenotazioni prese dal sito web ogni giorno. Ogni booking_id
rappresenta un ordine prenotato sul sito web.

Ogni giorno, vengono aggiunti circa 4 ordini alla tabella delle prenotazioni, che già contiene 80 ordini. Quando questo modello viene eseguito utilizzando dbt, elimina l’intera tabella dall’ultimo giorno e la sostituisce con 84 record, inclusi gli ordini vecchi e nuovi (80 ordini da dati cumulativi storici + 4 nuovi ordini aggiunti per l’ultimo giorno). Inoltre, per ogni 4 nuovi record aggiunti, il tempo di interrogazione aumenta di circa 0,5 secondi.

Immagina ora che 4 ordini equivalgano a 4000 al giorno e che 80 ordini rappresentino effettivamente 800.000 record. Riesci a indovinare quanto tempo ci vorrà per trasformare la tabella fct_bookings e dove saremo, ad esempio, tra 3 mesi?
Bene, lascio a te i calcoli.
L’uovo d’oro
Quindi, dopo aver vagato senza meta tra i thread della Community di dbt e aver dato una rapida occhiata alla documentazione di dbt (voglio dire, chi non l’ha mai fatto?), sono inciampato nel Santo Graal di dbt Incremental. È come trovare un ago in un pagliaio, solo che l’ago è d’oro e il pagliaio è fatto di codice.
In parole semplici, dbt Incremental significa che non è necessario elaborare tutti i dati dall’inizio. Si elaborano solo i dati nuovi e modificati, risparmiando tempo e risorse. È come una scorciatoia che funziona davvero e non ti metterà nei guai con il tuo capo.

Se vuoi saperne di più sui dettagli tecnici di dbt Incremental, dai un’occhiata a questo blog e a questo documento:
Il potere dei modelli incrementali di dbt per i Big Data
Un esperimento su BigQuery
towardsdatascience.com
Modelli incrementali | dbt Developer Hub
Leggi questo tutorial per imparare come utilizzare modelli incrementali nella costruzione di progetti con dbt.
docs.getdbt.com
Per impostare questo modello nel tuo modello dbt, devi aggiungere un blocco di configurazione all’inizio del tuo script del modello, tenendo presente questi due componenti:
- Materialized: Di default, la vista materializzata di un modello dbt è uguale a ‘table’ quando non c’è alcuna configurazione. Per impostare la modalità incrementale, impostare la vista materializzata su ‘incremental’. Per ulteriori informazioni su altre materializzazioni dbt, visita:
Materializzazioni | dbt Developer Hub
Leggi questo tutorial per imparare come utilizzare le materializzazioni nella costruzione di progetti con dbt.
docs.getdbt.com
- Unique_key: Anche se la configurazione di una chiave univoca è facoltativa secondo la documentazione di dbt, è estremamente importante considerare razionalmente come si desidera impostarla. Fondamentalmente, la chiave univoca sarà il principale indicatore che permette a dbt di sapere se il record deve essere aggiunto o modificato. Alcune domande da tenere a mente sono:
- La chiave univoca è realmente unica?
- È una combinazione di due o più colonne?
Non impostare una chiave univoca può portare a dati mancanti e valori ambigui, quindi fai attenzione!
Ecco un esempio di come viene impostato il blocco di configurazione per una singola chiave univoca:
Nel caso in cui la chiave univoca sia la combinazione di più colonne, è possibile modificare la configurazione in questo modo:
Nota: se stai utilizzando BigQuery o Snowflake per archiviare i tuoi dati, potresti avere l’opzione di configurare ulteriori parametri come la sincronizzazione. Ma dato che il database della mia azienda è basato su Redshift, specificamente su Postgres, non abbiamo questi meccanismi sofisticati.
Una volta fatto ciò, c’è solo un altro passo importante da aggiungere allo script dei modelli incrementali di dbt: un blocco condizionale per la macro is_incremental()
.
La macro is_incremental() restituisce True se vengono soddisfatte le seguenti condizioni:
- La tabella di destinazione esiste già nel database.
- dbt non sta eseguendo la modalità
full-refresh
. - Il modello in esecuzione è configurato con
materialized='incremental'
Nota che l’SQL nel tuo modello deve essere valido indipendentemente dal fatto che is_incremental()
valuti a True o False.
Tornando all’esempio di fct_booking, ecco la query originale:
Dopo aver applicato la configurazione incrementale descritta sopra, abbiamo un modello che include la chiave univoca, un tag per il modello e un blocco condizionale per la macro is_incremental() come segue:
Come si può vedere nel codice, la unique_key è stata impostata sul booking_id
, poiché un booking_id corrisponde a un ordine.
Per renderlo più elegante, ho anche aggiunto un tag di modello come incremental_model per qualsiasi altro modello che integro con una materialized incrementale. Il motivo principale è che spesso, quando qualcosa va storto con il modello incrementale di dbt, di solito va storto “in blocco”. Pertanto, per aggiornarli senza influire su altri modelli e senza dover ricordare ogni singolo modello abilitato in modalità incrementale, posso eseguire il codice sopra anziché dover specificare singolarmente ciascun nome di modello con la modalità incrementale.
dbt run — select tag:incremental_model --full-fresh
Nota anche che se il modello incrementale è configurato in modo errato e aggiorna dati non corretti nella tabella di produzione, dovrò eseguire nuovamente il modello utilizzando il comando --full-refresh
. Tuttavia, tieni presente che eseguirlo in modalità di aggiornamento completo invece che in modalità incrementale sarà più lento, quindi ricorda di scegliere il momento giusto per farlo (consiglio: non farlo alle 9 del mattino).
Lo Schiaffo di Ritorno
Fino a questo punto, la vita era di nuovo bella! Ho configurato la tabella senza problemi e la query di performance è migliorata significativamente. Finalmente posso dormire di notte. La mia mano può toccare l’erba e dbt incrementale non lascerà scappare nemmeno la piccola Leah – un sogno diventato realtà. Tuttavia, poco dopo, un ragazzo del team Finanza è corso alla mia scrivania con un report in mano e ha affermato in modo aggressivo: “Mi hai dato i dati sbagliati!”
Si è scoperto che i modelli incrementali hanno accidentalmente saltato molti ordini in un giorno e poi sono passati al giorno successivo. “Come è possibile? Ho seguito il tutorial degli esperti, non può essere sbagliato!” sussurrai nella mia testa. Tranne che c’è qualcosa che sta succedendo a monte che potrei aver perso. Dopo un po’ di indagini, il problema è venuto alla luce.
Ogni giorno, viene eseguito un processo di estrazione e caricamento dati a mezzanotte per sincronizzare tutti i dati fino a quel momento. Questa sincronizzazione di solito avviene a mezzanotte, ma il suo timing può essere influenzato da fattori come il tempo di avvio e la cache dei pacchetti. È importante notare che la parte di estrazione del processo può iniziare leggermente dopo mezzanotte.
Consideriamo uno scenario in cui l’estrazione inizia alle 00:02 e qualcuno decide di effettuare una prenotazione intorno alle 00:01. In questa situazione, i dati includeranno anche una piccola parte degli ordini di quel giorno, che viene definita “dati di arrivo tardivo” in termini tecnici.
Tuttavia, c’è un inconveniente con la logica corrente del filtro WHERE. L’efficienza del filtro viene compromessa perché aggiunge solo nuovi record dalla data più recente di created_at
. Ciò significa che non catturerà tutti i dati per l’intera giornata.
Per risolvere questo problema, torceremo un po’ questa logica:
Il nuovo filtro prevede la sincronizzazione di tutti i dati degli ultimi 7 giorni. I nuovi dati verranno aggiunti all’insieme di dati esistente, mentre i vecchi dati con valori di campo aggiornati verranno sostituiti.
Il Compromesso
Se hai seguito finora, potresti chiederti: “Quanti giorni dovrei andare indietro utilizzando il filtro is_incremental? E perché ho scelto 7 giorni per il mio caso? Cosa succede se ho bisogno dei dati degli ultimi 30 giorni?” Beh, la risposta non è semplice – dipende dal tuo scenario specifico.
Nel mio caso, mi assicuro che ogni giorno ci sia almeno un ordine. Poiché potrebbero esserci cambiamenti interni nei dati durante gli ultimi 7 giorni, imposto il mio filtro per aggiungere nuovi dati e aggiornare i dati esistenti entro quel periodo. Tuttavia, se sei sicuro delle prestazioni della tua query e vuoi andare indietro ancora di più, ad esempio negli ultimi 365 giorni, sei libero di farlo! Tieni solo presente che ci sono dei compromessi da considerare.
Il motivo principale per utilizzare un modello incrementale è ridurre i costi in termini di prestazioni di esecuzione del modello. Tuttavia, scansionare un dataset più grande per gli ultimi 7 giorni potrebbe rallentare le prestazioni, a seconda delle dimensioni dei dati e del caso d’uso specifico della tua azienda. È essenziale trovare il giusto equilibrio in base alle tue esigenze.
Per un approccio più generale, consiglio di utilizzare 7 giorni come regola standard. Puoi impostare gli aggiornamenti dei dati su base settimanale o annuale per un refresh completo dei modelli incrementali di dbt. Questo approccio ti permette di tenere conto di eventuali problemi imprevisti, poiché non importa quanto sia ben impostata la tua configurazione, potrebbero comunque verificarsi occasionali periodi di inattività.
Nel mio caso d’uso, di solito programmo l’esecuzione incrementale con un refresh completo durante il fine settimana, quando ci sono meno attività operative. Tuttavia, questo programma può essere personalizzato in base alle esigenze del tuo team.
Ricorda, la chiave è trovare il giusto compromesso tra freschezza dei dati e prestazioni delle query, garantendo che i tuoi dati rimangano accurati e aggiornati, ottimizzando al contempo l’efficienza del tuo modello.