SELECT

SELECT

Ridurre i costi di Databricks quando i quick win non bastano più

Questa pagina è disponibile anche in English, Deutsch, Español, Français, 日本語 e Português.

By SELECTNov 12, 2022 min read

TL;DR

I quick win dell'ottimizzazione dei costi di Databricks (auto-termination, timeout, autoscaling, compute dimensionato correttamente) offrono il miglior ritorno in assoluto: spesso tagliano il 20-40% della fattura e richiedono pochi minuti per essere applicati. Oltre questi, di solito è disponibile un ulteriore 20-40%, ma costa giorni o settimane di lavoro e ripaga solo sui workloads dove i conti tornano. Questa guida riguarda proprio quel secondo livello: come individuare i job per cui vale la pena investire, e come fargli fare meno lavoro prima di decidere su quale compute eseguirli.

Introduzione

La maggior parte dei consigli sui costi di Databricks si ferma alla configurazione. Impostare l'auto-termination, aggiungere i timeout, attivare l'autoscaling, smettere di sovradimensionare i cluster. Sono consigli corretti, e andrebbero seguiti tutti per primi, perché si tratta davvero del lavoro con il ritorno più alto disponibile. A seconda del punto di partenza, questi quick win recuperano comunemente il 20-40% della fattura, e la maggior parte richiede pochi minuti. Nessuno dovrebbe saltarli, e questo post non sostiene affatto che siano poca cosa.

Il punto è che hanno un tetto. Una volta eliminati i cluster inattivi, impostati i timeout e dimensionato correttamente il compute, si è ottenuto un grande risparmio una tantum e la curva si appiattisce. I workloads continuano a fare esattamente lo stesso lavoro di prima. Ottenere il successivo 20-40% significa cambiare ciò che i job fanno davvero: una pipeline che rielabora tutto lo storico ogni notte, una query che scansiona cento volte più dati di quelli che restituisce, una dashboard "in tempo reale" che nessuno apre più di una volta al giorno. Nessuno di questi casi diventa più economico ridimensionando il cluster. Diventano più economici riprogettando il lavoro, e questo costa giorni o settimane anziché minuti. E ripaga solo su alcuni workloads, motivo per cui il primo vero passo è capire quali. Questa guida parla di quel secondo livello.

Perché la maggior parte degli sforzi sui costi di Databricks si arena dopo i quick win?

Il primo intervento sui costi di Databricks punta quasi sempre alla stessa manciata di controlli, e per una buona ragione. Sono le correzioni con il ritorno più alto e lo sforzo minore, e saltarle è il modo in cui le fatture vanno fuori controllo. Se non sono ancora state applicate, è da lì che bisogna partire: il nostro Databricks Pricing Explained illustra in dettaglio auto-termination, timeout e Job Compute rispetto all'all-purpose. Quel post è il prerequisito; questo è ciò che viene dopo.

Ecco il problema del fermarsi lì. Ognuno di quei controlli è una correzione una tantum. L'auto-termination evita di pagare per cluster inattivi, ma una volta che il tempo di inattività è a zero, non c'è più nulla da recuperare. Spostare una pipeline da un cluster all-purpose a 0,55 $ per DBU a Jobs Classic a 0,30 $ per DBU riduce la tariffa di quel job di circa il 45%, ma quel passaggio si può fare una sola volta. Sono risparmi consistenti e immediati, spesso da un quinto a due quinti della fattura, e poi finiscono lì. La curva si appiattisce e la spesa si assesta su una nuova baseline, più bassa, che smette di muoversi.

Quella baseline è determinata da quanto lavoro fanno i job, ed è la variabile che i quick win non toccano mai. Una pipeline giornaliera che riscrive integralmente una tabella da un miliardo di righe resta costosa anche sul cluster Jobs più economico e correttamente dimensionato che si possa creare. Il livello successivo di risparmio vive nel workload stesso: elaborare meno dati, eseguire meno spesso, calcolare i risultati una volta invece che ripetutamente. È più difficile che attivare un'opzione di configurazione, richiede giorni o settimane invece di minuti e non ripaga ovunque. Su una pipeline economica o poco frequente il tempo di engineering costerà più di quanto farà risparmiare. Ecco perché il passo successivo non è iniziare a ottimizzare, ma scoprire quali workloads vale davvero la pena ottimizzare.

