I capitoli 10, 11 e 12 hanno coperto la progettazione concettuale e logica: schemi di fatto, schemi a stella, viste materializzate. Questo capitolo chiude la metodologia con la progettazione fisica, che nelle slide del corso ha due grandi blocchi. Il primo è la progettazione dell'ETL: le procedure che portano i dati dalle sorgenti operazionali al data mart, dal livello estensionale dell'integrazione (capitolo 6) fino al caricamento delle dimension table e delle fact table con le chiavi surrogate. Il secondo è la scelta degli indici: le caratteristiche dei data mart — accessi in sola lettura, aggiornamento periodico, accessi ad ampie porzioni di dati — rendono i B+-tree non più sufficienti e aprono la strada agli indici bitmap, di join e star. È la materia che i quesiti d'esame riassumono in poche domande secche: quale tecnica di estrazione incrementale, quale indice per quale predicato.
Durante la fase di progettazione dell'ETL vengono definite le procedure necessarie a caricare all'interno del data mart i dati provenienti dalle sorgenti operazionali. Le procedure operano in due direzioni: dalle sorgenti operazionali all'ODS realizzano a livello estensionale le trasformazioni definite in fase di integrazione; dall'ODS al data mart conformano la struttura dei dati del livello riconciliato agli schemi a stella utilizzati in ambito multidimensionale.
La distinzione fra le due direzioni è importante perché corrisponde a due nature diverse dei dati. Fra le sorgenti e l'ODS i dati sono integrati e riconciliati: nascono dalle trasformazioni del capitolo 6, applicate però ai valori reali invece che agli schemi. Fra l'ODS e il data mart i dati vengono ridimensionati: gli schemi a stella richiedono chiavi surrogate, gerarchie denormalizzate e misure già pronte per l'aggregazione.
Il percorso che alimenta l'ODS attraversa la staging area: uno spazio utilizzato per memorizzare in via transitoria le informazioni necessarie all'esecuzione delle procedure. La terminologia delle slide distingue quattro operazioni:
| Operazione | Ruolo |
|---|---|
| Estrazione | Operazioni che permettono di acquisire i dati dalle sorgenti |
| Pulizia | Operazioni che permettono di eliminare le incongruenze dovute a errori e omissioni |
| Trasformazione | Operazioni che conformano i dati delle sorgenti allo schema riconciliato |
| Caricamento | Operazioni necessarie a inserire i dati trasformati nell'ODS |
Avanzate le quattro operazioni e osservate dove risiede ciascun dato durante il percorso.
Le operazioni di estrazione dipendono dalla natura dei dati presenti nelle sorgenti operazionali. Le slide classificano tre livelli di storicizzazione:
Transitoria. Il sistema mantiene solo l'immagine corrente dei dati, sovrascrivendo quelli che non sono più validi. Esempi tipici: dati di inventario, scorte di magazzino. Per l'estrazione è un caso difficile: dello stato precedente non resta nulla da leggere.
Semi-storicizzata. Il sistema mantiene un limitato numero di stati precedenti, e non è possibile determinare per quanto tempo ciascun dato verrà conservato. L'estrazione può trovare qualche stato passato, ma senza garanzie sulla completezza della storia.
Storicizzata. Tutte le modifiche intervenute nei dati vengono mantenute in un intervallo di tempo ben definito. Esempi tipici: dati bancari e assicurativi. È il caso più favorevole per l'estrazione incrementale, perché ogni stato passato è ancora leggibile.
Indipendentemente dalla natura dei dati, l'estrazione può avvenire in due modalità. L'estrazione statica estrae tutti i dati dei database sorgenti. L'estrazione incrementale estrae solo i dati modificati o inseriti nell'intervallo di tempo intercorso dall'ultima estrazione, e può essere immediata (le modifiche vengono rilevate e catturate nel momento in cui si verificano) oppure ritardata (le modifiche vengono individuate a posteriori, analizzando tracce lasciate dal sistema).
Le slide presentano quattro tecniche di estrazione incrementale, che si distinguono per il meccanismo con cui le modifiche vengono individuate e per il momento in cui vengono catturate.
Tecnica di estrazione immediata. Le modifiche vengono rilevate da specifiche funzioni implementate direttamente all'interno delle applicazioni OLTP, che le segnalano all'estrattore. Richiede quindi la modifica delle applicazioni; è utile con sistemi legacy che non forniscono sistemi di triggering o log, e trova applicazione anche nei sistemi moderni quando esiste un livello di API comune a tutte le applicazioni: una sola modifica serve per tutti gli accessi di uno stesso tipo.
Tecnica di estrazione ritardata. Le modifiche vengono memorizzate in appositi file prodotti dal DBMS. Il problema: interpretare il contenuto dei log può risultare molto complesso, perché il formato è normalmente proprietario dello specifico DBMS. È consigliabile solo quando il modulo di estrazione è fornito direttamente dal produttore del DBMS.
Tecnica di estrazione immediata. Le modifiche vengono individuate mediante funzioni basate su eventi implementate e controllate direttamente nel DBMS (i trigger). Per motivi prestazionali non è possibile adottarla in modo estensivo: richiederebbe al DBMS di monitorare continuamente tutte le transazioni potenzialmente in grado di innescare un trigger.
Tecnica di estrazione ritardata. Prevede la modifica dello schema del database, che dovrà contenere uno o più campi necessari a contrassegnare i record modificati. Il modulo di estrazione opera a posteriori, individuando il tipo di modifica subita dai dati grazie alle marche.
L'efficacia della tecnica basata su marche temporali dipende dalla struttura stessa del sistema operazionale: se i dati sono transitori o semi-storicizzati, l'estrazione basata su marche temporali non può identificare gli stati intermedi di quei record modificati più volte durante l'intervallo di aggiornamento. L'esempio delle slide è il record del vino Barolo: estratto l'1/4/2002 con quantità 100, il 2/4/2002 la quantità diventa 200, il 3/4/2002 diventa 150. L'estrazione del 3/4/2002 legge lo stato corrente (150) e la marca temporale segnala la modifica — ma la modifica intermedia (200) è persa: non è mai stata estratta e dello stato intermedio non resta traccia.
Data la situazione descritta, scegliete la tecnica di estrazione incrementale appropriata.
Qualunque tecnica incrementale si utilizzi, il risultato della fase di estrazione consiste nell'insieme di record della sorgente modificati, aggiunti o cancellati rispetto alla precedente esecuzione della procedura di estrazione. I dati risiedono nella staging area.
Per facilitare le fasi successive è opportuno associare a ogni record estratto il tipo di operazione (Inserimento, Modifica, Cancellazione) che ne ha generato la variazione. Nell'esempio delle slide, fra la situazione al 4/4/2002 e quella al 6/4/2002 la differenza incrementale contiene quattro record: il record 3 (Barbera) è stato cancellato (C), il record 4 (Sangiovese) è stato modificato (M, da 45 a 145), i record 5 e 6 (Vermentino, Trebbiano) sono stati inseriti (I).
La modalità di caricamento dei dati dalla staging area all'ODS dipende dalla tecnica utilizzata in fase di estrazione e dal livello di storicizzazione dell'ODS:
Il livello di storicizzazione dell'ODS dipende da quello delle sorgenti operazionali e dai requisiti utente relativi alla reportistica operativa: un ODS storicizzato è il presupposto per le gerarchie dinamiche di tipo III del capitolo 12.
Il caricamento di una dimension table richiede quattro passi: identificazione dei dati da caricare; sostituzione degli identificatori con chiavi surrogate; verifica delle corrispondenze identificatori-chiavi surrogata tramite le tabelle di look-up; caricamento delle tuple nella dimension table. La chiave surrogata di una tupla già presente viene riusata; per le tuple nuove viene generata una nuova chiave.
Il caricamento delle fact table segue l'alimentazione delle dimension table, per poter rispettare i vincoli di integrità referenziale. Anche qui si identificano i dati da caricare e si sostituiscono gli identificatori con le chiavi surrogate, usando le tabelle di look-up; alle chiavi si aggiungono le misure (mis 1, mis 3, mis 5, mis 6 nell'esempio delle slide).
Scegliete l'esito corretto per ciascuna situazione di caricamento.
L'insieme delle operazioni atte a garantire la correttezza e la consistenza dei dati presenti nel livello riconciliato. Le slide classificano le sorgenti di errore in quattro famiglie:
| Famiglia | Esempio |
|---|---|
| Errori di battitura | valori digitati male nei sistemi operazionali |
| Differenza di formato dei dati nello stesso campo | date in formati diversi, maiuscole/minuscole |
| Inconsistenza tra valori e descrizione dei campi | evoluzione del modo di operare dell'azienda, evoluzioni della società, convenzioni interne ai reparti diverse da quelle generali del sistema informativo |
| Inconsistenza tra valori di campi correlati | Città='Bologna' e Regione='Lazio' nella stessa tupla |
La maggior parte delle inconsistenze può essere prevenuta rendendo più rigorose le regole di inserimento dei dati nelle applicazioni del sistema operazionale.
Data l'inconsistenza descritta, scegliete la famiglia di tecniche più adatta.
Le viste materializzate del capitolo 12 vanno aggiornate quando arrivano nuovi dati. Il problema è la scelta della vista aggiornata che minimizza il costo di aggiornamento: la risposta delle slide è semplice e diretta — è la più piccola vista che permette di risolvere l'interrogazione.
Il collegamento con l'aggregate navigator del capitolo 12 è immediato: se il navigator sceglie la vista che minimizza il costo di esecuzione delle interrogazioni, il refresh sceglie quella che minimizza il costo di aggiornamento — e in entrambi i casi la logica è la stessa, scegliere la vista più piccola che «fa il lavoro».
Le specifiche caratteristiche dei data mart permettono di utilizzare classi di indici diverse dal ben noto B+-tree utilizzato nella maggior parte dei DBMS commerciali:
Alcuni indici sono nati in conseguenza delle esigenze di data warehousing; altri erano preesistenti ma non venivano utilizzati. Le tre classi citate dalle slide sono gli indici bitmap, gli indici di join e gli indici star.
Nei B+-tree le foglie contengono tutti i valori di chiave; i nodi interni, organizzati come un B-tree, costituiscono solo una mappa per consentire una rapida localizzazione delle chiavi e memorizzano dei separatori.
I B+-tree non sono più sufficienti ma rimangono ancora molto utili: il punto non è abbandonarli, ma affiancarli con indici pensati per la bassa selettività e per i join a stella.
Un indice bitmap su un attributo è composto da una matrice di bit contenente tante righe quante sono le tuple della relazione e tante colonne quanti sono i valori distinti (di chiave) dell'attributo. Il bit (i, j) è posto a TRUE se nella tupla i-esima è presente il valore j-esimo.
L'esempio delle slide costruisce un indice bitmap sulla Posizione della tabella impiegati, con sei valori distinti: Ingegnere, Consulente, Manager, Programmatore, Assistente, Ragioniere. La matrice ha una riga per impiegato (RID 1–5) e una colonna per posizione: l'impiegato con RID 1 è un Manager, quindi la sua riga ha un solo bit a 1, nella colonna Manager.
Normalmente i bitmap sono associati a B+-tree le cui foglie contengono vettori di bit invece di RID. La valutazione di un predicato segue quattro passi: si naviga il B+-tree, si caricano i vettori di bit corrispondenti, si determina quali bit sono posti a TRUE e infine si recuperano le tuple corrispondenti ai RID.
L'esempio delle slide è la domanda «Quanti maschi in Emilia-Romagna sono assicurati?». Si prendono i vettori di bit delle tre condizioni (sesso = M, assicurato = Sì, regione = E/R) e se ne fa l'AND vettoriale: il numero di bit a 1 nel risultato è la risposta — senza mai leggere le tuple.
Gli indici bitmap sono adatti ad attributi con ridotta cardinalità: ogni nuovo valore distinto richiede un ulteriore vettore di bit, e all'aumentare del numero di chiavi distinte aumenta la sparsità della matrice. Con NR = 10.000.000 e puntatori da 4 byte, il confronto fra B-tree e bitmap è:
| Indice | Spazio |
|---|---|
| B-tree | NR × Len(Pointer) = 10.000.000 × 32 bit |
| Bitmap | NR × NK × 1 bit |
Si ha un risparmio di spazio se densità media ≥ 1 / Len(RID); la compressione delle matrici riduce il fattore di crescita della dimensione. Per valori piccoli di NK (attributi a bassa cardinalità) il bitmap occupa una frazione del B-tree; al crescere di NK la matrice si sparge e il vantaggio si riduce — il grafico delle slide mostra le due curve che si incrociano intorno a qualche decina di chiavi distinte.
Data la selettività del predicato, scegliete l'indice più adatto.
Le interrogazioni su schemi a stella richiedono sempre uno o più join. Un indice di join calcola in anticipo le tuple che soddisfano un particolare predicato di join, memorizzando le coppie di RID corrispondenti. Nell'esempio delle slide, l'indice contiene le coppie che soddisfano Vendite.IDNegozi = Negozi.IDNegozi: la riga 1 di Vendite (IDNegozi 123) corrisponde alla riga 1 di Negozi, e così via.
Gli indici star estendono il concetto di indice di join a più tabelle, concatenando i valori delle colonne della fact table e di più dimension table. L'esempio delle slide collega Vendite con Settimana e Negozi: ogni riga dell'indice contiene la quadrupla (VRID, NRID, SID, PID) che rappresenta una combinazione di tuple che soddisfa i join. Tre osservazioni dalle slide:
Il numero di indici star necessari a rispondere efficientemente a interrogazioni che coinvolgono un insieme arbitrario di dimensioni è funzione del numero di permutazioni dell'insieme di dimensioni: cresce rapidamente, ed è per questo che la scelta va guidata dal carico di lavoro.
Data la caratteristica del data mart o del predicato, scegliete la classe di indici appropriata.
Le slide chiudono la parte sugli indici con i consigli operativi: creare indici su
Se il DBMS non utilizza statistiche per definire il piano di accesso, la creazione degli indici deve essere valutata con molta attenzione: senza stime di selettività e cardinalità, un indice scelto a priori può peggiorare le prestazioni invece di migliorarle.
Le slide illustrano la logica di scelta con un esempio ispirato a Redbrick 6.0, un DBMS relazionale storico pensato per il data warehousing. L'algoritmo decide come rispondere a una query interrogandosi su: quante dimension table sono coinvolte da condizioni (0, 1, 2 o più); se esiste un indice sulla fact table che inizia con la chiave della prima dimension table condizionata (IXFT1 su k1); se la query richiede di accedere ai dati della fact table. A seconda delle risposte si accede alla dimension table e poi all'indice, si esegue un hash join, si fa una RID intersection fra le condizioni, o si scandisce la fact table facendo il join con tutte le dimension table richieste dal group-by set — terminando con il raggruppamento delle tuple. La morale è che la scelta dell'indice non è indipendente dal piano di accesso.
Si definiscono le procedure necessarie a caricare all'interno del data mart i dati provenienti dalle sorgenti operazionali. Due direzioni: (1) dalle sorgenti operazionali all'ODS, realizzando a livello estensionale le trasformazioni definite in fase di integrazione; (2) dall'ODS al data mart, conformando la struttura del livello riconciliato agli schemi a stella.
Estrazione (acquisire i dati dalle sorgenti), pulizia (eliminare le incongruenze dovute a errori e omissioni), trasformazione (conformare i dati allo schema riconciliato), caricamento (inserire i dati trasformati nell'ODS). Tutte transitano dalla staging area, spazio di memorizzazione transitoria.
Per natura dei dati: transitoria (solo immagine corrente), semi-storicizzata (limitato numero di stati precedenti), storicizzata (tutte le modifiche mantenute). Per modalità: estrazione statica (tutti i dati) e incrementale (solo i dati modificati/inseriti dall'ultima estrazione), che può essere immediata o ritardata.
Assistita dall'applicazione (immediata: funzioni dentro le applicazioni OLTP); basata su log (ritardata: file di log del DBMS, formato proprietario, consigliata solo col modulo del produttore); basata su trigger (immediata: funzioni basate su eventi nel DBMS, non estensibile per motivi prestazionali); basata su marche temporali (ritardata: campi di marcatura nello schema, opera a posteriori).
Se i dati sono transitori o semi-storicizzati, non può identificare gli stati intermedi dei record modificati più volte durante l'intervallo di aggiornamento: resta solo l'ultimo stato, e le modifiche intermedie vanno perse (esempio Barolo: 100 → 200 → 150, l'estrazione legge 150 e perde il 200).
Nell'insieme di record della sorgente modificati, aggiunti o cancellati rispetto alla precedente esecuzione, residenti nella staging area. A ogni record conviene associare il tipo di operazione (Inserimento, Modifica, Cancellazione) che ne ha generato la variazione.
Estrazione statica → riscrittura completa. Estrazione incrementale: ODS non storicizzato → si memorizza solo il tipo di operazione; ODS storicizzato → si memorizzano anche le marche temporali di validità. Il livello di storicizzazione dell'ODS dipende dalle sorgenti e dai requisiti di reportistica operativa.
Si identificano i dati da caricare, si sostituiscono gli identificatori con chiavi surrogate verificando le corrispondenze nelle tabelle di look-up, quindi si caricano le tuple. Le fact table si caricano dopo le dimension table per rispettare i vincoli di integrità referenziale.
Errori di battitura, differenze di formato, inconsistenze tra valori e descrizione, inconsistenze tra campi correlati (Bologna/Lazio). Tecniche: basate su dizionari (look-up per sinonimi e abbreviazioni, dominio noto e limitato), ad hoc (equazioni, outliers), fusione approssimata (join approssimati, purge/merge). La prevenzione con regole di inserimento rigorose è la strada migliore.
La più piccola vista che permette di risolvere l'interrogazione: è quella che minimizza il costo di aggiornamento, esattamente come l'aggregate navigator sceglie la vista che minimizza il costo di esecuzione.
Danno buone prestazioni con selettività molto elevata, ma le interrogazioni OLAP usano predicati a bassa selettività (es. sesso); sono più adatti a interrogazioni semplici e richiedono molto spazio. Non sono più sufficienti, ma rimangono molto utili.
Una matrice di bit: una riga per tupla, una colonna per valore distinto; bit (i,j) = TRUE se la tupla i ha il valore j. Vantaggi: spazio ridotto, I/O basso (si leggono solo i vettori), ottimi per query senza accesso ai dati, operatori binari (AND/OR/NOT) per i predicati. Adatti a cardinalità ridotta: si risparmia spazio se densità media ≥ 1/Len(RID).
L'indice di join calcola in anticipo le coppie di RID che soddisfano un predicato di join. L'indice star estende il concetto a più tabelle, concatenando le colonne della fact table e di più dimension table: efficiente sulle colonne iniziali, sub-ottimo altrimenti; il numero di indici necessari cresce con le permutazioni delle dimensioni.
Chiavi importate della fact table (per i join: B+-tree, indici di join, star, bitmapped join index), attributi dimensionali spesso in selezione (B+-tree o bitmap), misure spesso in clausole di selezione (bitmap evoluti). Se il DBMS non usa statistiche, la creazione va valutata con molta attenzione.