Quando viene menzionata l'ottimizzazione delle query MySQL, gli indici sono una delle prime cose che vengono trattate. Oggi cercheremo di capire perché sono così importanti.
Cosa sono gli indici?
In generale, un indice è un elenco alfabetico di record con riferimenti alle pagine in cui sono citati. In MySQL, un indice è una struttura di dati utilizzata per trovare rapidamente le righe. Gli indici sono anche chiamati chiavi e tali chiavi sono fondamentali per ottenere buone prestazioni:man mano che i dati aumentano, la necessità di utilizzare gli indici in modo corretto potrebbe diventare sempre più importante. L'utilizzo degli indici è uno dei modi più efficaci per migliorare le prestazioni delle query:se gli indici vengono utilizzati correttamente, le prestazioni delle query potrebbero aumentare di decine o addirittura centinaia di volte.
Oggi cercheremo di spiegare i vantaggi e gli svantaggi di base dell'utilizzo degli indici in MySQL. Tieni presente che gli indici MySQL da soli meritano un intero libro, quindi questo post non coprirà assolutamente tutto, ma sarà un buon punto di partenza. Per coloro che sono interessati a come funzionano gli indici a un livello più profondo, la lettura del libro Relational Database Index Design and the Optimizers di Tapio Lahdenmäki e Michael Leach dovrebbe fornire maggiori informazioni.
I vantaggi dell'utilizzo degli indici
Ci sono alcuni vantaggi principali dell'utilizzo degli indici in MySQL e questi sono i seguenti:
- Gli indici consentono di trovare rapidamente le righe corrispondenti a una clausola WHERE;
- Gli indici possono aiutare le query a evitare la ricerca in determinate righe, riducendo così la quantità di dati che il server deve esaminare - se c'è una scelta tra più indici, MySQL usa solitamente l'indice più selettivo, cioè un indice che trova il minor numero di righe;
- Gli indici possono essere utilizzati per recuperare righe da altre tabelle nelle operazioni JOIN;
- Gli indici possono essere utilizzati per trovare il valore minimo o massimo di una colonna specifica che utilizza un indice;
- Gli indici possono essere utilizzati per ordinare o raggruppare una tabella se le operazioni vengono eseguite su un prefisso all'estrema sinistra di un indice; allo stesso modo, un prefisso all'estrema sinistra di un indice a più colonne potrebbe essere utilizzato da Query Optimizer per cercare le righe;
- Gli indici possono essere utilizzati anche per salvare l'I/O del disco:quando è in uso un indice di copertura, una query può restituire valori direttamente dalla struttura dell'indice salvando l'I/O del disco.
Allo stesso modo, esistono diversi tipi di indici:
- INDEX è un tipo di indice in cui i valori non devono essere univoci. Questo tipo di indice accetta valori NULL;
- UNICO INDEX viene spesso utilizzato per rimuovere le righe duplicate da una tabella:questo tipo di indice consente agli sviluppatori di imporre l'unicità dei valori di riga;
- FULLTEXT INDEX è un indice che viene applicato ai campi che utilizzano funzionalità di ricerca full-text. Questo tipo di indice trova le parole chiave nel testo invece di confrontare direttamente i valori con i valori nell'indice;
- DESCENDING INDEX è un indice che memorizza le righe in ordine decrescente:Query Optimizer sceglierà questo tipo di indice quando la query richiede un ordine decrescente. Questo tipo di indice è stato introdotto in MySQL 8.0;
- PRIMARY KEY è anche un indice. In poche parole, la CHIAVE PRIMARIA è una colonna o un insieme di colonne che identifica ogni riga in una tabella, usata frequentemente insieme ai campi che hanno un attributo AUTO_INCREMENT. Questo tipo di indice non accetta valori NULL e una volta impostati i valori nella CHIAVE PRIMARIA non possono essere modificati.
Ora cercheremo di esaminare sia i vantaggi che gli svantaggi dell'utilizzo degli indici in MySQL. Inizieremo con il vantaggio probabilmente più discusso:accelerare le query che corrispondono a una clausola WHERE.
Accelerare le query corrispondenti a una clausola WHERE
Gli indici sono usati frequentemente per velocizzare le query di ricerca che corrispondono a una clausola WHERE. Il motivo per cui un indice rende più veloci tali operazioni di ricerca è piuttosto semplice:le query che utilizzano un indice evitano una scansione completa della tabella.
Per velocizzare le query che corrispondono a una clausola WHERE puoi utilizzare l'istruzione EXPLAIN in MySQL. L'istruzione EXPLAIN SELECT dovrebbe fornire alcune informazioni su come Query Optimizer MySQL esegue la query:può anche mostrare se la query in questione utilizza un indice o meno e quale indice utilizza. Dai un'occhiata alla seguente spiegazione della query:
mysql> EXPLAIN SELECT * FROM demo_table WHERE field_1 = “Demo” \G;
*************************** 1. row ***************************
<...>
possible_keys: NULL
key: NULL
key_len: NULL
<...>
La query precedente non utilizza un indice. Tuttavia, se aggiungiamo un indice su "field_1", l'indice verrebbe utilizzato correttamente:
mysql> EXPLAIN SELECT * FROM demo_table WHERE field_1 = “Demo” \G;
*************************** 1. row ***************************
<...>
possible_keys: field_1
key: field_1
key_len: 43
<...>
La colonna possible_keys descrive i possibili indici che MySQL può scegliere, la colonna key descrive l'indice effettivamente scelto e la colonna key_len descrive la lunghezza della chiave scelta.
In questo caso, MySQL eseguirà una ricerca dei valori nell'indice e restituirà tutte le righe contenenti il valore specificato - di conseguenza, la query sarebbe più veloce. Sebbene gli indici aiutino alcune query a essere più veloci, ci sono un paio di cose che devi tenere a mente se vuoi che i tuoi indici aiutino le tue query:
- Isola le tue colonne - MySQL non può utilizzare gli indici se le colonne su cui vengono utilizzati gli indici non sono isolate. Ad esempio, una query come questa non userebbe un indice:
SELECT field_1 FROM demo_table WHERE field_1 + 5 = 10;
Per risolvere questo problema, lascia la colonna che segue la clausola WHERE da sola - semplifica il più possibile la tua query e isola le colonne;
- Evita di usare query LIKE con un carattere jolly precedente:in questo caso, MySQL non utilizzerà un indice perché il carattere jolly precedente significa che può esserci qualcosa prima del testo. Se devi utilizzare query LIKE con caratteri jolly e desideri che le query utilizzino indici, assicurati che il carattere jolly si trovi alla fine dell'istruzione di ricerca.
Naturalmente, l'accelerazione delle query che corrispondono a una clausola WHERE può essere eseguita anche in altri modi (ad esempio, il partizionamento), ma per semplicità, non lo esamineremo ulteriormente in questo post.
Tuttavia, ciò che potrebbe interessarci sono diversi tipi di tipi di indice, quindi esamineremo questo ora.
Eliminazione dei valori duplicati in una colonna - Indici UNIQUE
Lo scopo di un indice UNIQUE in MySQL è quello di rafforzare l'unicità dei valori in una colonna. Per utilizzare un indice UNIQUE, eseguire una query CREATE UNIQUE INDEX:
CREATE UNIQUE INDEX demo_index ON demo_table(demo_column);
You can also create a unique index when you create a table:
CREATE TABLE demo_table (
`demo_column` VARCHAR(100) NOT NULL,
UNIQUE KEY(demo_column)
);
Questo è tutto ciò che serve per aggiungere un indice univoco a una tabella. Ora, quando provi ad aggiungere un valore duplicato alla tabella, MySQL tornerà con il seguente errore:
#1062 - Duplicate entry ‘Demo’ for key ‘demo_column’
Indici FULLTEXT
Un indice FULLTEXT è un indice di questo tipo che viene applicato alle colonne che utilizzano funzionalità di ricerca full-text. Questo tipo di indice ha molte funzionalità uniche, tra cui stopword e modalità di ricerca.
L'elenco delle stopword di InnoDB ha 36 parole mentre l'elenco delle stopword di MyISAM ne ha 143. In InnoDB, le stopword sono derivate dalla tabella impostata nella variabile innodb_ft_user_stopword_table, altrimenti, se questa variabile non è impostata vengono derivate dalla variabile innodb_ft_server_stopword_table. Se nessuna di queste due variabili è impostata, InnoDB utilizza l'elenco integrato. Per visualizzare l'elenco delle parole non significative di InnoDB predefinito, interroga la tabella INNODB_FT_DEFAULT_STOPWORD.
In MyISAM, le stopword sono derivate dal file storage/myisam/ft_static.c. La variabile ft_stopword_file consente di modificare l'elenco delle parole non significative predefinito. Le stopword saranno disabilitate se questa variabile è impostata su una stringa vuota, ma tieni presente che se questa variabile definisce un file, il file definito non viene analizzato per i commenti - MyISAM tratterà tutte le parole trovate nel file come stopword.
Gli indici FULLTEXT sono famosi anche per le sue modalità di ricerca uniche:
- Se viene eseguita una query di ricerca FULLTEXT senza modificatori, verrà attivata una modalità in linguaggio naturale. La modalità in linguaggio naturale può essere attivata anche utilizzando il modificatore IN MODALITÀ LINGUA NATURALE;
- Il modificatore WITH QUERY EXPANSION abilita una modalità di ricerca con espansione della query. Tale modalità di ricerca funziona eseguendo la ricerca due volte e quando la ricerca viene eseguita per la seconda volta, il set di risultati includerà alcuni dei documenti più rilevanti della prima ricerca. In generale, questo modificatore è utile quando l'utente ha una conoscenza implicita (ad esempio, l'utente potrebbe cercare "database" e sperare di vedere "InnoDB" e "MyISAM" nel set di risultati);
- Il modificatore IN BOOLEAN MODE consente la ricerca con operatori booleani. Ad esempio, gli operatori +, - o * svolgerebbero ciascuno compiti diversi:l'operatore + definirebbe che il valore deve essere presente in una riga, l'operatore - definirebbe che il valore non deve esistere e l'operatore * fungerebbe da carattere jolly.
Una query che utilizza un indice FULLTEXT ha il seguente aspetto:
SELECT * FROM demo_table WHERE MATCH(demo_field) AGAINST(‘value’ IN NATURAL LANGUAGE MODE);
Tieni presente che gli indici FULLTEXT sono generalmente utili per le operazioni MATCH() AGAINST() - non per le operazioni WHERE, il che significa che se viene utilizzata una clausola WHERE, l'utilità di utilizzare diversi tipi di indice non verrebbe eliminata.
Vale anche la pena ricordare che gli indici FULLTEXT hanno una lunghezza minima di caratteri. In InnoDB, una ricerca FULLTEXT può essere eseguita solo quando la query di ricerca è composta da un minimo di tre caratteri:questo limite viene aumentato a quattro caratteri nel motore di archiviazione MyISAM.
Indici DISCENDENTI
Un indice DESCENDING è un tale indice in cui InnoDB memorizza le voci in ordine decrescente:Query Optimizer utilizzerà tale indice quando la query richiede un ordine decrescente. Tale indice può essere aggiunto a una colonna eseguendo una query come di seguito:
CREATE INDEX descending_index ON demo_table(column_name DESC);
Un indice crescente può anche essere aggiunto a una colonna:basta sostituire DESC con ASC.
CHIAVI PRIMARIE
Una CHIAVE PRIMARIA funge da identificatore univoco per ogni riga di una tabella. Una colonna con una CHIAVE PRIMARIA deve contenere valori univoci - non è consentito utilizzare nemmeno valori NULL. Se viene aggiunto un valore duplicato a una colonna che ha una CHIAVE PRIMARIA, MySQL risponderà con un errore #1062:
#1062 - Duplicate entry ‘Demo’ for key ‘PRIMARY’
Se viene aggiunto un valore NULL alla colonna, MySQL risponderà con un errore #1048:
#1048 - Column ‘id’ cannot be null
Gli indici primari sono anche chiamati indici cluster (ne parleremo più avanti).
Puoi anche creare indici su più colonne contemporaneamente:tali indici sono chiamati indici multicolonna.
Indici multicolonna
Gli indici su più colonne sono spesso fraintesi - a volte sviluppatori e DBA indicizzano tutte le colonne separatamente o le indicizzano nell'ordine sbagliato. Per rendere le query che utilizzano indici multicolonna il più efficaci possibile, ricorda che l'ordine delle colonne negli indici che utilizzano più di una colonna è una delle cause più comuni di confusione in questo spazio, poiché non ci sono "da questa parte o dall'autostrada ” soluzioni per l'ordine degli indici, è necessario ricordare che l'ordine corretto degli indici a più colonne dipende dalle query che utilizzano l'indice. Anche se questo può sembrare abbastanza ovvio, ricorda che l'ordine delle colonne è fondamentale quando si tratta di indici a più colonne:scegli l'ordine delle colonne in modo che sia il più selettivo possibile per le query che verranno eseguite più frequentemente.
Per misurare la selettività per colonne specifiche, ottieni il rapporto tra il numero di valori indicizzati distinti e il numero totale di righe nella tabella:la colonna con la selettività più alta dovrebbe essere la prima .
A volte è necessario indicizzare anche colonne di caratteri molto lunghe e, in tal caso, è possibile risparmiare tempo e risorse indicizzando i primi caratteri, un prefisso, anziché l'intero valore.
Indici prefissi
Gli indici dei prefissi possono essere utili quando le colonne contengono valori di stringa molto lunghi, il che significherebbe che l'aggiunta di un indice sull'intera colonna consumerebbe molto spazio su disco. MySQL aiuta a risolvere questo problema consentendo di indicizzare solo un prefisso del valore che a sua volta riduce la dimensione dell'indice. Dai un'occhiata:
CREATE TABLE `demo_table` (
`demo_column` VARCHAR(100) NOT NULL,
INDEX(demo_column(10))
);
La query precedente creerebbe un indice di prefisso sulla colonna demo indicizzando solo i primi 10 caratteri del valore. Puoi anche aggiungere un indice di prefisso a una tabella esistente:
CREATE INDEX index_name ON table_name(column_name(length));
Quindi, ad esempio, se vuoi indicizzare i primi 5 caratteri di una demo_column su una demo_table, puoi eseguire la seguente query:
CREATE INDEX demo_index ON demo_table(demo_column(5));
Dovresti scegliere un prefisso sufficientemente lungo da dare selettività, ma anche sufficientemente corto da dare spazio. Tuttavia, potrebbe essere più facile a dirsi che a farsi:devi sperimentare e trovare la soluzione che funzioni per te.
Indici di copertura
Un indice di copertura "copre" tutti i campi richiesti per eseguire una query. In altre parole, quando tutti i campi di una query sono coperti da un indice, è in uso un indice di copertura. Ad esempio per una query del genere:
SELECT id, title FROM demo_table WHERE id = 1;
Un indice di copertura potrebbe essere simile a questo:
INDEX index_name(id, title);
Se vuoi assicurarti che una query utilizzi un indice di copertura, emetti un'istruzione EXPLAIN su di essa, quindi dai un'occhiata alla colonna Extra. Ad esempio, se la tua tabella ha un indice multicolonna su id e title e viene eseguita una query che accede solo a queste due colonne, MySQL utilizzerà l'indice:
mysql> EXPLAIN SELECT id, title FROM demo_table \G;
*************************** 1. row ***************************
<...>
type: index
key: index_name
key_len: 5
rows: 1000
Extra: Using index
<...>
Tieni presente che un indice di copertura deve memorizzare i valori delle colonne che copre. Ciò significa che MySQL può utilizzare solo gli indici B-Tree per coprire le query perché altri tipi di indici non memorizzano questi valori.
Indici cluster, secondari e cardinalità dell'indice
Quando vengono discussi gli indici, potresti anche sentire i termini cluster, indici secondari e cardinalità dell'indice. In parole povere, gli indici cluster sono un approccio all'archiviazione dei dati e tutti gli indici diversi dagli indici cluster sono indici secondari. La cardinalità dell'indice è invece il numero di valori univoci in un indice.
Un indice cluster velocizza le query perché anche i valori vicini vengono archiviati uno vicino all'altro sul disco, ma questo è anche il motivo per cui puoi avere un solo indice cluster in una tabella.
Un indice secondario è qualsiasi indice che non sia l'indice primario. Tale indice potrebbe avere duplicati.
Gli svantaggi dell'utilizzo degli indici
L'utilizzo degli indici ha sicuramente dei vantaggi, ma non dobbiamo dimenticare che gli indici possono essere una delle principali cause di problemi anche in MySQL. Alcuni degli svantaggi dell'utilizzo degli indici sono i seguenti:
- Gli indici possono peggiorare le prestazioni di determinate query - anche se gli indici tendono a velocizzare le prestazioni delle query SELECT, rallentano le prestazioni delle query INSERT, UPDATE e DELETE perché quando i dati vengono aggiornati index deve essere aggiornato insieme ad esso:qualsiasi operazione che comporti la manipolazione degli indici sarà più lenta del solito;
- Gli indici consumano spazio su disco:un indice occupa il proprio spazio, quindi anche i dati indicizzati consumeranno più spazio su disco;
- Gli indici ridondanti e duplicati possono essere un problema:MySQL ti consente di creare indici duplicati su una colonna e non ti "protegge" dal fare un simile errore. Dai un'occhiata a questo esempio:
CREATE TABLE `demo_table` ( `id` INT(10) NOT NULL AUTO_INCREMENT PRIMARY KEY, `column_2` VARCHAR(10) NOT NULL, `column_3` VARCHAR(10) NOT NULL, INDEX(id), UNIQUE(id) );
Un utente inesperto potrebbe pensare che questa query faccia aumentare automaticamente la colonna id, quindi aggiunge un indice sulla colonna e fa in modo che la colonna non accetti valori duplicati. Tuttavia, questo non è ciò che sta accadendo qui. In questo caso, la stessa colonna contiene tre indici:un normale INDEX, e poiché MySQL implementa entrambi i vincoli PRIMARY KEY e UNIQUE con gli indici, ciò aggiunge altri due indici sulla stessa colonna!
Conclusione
Per concludere, gli indici in MySQL hanno il loro posto:gli indici possono essere utilizzati in una moltitudine di scenari, ma ognuno di questi scenari di utilizzo ha i suoi aspetti negativi che devono essere considerati per ottenere il massimo da indici in uso.
Per utilizzare bene gli indici, profila le tue query, dai un'occhiata alle opzioni che hai quando si tratta di indici, conosci i loro vantaggi e svantaggi, decidi di quali indici hai bisogno in base alle tue esigenze e dopo aver indicizzato le colonne, assicurati che i tuoi indici siano effettivamente utilizzato da MySQL. Se hai indicizzato correttamente il tuo schema, le prestazioni delle tue query dovrebbero migliorare, ma se il tempo di risposta non ti soddisfa, verifica se è possibile creare un indice migliore per migliorarlo.