Come individuare i workloads che vale la pena ottimizzare?

Prima di ottimizzare qualsiasi cosa, bisogna scoprire che cosa costa davvero. Conta più di quanto sembri, perché l'istinto ingegneristico e la fattura raramente indicano la stessa cosa. La pipeline che dà fastidio è spesso economica. Quella a cui nessuno pensa gira su un cluster di grandi dimensioni tre volte al giorno e domina silenziosamente la fattura.

Il modo più rapido per classificare la spesa sono le system table di billing. Si esegue il join tra system.billing.usage (ogni DBU consumata) e system.billing.list_prices (il prezzo effettivo per SKU) per trasformare le quantità in dollari, poi si aggiunge system.lakeflow.jobs per associare un nome di job leggibile, così la classifica diventa davvero utilizzabile:

WITH price AS (
SELECT
sku_name,
pricing.effective_list.default AS price_effective
FROM system.billing.list_prices
WHERE
price_end_time IS NULL
),
usage AS (
SELECT
*
FROM
system.billing.usage
WHERE
usage.billing_origin_product = 'JOBS'

Espandi il codice

Questa query classifica i job per costo giornaliero con i nomi associati, e risponde all'unica domanda che conta in questa fase: quali job, identificati per nome, determinano la fattura? Il join con system.lakeflow.jobs, tra l'altro, è ciò che rende il tagging un investimento sensato. I tag da soli non abbassano mai una fattura, ma sono ciò che permette di raggruppare questo output per team, ambiente o progetto, invece di annegare in ID di job e nomi che cambiano nel tempo.

La ragione per classificare prima di agire è che l'ottimizzazione ha un costo a sua volta, ed è facile spendere più tempo di engineering di quanto se ne risparmi. Riscrivere una pipeline che rappresenta l'1% della fattura è un esercizio appagante che non si vedrà mai sulla fattura, e le ore dedicate supereranno di solito di gran lunga i risparmi per anni. Conviene ordinare per costo, guardare la manciata di workloads in cima alla lista e ignorare la coda lunga finché quelli costosi non sono sistemati. Si ottimizzano gli outlier, non tutto.

Classificare la propria spesa per workload significa scrivere e mantenere queste query, e tenere aggiornate le dashboard man mano che i job vengono rinominati e le pipeline evolvono. SELECT esegue questa attribuzione automaticamente, facendo emergere i workloads più costosi, il compute inattivo e i mismatch sul tipo di compute, senza costruire query a mano. Scopra cosa rileva nel suo ambiente.

Perché ottimizzare il job batte ridimensionare il cluster?

Questa è la sezione che ripaga tutto il resto. Quando un job è lento o costoso, la soluzione istintiva è un cluster più grande, perché è una sola impostazione e di solito funziona. È anche la soluzione più costosa disponibile, e lascia intatto lo spreco sottostante. Un job che scansiona più dati del necessario, fa shuffle inutili o rielabora uno storico già elaborato brucerà compute in eccesso a qualsiasi dimensione di cluster. Scalare verso l'alto fa solo pagare quella tassa più in fretta.

Per essere concreti: un mio cliente eseguiva una pipeline di dati IoT che abbiamo ridotto di circa l'85% nel tempo di esecuzione e del 75% nei costi, circa 10.000 $ l'anno, sullo stesso ambiente. Il compute non era mal dimensionato in partenza: non ottimale, ma nemmeno fuori misura. Ridimensionarlo avrebbe spostato la fattura di percentuali a una cifra. I risparmi sono arrivati interamente dal cambiare ciò che la pipeline faceva: caricamento incrementale dei layer bronze e silver invece della rielaborazione completa, rinvio di un costoso join sulla fact table a dopo l'aggregazione giornaliera che aveva ridotto i dati, riscrittura della trasformazione più pesante e consumo servito tramite view invece di duplicare i dati in nuove tabelle. Ognuna di queste azioni corrisponde a una delle leve qui sotto. Nessuna di queste, da sola, ha fatto tutto il lavoro, e il dimensionamento del compute non era nella lista.

Leggere il query profile, poi riprogettare la parte costosa

Databricks espone un query profile dettagliato per ogni esecuzione, che mostra dove è andato il tempo in ciascuno stadio del piano. Dalla cronologia delle query, o direttamente da un job o da una query SQL, è possibile aprire il profilo e scaricare l'intero piano di esecuzione in JSON. Quel JSON è l'input per il trucco più efficace e meno conosciuto del moderno workflow di data engineering: darlo in pasto a un LLM.

Un LLM è molto bravo a leggere un query profile e dire dove sta il costo: quale stadio ha fatto spill, quale join è esploso, dove uno shuffle è girato su molte più partizioni di quanto i dati giustificassero. Su un job reale, uno ha segnalato che il collo di bottiglia era una trasformazione SQL che costruiva un calendario completo di date (una riga per entità per giorno, su circa quattordici anni di storico) per poi riaggregarlo, producendo decine di milioni di righe intermedie per calcolare un risultato che ne richiedeva una frazione minuscola. E non si è fermato alla diagnosi. Ha proposto una prima riscrittura in PySpark con un approccio event-based a scansione di intervalli che non materializza mai il calendario, e da lì è diventato un vero collaboratore nella costruzione della soluzione. Io segnalavo un caso limite che la prima versione non gestiva, lui integrava la correzione, catturavamo quel caso in uno unit test, e abbiamo iterato quel ciclo per due o tre giorni finché i risultati non sono stati corretti su tutta la linea. Il job giornaliero è passato da circa due ore a dieci minuti, una riduzione di circa 15 volte sia nel runtime sia nel costo in DBU.

Il punto è la divisione del lavoro, e non è "l'AI fa emergere i problemi, l'umano fa il lavoro vero". Il modello ha trovato il collo di bottiglia, proposto l'algoritmo e scritto la maggior parte del codice. Il mio compito era guidare: intercettare i casi che gli sfuggivano, decidere quando la soluzione era davvero corretta e mantenere rigorosa l'iterazione con i test. È un uso davvero diverso e più potente rispetto a incollare un messaggio di errore. Se si lavora nel workspace di Databricks, Genie gestisce bene questo compito; se si è nel proprio IDE, va benissimo qualunque assistente già integrato. Lo strumento conta meno dell'abitudine di leggere il profilo e ricostruire la parte costosa invece di ricorrere a un cluster più grande.

Passare all'incrementale invece di sovrascrivere l'intera tabella

La sovrascrittura completa è il pattern di pipeline più semplice, e per tabelle piccole o di dimensioni moderate è spesso la scelta giusta. Non sempre la complessità dell'elaborazione incrementale è giustificata. Ma quando i volumi di dati sono alti, specialmente per dati transazionali, telemetria o serie temporali, rielaborare l'intera tabella a ogni esecuzione diventa più costoso e più difficile da giustificare ogni volta che la tabella cresce.

L'alternativa incrementale è fare append dei nuovi record per i dati immutabili, o merge dei record modificati per i dati che si aggiornano, ed estendere quel pattern a quanti più layer possibile della propria architettura medallion. Solo quando i dati sono aggregati fino a qualcosa di piccolo, una sovrascrittura completa torna a essere ragionevole. Spark Structured Streaming è lo strumento pratico in questo caso: abbinato ad Auto Loader per l'ingestion di file o leggendo nativamente da una tabella Delta, gestisce automaticamente i checkpoint, così ogni esecuzione elabora solo ciò che è arrivato dall'ultima. Il vantaggio più grande per la maggior parte dei team non è l'elaborazione continua in tempo reale, ma l'affidabile pattern di batch incrementale che lo streaming rende facile: elaborare i nuovi dati senza una nuova scansione di quelli già esistenti.

Mettere in discussione il requisito "real-time" prima di accettarlo

"Ci servono dati quasi in tempo reale" è uno dei requisiti più costosi che un data team possa accettare senza obiezioni, ed è molto raramente vero. Gli stakeholder non hanno quasi mai bisogno di un flusso continuo di dati freschi. Hanno bisogno di una vista abbastanza recente nel momento in cui effettivamente guardano, che è una cosa completamente diversa e molto più economica. Prima di costruire per il real-time, conviene scoprire quanto spesso la dashboard viene davvero aperta e quanto spesso il report viene davvero letto. Aggiornamenti orari, giornalieri, persino settimanali sono sufficienti molto più spesso di quanto la richiesta originale lasci intendere.

La differenza di costo non è marginale. Prendiamo una pipeline di esempio e, a parità di lavoro, cambiamo solo la cadenza: eseguita in continuo potrebbe costare 58 unità; eseguita ogni ora lo stesso lavoro scende a circa 5 unità; eseguita tre volte al giorno si è vicini a 1 unità. Non è una differenza di arrotondamento: è circa 12 volte dal continuo all'orario, e da lì un altro grande gradino verso il basso.

La mossa pratica è costruire ogni pipeline con Structured Streaming indipendentemente dalla frequenza richiesta inizialmente, perché lo streaming rende banale passare dal batch pianificato al funzionamento continuo. Poi partire dalla frequenza più bassa che soddisfa l'effettiva esigenza di business, e alzarla solo quando ci sono prove concrete che serve maggiore freschezza. La possibilità di scalare resta disponibile quando serve; i risparmi derivanti dal non scalare prematuramente arrivano subito.

Costruire view invece di tabelle

Non tutte le tabelle devono contenere fisicamente i dati. Una quota consistente delle tabelle in un tipico ambiente Databricks esiste non per trasformazioni complesse, ma per esporre i dati in una forma diversa: uno schema diverso, un filtro, un sottoinsieme di colonne, un diverso set di permessi di accesso. In quei casi, copiare fisicamente i dati aggiunge costi di storage, costi di compute e complessità di pipeline senza alcun beneficio reale.

Una view è lo strumento giusto per questo. È un layer virtuale definito da una query, senza nulla di copiato o memorizzato, e per una semplice selezione di colonne o un filtro l'esperienza di lettura è indistinguibile da una tabella. Le tabelle fisiche vanno riservate agli output di trasformazioni davvero costose, dove materializzare il risultato evita ricalcoli significativi. Per tutto il resto, una view mantiene lo storage snello e le pipeline più semplici. Per un approfondimento di questi pattern di workflow, il nostro ebook gratuito sull'ottimizzazione dei costi li percorre tutti, insieme al diagramma di flusso diagnostico che usiamo per decidere quale leva applicare a un dato workload.

Come scegliere il compute giusto una volta che il job è efficiente?

Si noti l'ordine. La scelta del compute viene dopo l'ottimizzazione del workload, non prima, e questa sequenza è l'intera tesi di questo post. Dimensionare il compute su un job inefficiente non fa che cristallizzare lo spreco alla scala scelta. Una volta che il job fa la giusta quantità di lavoro, la decisione sul compute diventa più semplice e la posta in gioco si abbassa.

Il default dovrebbe essere il serverless, su tutta la linea, per job, notebook e SQL. Può sembrare controintuitivo, perché il serverless ha una tariffa per DBU più alta: su Azure Premium, Jobs Serverless costa 0,45 $ per DBU contro 0,30 $ di Jobs Classic, e il Serverless interattivo costa 0,95 $ contro 0,55 $ dell'all-purpose classico. Ma la tariffa per DBU è solo metà della storia, ed è la parte che i team trascurano più spesso quando fanno il confronto. Sul compute classico, l'addebito per DBU non è l'intera fattura. Si pagano anche, separatamente, al cloud provider le VM sottostanti, ed è un numero davvero difficile da stimare in anticipo. Il serverless include l'infrastruttura nella tariffa per DBU, quindi il prezzo che si vede è il prezzo che si paga.

Questa inclusione ribalta il confronto superficiale più spesso di quanto ci si aspetti. La pipeline ETL oraria di un cliente è un esempio chiaro. Su Job Classic a 0,30 $ per DBU consumava circa 44 DBU al giorno, pari a circa 13 $ di DBU più altri 10 $ di VM cloud, circa 23 $ al giorno in totale. La versione serverless dello stesso workload usava meno DBU (circa 38, perché scalava in modo più aderente al lavoro effettivo) a 0,45 $ per DBU, per circa 18 $ al giorno senza alcuna fattura cloud separata. L'opzione con la tariffa nominale più alta era quella più economica una volta contato l'addebito delle VM, e per arrivarci non è servito alcun tuning.

C'è un secondo costo che non compare mai su nessuna delle due fatture: il tempo di engineering per dimensionare e mettere a punto correttamente un cluster classico (tipo di istanza, rapporto memoria-core, comportamento dell'autoscaling, e la revisione di tutto questo man mano che il workload evolve). Per la maggior parte dei workloads quel tempo costa più di qualsiasi sovrapprezzo sulla DBU. Una giornata di tuning per limare il 10% da una pipeline modesta può impiegare un anno per rientrare. Il serverless elimina inoltre un'intera classe di problemi operativi: niente avvio dei cluster, niente gestione delle versioni, niente cluster inattivi da dimenticare.

