Perché è necessario?
Quando i dati vengono archiviati su dispositivi di archiviazione basati su disco, vengono archiviati come blocchi di dati. Questi blocchi sono accessibili nella loro interezza, rendendoli l'operazione di accesso al disco atomico. I blocchi del disco sono strutturati in modo molto simile agli elenchi collegati; entrambi contengono una sezione per i dati, un puntatore alla posizione del nodo (o blocco) successivo ed entrambi non devono essere archiviati in modo contiguo.
A causa del fatto che un numero di record può essere ordinato solo su un campo, possiamo affermare che la ricerca su un campo che non è ordinato richiede una ricerca lineare che richiede l' N/2
accesso ai blocchi (in media), dove N
è il numero di blocchi che il tavolo si estende. Se quel campo è un campo non chiave (cioè non contiene voci univoche), è necessario cercare l'intero spazio tabella agli N
accessi ai blocchi.
Considerando che con un campo ordinato, può essere utilizzata una ricerca binaria, che ha log2 N
accessi a blocchi. Inoltre, poiché i dati vengono ordinati in base a un campo non chiave, non è necessario cercare valori duplicati nel resto della tabella, una volta trovato un valore più elevato. Pertanto l'aumento delle prestazioni è notevole.
Che cos'è l'indicizzazione?
L'indicizzazione è un modo per ordinare un numero di record su più campi. La creazione di un indice su un campo in una tabella crea un'altra struttura di dati che contiene il valore del campo e un puntatore al record a cui si riferisce. Questa struttura di indice viene quindi ordinata, consentendo di eseguire ricerche binarie su di essa.
Il lato negativo dell'indicizzazione è che questi indici richiedono spazio aggiuntivo sul disco poiché gli indici sono memorizzati insieme in una tabella usando il motore MyISAM, questo file può raggiungere rapidamente i limiti di dimensione del file system sottostante se molti campi all'interno della stessa tabella sono indicizzati .
Come funziona?
Innanzitutto, delineamo uno schema di tabella del database di esempio;
Nome campo Tipo di dati Dimensioni su disco
id (chiave primaria) INT 4 byte senza segno
firstName Char (50) 50 byte
lastName Char (50) 50 byte
emailAddress Char (100) 100 byte
Nota : char è stato usato al posto di varchar per consentire una dimensione accurata sul valore del disco. Questo database di esempio contiene cinque milioni di righe ed è non indicizzato. Verranno ora analizzate le prestazioni di diverse query. Si tratta di una query che utilizza l' id (un campo chiave ordinato) e uno che utilizza firstName (un campo non ordinato non chiave).
Esempio 1 : campi ordinati o non ordinati
Dato il nostro database di esempio di r = 5,000,000
record di dimensioni fisse che forniscono una lunghezza record di R = 204
byte e sono archiviati in una tabella utilizzando il motore MyISAM che utilizza i B = 1,024
byte di dimensione del blocco predefiniti . Il fattore di blocco della tabella sarebbero i bfr = (B/R) = 1024/204 = 5
record per blocco del disco. Il numero totale di blocchi richiesti per contenere la tabella è N = (r/bfr) = 5000000/5 = 1,000,000
blocchi.
Una ricerca lineare sul campo ID richiederebbe una media di N/2 = 500,000
accessi a blocchi per trovare un valore, dato che il campo ID è un campo chiave. Ma poiché anche il campo ID è ordinato, è possibile condurre una ricerca binaria che richiede una media di log2 1000000 = 19.93 = 20
accessi a blocchi. Immediatamente possiamo vedere che questo è un drastico miglioramento.
Ora il campo firstName non è né ordinato né un campo chiave, quindi una ricerca binaria è impossibile, né i valori sono univoci, e quindi la tabella richiederà la ricerca fino alla fine per un accesso esatto a N = 1,000,000
blocchi. È questa situazione che l'indicizzazione mira a correggere.
Dato che un record di indice contiene solo il campo indicizzato e un puntatore al record originale, è logico che sarà più piccolo del record multi-campo a cui punta. Quindi l'indice stesso richiede meno blocchi su disco rispetto alla tabella originale, che pertanto richiede meno accessi ai blocchi per scorrere. Lo schema per un indice nel campo firstName è delineato di seguito;
Nome campo Tipo di dati Dimensioni su disco
firstName Char (50) 50 byte
(puntatore del record) 4 byte speciali
Nota : i puntatori in MySQL hanno una lunghezza di 2, 3, 4 o 5 byte a seconda della dimensione della tabella.
Esempio 2 - indicizzazione
Dato il nostro database di esempio di r = 5,000,000
record con una lunghezza record di R = 54
byte di indice e utilizzando i B = 1,024
byte di dimensione del blocco predefiniti . Il fattore di blocco dell'indice sarebbe rappresentato dai bfr = (B/R) = 1024/54 = 18
record per blocco del disco. Il numero totale di blocchi richiesti per contenere l'indice è N = (r/bfr) = 5000000/18 = 277,778
blocchi.
Ora una ricerca che utilizza il campo firstName può utilizzare l'indice per aumentare le prestazioni. Ciò consente una ricerca binaria dell'indice con una media degli log2 277778 = 18.08 = 19
accessi ai blocchi. Per trovare l'indirizzo del record effettivo, che richiede un ulteriore accesso al blocco per la lettura, portando il totale a 19 + 1 = 20
bloccare gli accessi, è molto diverso dai 1.000.000 di accessi al blocco richiesti per trovare una corrispondenza firstName nella tabella non indicizzata.
Quando dovrebbe essere usato?
Dato che la creazione di un indice richiede spazio su disco aggiuntivo (277.778 blocchi extra dall'esempio precedente, un aumento del ~ 28%) e che troppi indici possono causare problemi derivanti dai limiti delle dimensioni del file system, è necessario usare un pensiero attento per selezionare il corretto campi da indicizzare.
Poiché gli indici vengono utilizzati solo per accelerare la ricerca di un campo corrispondente all'interno dei record, è logico che i campi di indicizzazione utilizzati solo per l'output sarebbero semplicemente uno spreco di spazio su disco e di tempo di elaborazione quando si esegue un'operazione di inserimento o eliminazione, e quindi dovrebbe essere evitato. Data anche la natura di una ricerca binaria, la cardinalità o unicità dei dati è importante. L'indicizzazione su un campo con una cardinalità di 2 dividerebbe i dati a metà, mentre una cardinalità di 1.000 restituirebbe circa 1.000 record. Con una cardinalità così bassa l'efficacia viene ridotta a un ordinamento lineare e l'ottimizzatore delle query eviterà di utilizzare l'indice se la cardinalità è inferiore al 30% del numero record, rendendo effettivamente l'indice uno spreco di spazio.