Come funzionano gli indici MySQL?


402

Sono davvero interessato a come funzionano gli indici MySQL, in particolare, come possono restituire i dati richiesti senza eseguire la scansione dell'intera tabella?

È fuori tema, lo so, ma se c'è qualcuno che potrebbe spiegarmelo in dettaglio, sarei molto, molto grato.



Questa è una domanda molto ampia. Se hai un esempio specifico di una query che non utilizzerà un indice e non sai perché, potresti pubblicarlo e le persone potrebbero aiutarti.
Hammerite,

SELECT * FROM members WHERE id = '1'- allora perché con l'indice funziona più velocemente? Cosa fa questo indice qui?
good_evening

2
Sembra una query che cerca solo uno specifico record indicizzato (forse identificato dalla chiave primaria). L'indice lo rende più veloce perché è archiviato in memoria, è possibile esaminare la riga dell'indice corrispondente e contiene un puntatore al punto in cui sono archiviati i dati effettivi. Quindi MySQL può andare nella posizione esatta della tabella senza dover scansionare la tabella.
Hammerite,

Molto bene grazie!
Razze di leggerezza in orbita

Risposte:


513

Fondamentalmente un indice su una tabella funziona come un indice in un libro (ecco da dove viene il nome):

Supponiamo che tu abbia un libro sui database e che desideri trovare alcune informazioni sull'archiviazione. Senza un indice (supponendo che nessun altro aiuto, come ad esempio un sommario), dovresti scorrere le pagine una per una, fino a quando non trovi l'argomento (che è a full table scan). D'altra parte, un indice ha un elenco di parole chiave, quindi dovresti consultare l'indice e vedere che storageè menzionato nelle pagine 113-120.231 e 354. Quindi potresti passare direttamente a quelle pagine, senza cercare (è una ricerca con un indice, un po 'più veloce).

Naturalmente, quanto utile sarà l'indice, dipende da molte cose - alcuni esempi, usando la similitudine sopra:

  • se avessi un libro sui database e indicizzassi la parola "database", vedresti che è menzionato nelle pagine 1-59,61-290 e da 292 a 400. In tal caso, l'indice non è di grande aiuto e potrebbe essere più veloce per scorrere le pagine una alla volta (in un database, questa è "scarsa selettività").
  • Per un libro di 10 pagine, non ha senso creare un indice, poiché potresti finire con un libro di 10 pagine con il prefisso di un indice di 5 pagine, il che è semplicemente sciocco: scansiona le 10 pagine e finisci con esso .
  • Anche l'indice deve essere utile: in genere non c'è alcun punto da indicizzare, ad esempio la frequenza della lettera "L" per pagina.

3
Stai spiegando che cos'è, non come tecnicamente funziona internamente.
Tutu Kumari

@Tutu Kumari: vedi le revisioni della domanda; sentiti libero di rivedere anche la risposta per adattarla alla domanda corrente (nota i vari motori e tipi di indice - vedi ad esempio la documentazione qui: dev.mysql.com/doc/refman/8.0/en/index-btree-hash.html )
Piskvor lasciò l'edificio il

259

La prima cosa che devi sapere è che gli indici sono un modo per evitare di scansionare l'intera tabella per ottenere il risultato che stai cercando.

Esistono diversi tipi di indici e sono implementati nel livello di archiviazione, quindi non esiste uno standard tra di loro e dipendono anche dal motore di archiviazione che stai utilizzando.

InnoDB e l'indice B + Tree

Per InnoDB, il tipo di indice più comune è l'indice basato su albero B +, che memorizza gli elementi in un ordine ordinato. Inoltre, non è necessario accedere alla tabella reale per ottenere i valori indicizzati, il che rende la query di ritorno molto più veloce.

Il "problema" su questo tipo di indice è che è necessario richiedere il valore più a sinistra per utilizzare l'indice. Quindi, se il tuo indice ha due colonne, ad esempio last_name e first_name, l'ordine in cui richiedi questi campi è molto importante .

Quindi, data la seguente tabella:

CREATE TABLE person (
    last_name VARCHAR(50) NOT NULL,
    first_name VARCHAR(50) NOT NULL,
    INDEX (last_name, first_name)
);

Questa query trarrebbe vantaggio dall'indice:

SELECT last_name, first_name FROM person
WHERE last_name = "John" AND first_name LIKE "J%"

Ma il seguente no

SELECT last_name, first_name FROM person WHERE first_name = "Constantine"

Perché stai interrogando first_nameprima la colonna e non è la colonna più a sinistra nell'indice.

Quest'ultimo esempio è anche peggio:

SELECT last_name, first_name FROM person WHERE first_name LIKE "%Constantine"

Perché ora stai confrontando la parte più a destra del campo più a destra nell'indice.

L'indice hash

Questo è un tipo di indice diverso che purtroppo supporta solo il backend di memoria. È velocissimo ma utile solo per ricerche complete, il che significa che non è possibile utilizzarlo per operazioni come >, <o LIKE.

Dal momento che funziona solo per il backend di memoria, probabilmente non lo userai molto spesso. Il caso principale che mi viene in mente in questo momento è quello in cui crei una tabella temporanea in memoria con una serie di risultati da un'altra selezione ed esegui molte altre selezioni in questa tabella temporanea utilizzando gli indici hash.

Se hai un VARCHARcampo grande , puoi "emulare" l'uso di un indice hash quando usi un B-Tree, creando un'altra colonna e salvando un hash del valore più grande su di esso. Diciamo che stai memorizzando un url in un campo e i valori sono abbastanza grandi. È inoltre possibile creare un campo intero chiamato url_hashe utilizzare una funzione hash simile CRC32o qualsiasi altra funzione hash per eseguire l'hashing dell'URL durante l'inserimento. E quindi, quando è necessario eseguire una query per questo valore, è possibile fare qualcosa del genere:

SELECT url FROM url_table WHERE url_hash=CRC32("http://gnu.org");

Il problema con l'esempio sopra è che poiché la CRC32funzione genera un hash piuttosto piccolo, si finiranno con molte collisioni nei valori con hash. Se hai bisogno di valori esatti, puoi risolvere questo problema procedendo come segue:

SELECT url FROM url_table 
WHERE url_hash=CRC32("http://gnu.org") AND url="http://gnu.org";

Vale comunque la pena eseguire l'hash delle cose anche se il numero di collisioni è elevato perché eseguirai solo il secondo confronto (quello della stringa) contro gli hash ripetuti.

Sfortunatamente, usando questa tecnica, devi ancora colpire la tabella per confrontare il urlcampo.

Incartare

Alcuni fatti che potresti prendere in considerazione ogni volta che vuoi parlare di ottimizzazione:

  1. Il confronto dei numeri interi è molto più veloce del confronto delle stringhe. Può essere illustrato con l'esempio sull'emulazione dell'indice hash in InnoDB.

  2. Forse, l'aggiunta di ulteriori passaggi in un processo rende più veloce, non più lento. Può essere illustrato dal fatto che è possibile ottimizzare a SELECTsuddividendolo in due passaggi, rendendo i primi valori di archivio in una tabella in memoria appena creata e quindi eseguendo le query più pesanti su questa seconda tabella.

MySQL ha anche altri indici, ma penso che B + Tree sia il più usato di sempre e l'hash sia una buona cosa da sapere, ma puoi trovare gli altri nella documentazione di MySQL .

Consiglio vivamente di leggere il libro "High Performance MySQL", la risposta sopra era decisamente basata sul suo capitolo sugli indici.


2
Le seguenti domande avranno dei vantaggi nel caso precedente? 1. SELECT last_name, first_name FROM person WHERE last_name= "Constantine" 2.SELECT last_name, first_name FROM person WHERE last_name LIKE "%Constantine"
Akshay Taru,

1
La prima domanda lo farà, la seconda query no. Usa EXPLAIN: dev.mysql.com/doc/refman/5.5/en/explain.html Per indicizzare la seconda query con MySQL, devi utilizzare FULLTEXT INDEX: dev.mysql.com/doc/refman/5.5/en/fulltext- search.html
Emilio Nicolás,

5
Ti ho votato perché eri a 127 e la risposta n. 1 a 256. Non potevo evitare di rendere tutto bello e pulito, per quanto riguarda i binari.
pbarney,

Questa è stata una nuova informazione per me "l'ordine di eseguire una query su questi campi è molto importante". Grazie.
Khatri,

1
@pbarney dopo tre anni sono rispettivamente vicino a 256 e 512, questo è ciò che chiamo un aumento binario!
nanocv,

43

Fondamentalmente un indice è una mappa di tutte le chiavi ordinate in ordine. Con un elenco in ordine, quindi invece di controllare ogni tasto, può fare qualcosa del genere:

1: vai al centro dell'elenco - è superiore o inferiore a quello che sto cercando?

2: Se più in alto, vai a metà tra il centro e il fondo, se in basso, al centro e in alto

3: è superiore o inferiore? Salta di nuovo al punto centrale, ecc.

Usando quella logica, puoi trovare un elemento in un elenco ordinato in circa 7 passaggi, invece di controllare ogni elemento.

Ovviamente ci sono delle complessità, ma questo ti dà l'idea di base.


29
Questa si chiama ricerca binaria.
ddlshack,

Grazie, finalmente una risposta che spiega perché è più veloce e non solo come funziona il db con gli indici.
Gershon Herczeg,

Il numero effettivo di passaggi dipende in larga misura dai dati: numero di valore e distribuzione univoci nell'intervallo. 7 è il massimo teorico per 100 valori. Discussione completa su come calcolare il numero di passaggi qui stackoverflow.com/questions/10571170/…
Joshua

L'indice MySQL più comune è un albero B + che funziona in modo simile a una ricerca binaria ma non è lo stesso. La complessità algoritmica è la stessa, ma non è così per le ricerche. Vedi en.wikipedia.org/wiki/B-tree
Matt

4

Dai un'occhiata a questo link: http://dev.mysql.com/doc/refman/5.0/en/mysql-indexes.html

Il modo in cui funzionano è troppo ampio per essere trattato in un post SO.

Ecco una delle migliori spiegazioni degli indici che ho visto. Purtroppo è per SQL Server e non MySQL. Non sono sicuro di quanto siano simili i due ...


2
Bell'articolo Non conosco SQL Server, ma i meccanismi di base sembrano molto simili. (metanote: la disabilitazione degli stili CSS nel secondo articolo collegato rivela il contenuto)
Piskvor ha lasciato l'edificio il

3

Guarda questi video per maggiori dettagli sull'indicizzazione

Indicizzazione semplice È possibile creare un indice univoco su una tabella. Un indice univoco significa che due righe non possono avere lo stesso valore di indice. Ecco la sintassi per creare un indice su una tabella

CREATE UNIQUE INDEX index_name
ON table_name ( column1, column2,...);

È possibile utilizzare una o più colonne per creare un indice. Ad esempio, possiamo creare un indice tutorials_tblusando tutorial_author.

CREATE UNIQUE INDEX AUTHOR_INDEX
ON tutorials_tbl (tutorial_author)

È possibile creare un semplice indice su una tabella. Ometti la parola chiave UNIQUE dalla query per creare un indice semplice. L'indice semplice consente valori duplicati in una tabella.

Se si desidera indicizzare i valori in una colonna in ordine decrescente, è possibile aggiungere la parola riservata DESC dopo il nome della colonna.

mysql> CREATE UNIQUE INDEX AUTHOR_INDEX
ON tutorials_tbl (tutorial_author DESC)

1
Benvenuto in Stack Overflow! Ho notato che tutte le tue risposte rimandano ai tuoi video. Si noti che l' autopromozione esplicita non è consentita .
SL Barth - Ripristina Monica il

Vuole promuovere i suoi video. LOL
Ilyas karim,

1

Voglio aggiungere i miei 2 centesimi. Sono ben lungi dall'essere un esperto di database, ma di recente ho letto un po 'su questo argomento; abbastanza per provare a dare un ELI5. Quindi, ecco la spiegazione del laico.


Capisco come tale che un indice sia come un mini-specchio del tuo tavolo, più o meno come un array associativo. Se lo inserisci con una chiave corrispondente, puoi semplicemente saltare a quella riga in un "comando".

Ma se non si dispone di quell'indice / array, l'interprete di query deve utilizzare un ciclo for per scorrere tutte le righe e verificare la corrispondenza (scansione della tabella completa).

Avere un indice ha il "rovescio della medaglia" dello spazio di archiviazione aggiuntivo (per quel mini-mirror), in cambio del "vantaggio" di cercare più velocemente i contenuti.

Si noti che (a seconda del proprio motore db) la creazione di chiavi primarie, esterne o uniche imposta automaticamente anche un rispettivo indice. Lo stesso principio è fondamentalmente perché e come funzionano quelle chiavi.


1

Aggiunta di una rappresentazione visiva all'elenco delle risposte. inserisci qui la descrizione dell'immagine

MySQL utilizza un ulteriore livello di riferimento indiretto: i record dell'indice secondario puntano ai record dell'indice primario e l'indice primario stesso contiene le posizioni delle righe su disco. Se un offset di riga cambia, è necessario aggiornare solo l'indice primario.

Avvertenza: la struttura dei dati del disco appare piatta nel diagramma ma in realtà è un albero B +.

Fonte: link


1

In MySQL InnoDB, ci sono due tipi di indice.

  1. Chiave primaria che si chiama indice cluster. Le parole chiave di indice sono memorizzate con dati di record reali nel nodo foglia B + Tree.

  2. Chiave secondaria che è indice non cluster. Questi indici memorizzano solo le parole chiave della chiave primaria insieme alle loro parole chiave dell'indice nel nodo foglia B + Tree. Pertanto, durante la ricerca dall'indice secondario, trova prima le parole chiave dell'indice della chiave primaria e scansiona la chiave primaria B + Tree per trovare i record di dati reali. Ciò renderà l'indice secondario più lento rispetto alla ricerca dell'indice primario. Tuttavia, se le selectcolonne sono tutte nell'indice secondario, non è necessario cercare nuovamente l'indice primario B + Albero. Questo si chiama indice di copertura.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.