Per essere onesti: il serverless è raramente l'opzione singolarmente più economica per una data pipeline presa in isolamento, ma per la maggior parte delle pipeline porta all'80-90% del risultato con zero sforzo di tuning. Sull'intero portafoglio, questo vince in termini di total cost of ownership, perché la grande maggioranza delle pipeline finisce per essere ragionevolmente efficiente senza dedicarvi un minuto. Poi si usa la classifica dei costi per individuare la manciata di job ad alta frequenza e alto costo che girano in modo prevedibile, e quelli sono i candidati da spostare sul compute classico e ottimizzare a mano, calcoli sulle VM cloud inclusi. Si ottimizzano gli outlier, e tutto il resto va in serverless per default.

Quando si configura il compute classico, conviene partire dalla risorsa più piccola che funziona e scalare sulla base di evidenze. Il sovradimensionamento è lo stato di default della maggior parte dei deployment Databricks, perché le configurazioni vengono copiate da un collega, ereditate da un vecchio progetto o lasciate a quanto suggerito dall'interfaccia. Meglio scegliere la dimensione più piccola di SQL warehouse e salire solo quando i tempi di query diventano inaccettabili; per i cluster, partire da un tipo di nodo general-purpose e fare autoscaling da uno a due o quattro worker. Un compute grande il doppio non esegue i job due volte più velocemente, ma costa immancabilmente il doppio.

