L’architettura MPP e le 60 distribution
Un dedicated SQL pool (ex SQL Data Warehouse) in Azure Synapse Analytics è un motore MPP (Massively Parallel Processing). Un nodo di controllo riceve la query, la ottimizza e la scompone in operazioni parallele affidate ai Compute node. Il numero di Compute node varia con il livello di scala (DWU, Data Warehouse Units), ma indipendentemente da quanti nodi hai, i dati sono sempre suddivisi fisicamente in 60 distribution: shard di storage a cui i Compute node si agganciano. A DW100c un solo nodo gestisce tutte le 60 distribution; a DW1000c dieci nodi ne gestiscono 6 ciascuno. Scalando riallochi le distribution ai nodi, non le ricrei.
Questa architettura ha una conseguenza fondamentale: come distribuisci le righe determina quanti dati devono spostarsi tra le distribution durante join e aggregazioni. Questo spostamento si chiama data movement ed è il principale collo di bottiglia delle query analitiche.
I tre tipi di distribuzione
Hash
La tabella è partizionata applicando una funzione hash a una colonna scelta: righe con lo stesso valore finiscono sempre nella stessa distribution. È la scelta per le fact table grandi (tipicamente >2 GB o comunque i tavoli più massivi del modello). Quando due tabelle hash sono distribuite sulla stessa colonna di join, il join avviene localmente in ogni distribution senza data movement (join co-locato), il caso più performante possibile.
Round-robin
Le righe vengono distribuite in modo uniforme e casuale, senza logica di chiave. È veloce da caricare e non soffre di skew, quindi è la scelta per staging/load table temporanee, prima di trasformare i dati verso le tabelle finali. Lo svantaggio: quasi ogni join o group by richiede data movement, perché le righe correlate non sono co-locate.
Replicated
Una copia completa della tabella viene materializzata su ogni Compute node. Elimina il data movement per le tabelle coinvolte, ma ha senso solo per dimension piccole (linea guida: <2 GB dopo compressione). Su tabelle grandi il costo di replica e di cache diventa proibitivo.
Scegliere la colonna di hash
La colonna di distribuzione hash va scelta con criterio:
- Alta cardinalità: molti valori distinti, così le righe si spalmano su tutte e 60 le distribution.
- Nessun data skew: la distribuzione dei valori deve essere bilanciata. Una colonna con pochi valori dominanti (o molti
NULL) concentra le righe in poche distribution. - Usata frequentemente nei JOIN (o
GROUP BY) rispetto ad altre tabelle grandi: abilita i join co-locati. - Non usata nei filtri di uguaglianza
WHERE col = x: filtrare sulla colonna di hash concentra il lavoro su una sola distribution, sprecando il parallelismo.
Sono cattive candidate le colonne data (poca cardinalità, filtrate spesso) e le foreign key sbilanciate.
Data movement, broadcast e columnstore
Quando le righe non sono co-locate, l’engine esegue operazioni di shuffle (ridistribuzione su una nuova colonna) o broadcast move (copia di una tabella piccola su tutte le distribution al volo). Rendere replicated una dimension elimina proprio il broadcast ricorrente.
Di default ogni tabella usa un clustered columnstore index (CCI): storage a colonne, altamente compresso, ideale per scansioni analitiche su fact table. Per efficienza il CCI ha bisogno di rowgroup pieni (fino a circa 1M righe); su tabelle piccole o molto sbilanciate i rowgroup restano sotto-popolati e conviene valutare heap o clustered index.
Partition switching
Oltre alla distribuzione, le tabelle possono avere partition (tipicamente su una colonna data). Il partition switching sposta un’intera partizione tra due tabelle come operazione di metadati istantanea, senza spostare dati fisicamente. È il pattern standard per caricare/eliminare dati per periodo: si carica in una staging table, si esegue lo SWITCH nella partizione target. Requisito chiave: le due tabelle devono avere struttura, distribuzione e schema di partizionamento identici.
Trappole tipiche d’esame
- Fact table enorme che fa scan lente con molto data movement → distribuiscila
HASHsulla colonna usata nei join con le altre fact/dimension grandi, non round-robin. - Una distribution è molto più grande delle altre / query lente su tabella hash → è data skew da colonna di hash a bassa cardinalità o con troppi
NULL; scegli una colonna ad alta cardinalità e bilanciata. - Dimension da 500 MB joinata di continuo con broadcast ripetuto → rendila
REPLICATED, non hash. (Ma se superasse i circa 2 GB, torna a hash.) - Caricamento veloce in tabella temporanea di staging → usa
ROUND_ROBIN: nessun costo di calcolo dell’hash, distribuzione uniforme immediata. - Caricare/eliminare dati mensili minimizzando lock e I/O → usa partition switching da/verso una staging table con schema identico, non
DELETE/INSERTmassivi.