Denormalizzare per migliorare le prestazioni? Sembra convincente, ma non trattiene l'acqua.
Chris Date, che in collaborazione con il dottor Ted Codd era il proponente originale del modello di dati relazionali, ha esaurito la pazienza con argomenti male informati contro la normalizzazione e li ha demoliti sistematicamente usando il metodo scientifico: ha ottenuto grandi database e testato queste affermazioni.
Penso che l'abbia scritto in Relational Database Writings 1988-1991, ma questo libro è stato successivamente inserito nell'edizione sei di Introduzione ai sistemi di database , che è il testo definitivo sulla teoria e la progettazione del database, alla sua ottava edizione mentre scrivo e probabilmente rimarrà in stampa per decenni a venire. Chris Date era un esperto in questo campo quando la maggior parte di noi correva ancora scalza.
Ha scoperto che:
- Alcuni valgono per casi speciali
- Tutti loro non riescono a pagare per un uso generale
- Tutti sono significativamente peggiori per altri casi speciali
Tutto ritorna a mitigare le dimensioni del set di lavoro. I join che coinvolgono chiavi correttamente selezionate con indici impostati correttamente sono economici, non costosi, poiché consentono una potatura significativa del risultato prima che le righe vengano materializzate.
La materializzazione del risultato implica letture di dischi di massa che rappresentano l'aspetto più costoso dell'esercizio per ordine di grandezza. L'esecuzione di un join, invece, richiede logicamente il recupero dei soli tasti . In pratica, nemmeno i valori chiave vengono recuperati: i valori hash chiave vengono utilizzati per i confronti dei join, mitigando il costo dei join a più colonne e riducendo radicalmente il costo dei join che comportano confronti tra stringhe. Non solo si adatta molto di più alla cache, c'è molto meno lettura del disco da fare.
Inoltre, un buon ottimizzatore sceglierà la condizione più restrittiva e la applicherà prima di eseguire un join, sfruttando in modo molto efficace l'elevata selettività dei join su indici con elevata cardinalità.
È vero che questo tipo di ottimizzazione può essere applicato anche ai database denormalizzati, ma il tipo di persone che vogliono denormalizzare uno schema in genere non pensa alla cardinalità quando (se) impostano gli indici.
È importante capire che le scansioni delle tabelle (esame di ogni riga in una tabella nel corso della produzione di un join) sono rare nella pratica. Uno strumento di ottimizzazione delle query sceglierà una scansione della tabella solo quando una o più delle seguenti condizioni sono valide.
- Ci sono meno di 200 righe nella relazione (in questo caso una scansione sarà più economica)
- Non ci sono indici adatti sulle colonne di join (se è significativo unirsi su queste colonne, allora perché non sono indicizzate? Risolvere il problema)
- Prima di poter confrontare le colonne è necessario un tipo di coercizione (WTF ?! risolverlo o tornare a casa) VEDI NOTE FINE PER IL PROBLEMA DI ADO.NET
- Uno degli argomenti del confronto è un'espressione (nessun indice)
L'esecuzione di un'operazione è più costosa di non eseguirla. Tuttavia, eseguire un'operazione sbagliata , essere costretti a effettuare operazioni inutili di I / O su disco e quindi a scartare le scorie prima di eseguire il join di cui si ha realmente bisogno, è molto più costoso. Anche quando è stata pre-calcolata un'operazione "sbagliata" e gli indici sono stati applicati in modo ragionevole, rimane una penalità significativa. La denormalizzazione per precompilare un join - nonostante le anomalie di aggiornamento comportate - è un impegno per un join specifico. Se avete bisogno di un diverso unirsi, che l'impegno sta andando a costare grande .
Se qualcuno vuole ricordarmi che si tratta di un mondo che cambia, penso che scoprirai che set di dati più grandi su hardware più grande esagerano la diffusione dei risultati di Date.
Per tutti voi che lavorate su sistemi di fatturazione o generatori di posta indesiderata (vergognatevi) e state indignando la mano sulla tastiera per dirmi che sapete per certo che la denormalizzazione è più veloce, scusate ma vivete in uno degli speciali casi, in particolare il caso in cui vengono elaborati tutti i dati, in ordine. Non è un caso generale, e si sono giustificati nella vostra strategia.
Si sono non giustificati nel generalizzare falsamente esso. Vedere la fine della sezione delle note per ulteriori informazioni sull'uso appropriato della denormalizzazione negli scenari di data warehousing.
Vorrei anche rispondere
I join sono solo prodotti cartesiani con alcuni lucidalabbra
Che carico di bollocks. Le restrizioni vengono applicate il più presto possibile, prima le più restrittive. Hai letto la teoria, ma non l'hai capito. I join vengono considerati "prodotti cartesiani ai quali si applicano i predicati" solo dall'ottimizzatore delle query. Questa è una rappresentazione simbolica (una normalizzazione, in effetti) per facilitare la decomposizione simbolica in modo che l'ottimizzatore possa produrre tutte le trasformazioni equivalenti e classificarle in base al costo e alla selettività in modo da poter selezionare il miglior piano di query.
L'unico modo in cui riuscirai mai a ottenere l'ottimizzatore per produrre un prodotto cartesiano è non riuscire a fornire un predicato: SELECT * FROM A,B
Appunti
David Aldridge fornisce alcune importanti informazioni aggiuntive.
Esistono in effetti una varietà di altre strategie oltre agli indici e alle scansioni delle tabelle e un moderno ottimizzatore le costerà tutte prima di produrre un piano di esecuzione.
Un consiglio pratico: se può essere usato come chiave esterna, indicizzalo, in modo che una strategia di indicizzazione sia disponibile per l'ottimizzatore.
Ero più intelligente dell'ottimizzatore MSSQL. Ciò è cambiato due versioni fa. Ora generalmente mi insegna . È, in un senso molto reale, un sistema esperto, che codifica tutta la saggezza di molte persone molto intelligenti in un dominio sufficientemente chiuso da rendere efficace un sistema basato su regole.
"Bollocks" potrebbe essere stato senza tatto. Mi viene chiesto di essere meno altero e mi viene in mente che la matematica non mente. Questo è vero, ma non tutte le implicazioni dei modelli matematici dovrebbero necessariamente essere prese alla lettera. Radici quadrate di numeri negativi sono molto utili se eviti attentamente di esaminarne l'assurdità (gioco di parole lì) e assicurati dannatamente di annullarle tutte prima di provare a interpretare la tua equazione.
Il motivo per cui ho risposto così selvaggiamente è stato che l'affermazione formulata lo dice
I join sono prodotti cartesiani ...
Questo potrebbe non essere ciò che si intendeva, ma è ciò che è stato scritto ed è categoricamente falso. Un prodotto cartesiano è una relazione. Un join è una funzione. Più specificamente, un join è una funzione valutata in base alla relazione. Con un predicato vuoto produrrà un prodotto cartesiano e verificare che ciò avvenga è un controllo di correttezza per un motore di query del database, ma nessuno scrive join non vincolati in pratica perché non hanno alcun valore pratico al di fuori di una classe.
L'ho chiamato perché non voglio che i lettori cadano nell'antica trappola di confondere il modello con la cosa modellata. Un modello è un'approssimazione, deliberatamente semplificata per una comoda manipolazione.
Il limite per la selezione di una strategia di join per la scansione di tabelle può variare tra i motori di database. È influenzato da una serie di decisioni di implementazione come il fattore di riempimento del nodo dell'albero, la dimensione del valore-chiave e le sottigliezze dell'algoritmo, ma in generale l'indicizzazione ad alte prestazioni ha un tempo di esecuzione di k log n + c . Il termine C è un overhead fisso costituito principalmente da tempo di configurazione e la forma della curva indica che non si ottiene un payoff (rispetto a una ricerca lineare) fino a quando n è tra le centinaia.
A volte la denormalizzazione è una buona idea
La denormalizzazione è un impegno per una particolare strategia di partecipazione. Come accennato in precedenza, ciò interferisce con altre strategie di join. Ma se si dispone di secchi di spazio su disco, modelli prevedibili di accesso e una tendenza a elaborarli in gran parte o tutti, quindi precompilare un join può essere molto utile.
È inoltre possibile capire i percorsi di accesso utilizzati in genere dall'operazione e precompilare tutti i join per tali percorsi di accesso. Questa è la premessa alla base dei data warehouse, o almeno lo è quando sono creati da persone che sanno perché stanno facendo quello che stanno facendo, e non solo per il rispetto della parola d'ordine.
Un data warehouse correttamente progettato viene prodotto periodicamente da una trasformazione in blocco da un sistema di elaborazione delle transazioni normalizzato. Questa separazione delle banche dati sulle operazioni e sui rapporti ha l'effetto molto desiderabile di eliminare lo scontro tra OLTP e OLAP (elaborazione delle transazioni online, ovvero immissione dei dati, e elaborazione analitica online, ovvero rapporti).
Un punto importante qui è che, a parte gli aggiornamenti periodici, il data warehouse è di sola lettura . Questo rende discutibile la questione delle anomalie di aggiornamento.
Non commettere l'errore di denormalizzare il database OLTP (il database su cui avviene l'immissione dei dati). Potrebbe essere più veloce per le esecuzioni di fatturazione, ma se lo fai otterrai anomalie di aggiornamento. Hai mai provato a convincere Reader's Digest a smettere di inviarti cose?
Lo spazio su disco è poco costoso in questi giorni, quindi buttati fuori. Ma la denormalizzazione è solo una parte della storia dei data warehouse. Guadagni prestazionali molto più grandi derivano da valori cumulativi precalcolati: totali mensili, quel genere di cose. Si tratta sempre di ridurre il set di lavoro.
Problema ADO.NET con tipi non corrispondenti
Supponiamo di avere una tabella di SQL Server contenente una colonna indicizzata di tipo varchar e di utilizzare AddWithValue per passare un parametro che vincola una query su questa colonna. Le stringhe C # sono Unicode, quindi il tipo di parametro dedotto sarà NVARCHAR, che non corrisponde a VARCHAR.
VARCHAR in NVARCHAR è una conversione in aumento, quindi accade in modo implicito, ma saluta l'indicizzazione e buona fortuna per capire perché.
"Count the disk hits" (Rick James)
Se tutto è memorizzato nella cache nella RAM, JOINs
è piuttosto economico. Cioè, la normalizzazione non ha molta penalità di prestazione .
Se uno schema "normalizzato" fa JOINs
colpire molto il disco, ma lo schema "denormalizzato" equivalente non dovrebbe colpire il disco, allora la denormalizzazione vince un concorso di prestazioni.
Commento dell'autore originale: i moderni motori di database sono molto bravi nell'organizzazione del sequenziamento degli accessi per ridurre al minimo le mancate cache durante le operazioni di join. Quanto sopra, sebbene vero, potrebbe essere errato nel senso che implica che i join sono necessariamente problematicamente costosi su dati di grandi dimensioni. Ciò porterebbe a un cattivo processo decisionale da parte di sviluppatori inesperti.