Come evitare che i costi tornino a salire?

L'ottimizzazione puntuale è un punto di partenza, non uno stato stabile. I team crescono, i workloads si moltiplicano, arrivano nuovi Engineers con le proprie abitudini, e i costi risalgono a meno che qualcosa non li freni. Quel qualcosa è un layer di governance, ed è ciò che trasforma una pulizia una tantum in una baseline duratura.

La contromisura più affidabile è smettere di permettere la creazione di compute ad hoc. Quando gli Engineers creano da soli i propri cluster, le impostazioni che contano (auto-termination, limiti di scaling, tipi di istanza, tag) vengono configurate in modo incoerente o per niente, e ogni attenta ottimizzazione fatta altrove va dispersa. Conviene definire cluster e warehouse come infrastructure as code, distribuirli tramite CI/CD e far scegliere agli Engineers da un set pre-approvato invece di lasciarli costruire in proprio. Per i power user che hanno davvero bisogno di compute personalizzato, le cluster policy sono la via di mezzo: una policy può imporre l'auto-termination, limitare la dimensione del cluster e restringere i tipi di istanza, lasciando comunque spazio per personalizzare entro quei limiti. La scelta efficiente in termini di costi diventa l'unica scelta disponibile. Per capire meglio perché pensiamo che questo problema meriti strumenti dedicati, si veda perché abbiamo costruito SELECT per Databricks.

La prevenzione non intercetta tutto, quindi va abbinata agli alert. Databricks ha una funzionalità nativa di budget nella console dell'account per le soglie di spesa, ed è un buon punto di partenza. Ma il segnale più sensibile è statistico, non un limite fisso: misurare la deviazione standard del costo giornaliero o settimanale per workload e generare un alert sugli scostamenti dall'intervallo normale. Una soglia fissa scatta solo dopo che la spesa ha già superato una linea rigida. Monitorare la deriva intercetta un problema il giorno stesso in cui un job inizia a comportarsi male, non a fine ciclo di fatturazione quando il danno è fatto. Quella visibilità il giorno stesso è la differenza tra una piccola correzione e una fattura salata.

L'ottimizzazione dei costi di Databricks non è un progetto una tantum. La piattaforma evolve, i team aggiungono workloads, e la baseline che si è faticato ad abbassare tornerà a salire senza visibilità continua. SELECT offre ai team di data platform quella visibilità in un unico posto: classifica la spesa per workload, intercetta la deriva sul nascere e fa emergere i risparmi che si accumulano nel tempo. Prenoti una demo per vedere cosa fa emergere nel suo ambiente.

FAQ

Qual è il modo più rapido per ridurre i costi di Databricks?

Due modifiche di configurazione recuperano più denaro con il minimo sforzo: impostare l'auto-termination su ogni cluster e SQL warehouse, in modo che le risorse inattive smettano di generare addebiti, e spostare le pipeline pianificate dal compute all-purpose a Jobs Compute. Su Azure Premium, questo porta un job da 0,55 $ per DBU a 0,30 $, un taglio di circa il 45% sulla tariffa di quel workload. Entrambe sono modifiche da un pomeriggio. L'avvertenza onesta: nessuna delle due tocca una pipeline progettata male. Se un job rielabora tutto il suo storico ogni notte o scansiona molti più dati di quelli che restituisce, resta costoso anche sul cluster più economico e correttamente dimensionato che gli si possa assegnare. I quick win fissano una baseline più bassa; abbassarla ulteriormente significa riprogettare il lavoro.

Il serverless è più economico del compute classico?

Non sulla base del costo per DBU, e non sempre in totale, quindi non c'è un sì o un no netto. Il serverless ha una tariffa più alta (su Azure Premium, 0,45 $ per DBU per Jobs Serverless contro 0,30 $ per Jobs Classic) ma include l'infrastruttura che altrimenti si dovrebbe pagare e mettere a punto da soli. Per la maggior parte dei workloads il tempo di engineering risparmiato supera il sovrapprezzo sulla tariffa, ed è per questo che il serverless è un buon default. La trappola da tenere d'occhio: il serverless scala in modo così fluido che gli aumenti di costo si nascondono meglio che sul compute classico. Due esecuzioni possono avere la stessa durata complessiva consumando quantità di DBU molto diverse, perché il serverless è scalato silenziosamente per quella più pesante. La durata sembra piatta mentre la fattura raddoppia. Va monitorato il consumo di DBU, non solo il tempo di esecuzione.

Unity Catalog aumenta la fattura Databricks?

Unity Catalog in sé non è un prodotto con un prezzo separato, ma abilitarlo cambia il profilo dei costi in modi che vale la pena comprendere prima di migrare. Predictive Optimization, che esegue automaticamente OPTIMIZE e VACUUM sulle tabelle gestite di Unity Catalog, consuma DBU (in genere si ripaga con i risparmi sulle query, ma è una voce di spesa reale). Le funzionalità di governance, la manutenzione delle tabelle gestite e il compute serverless su cui girano diverse operazioni del catalogo finiscono tutti da qualche parte in fattura. Trattiamo in una guida dedicata come Unity Catalog incide sui costi; in breve: la governance vale la pena, ma non è gratuita, ed è bene sapere dove finisce sulla fattura prima di attivarla.