2011-11-15 6 views
5

Perché Mysql optimizer sceglie l'indice secondario quando esegue un 'select * from lookup' senza una clausola order by.Perché MySQL Query Optimizer sceglie l'indice secondario sull'indice primario clusterizzato?

È solo un colpo di fortuna o si tratta di un'ottimizzazione dietro le quinte che presuppone dal momento che è stato aggiunto un indice secondario più importante della chiave primaria.

Mi aspetto che i risultati vengano ordinati tramite chiave primaria poiché una scansione di tutti i nodi foglia può fornire tutti i dati necessari per rispondere a questa query.

di riprodurre creo una semplice tabella coppia chiave/valore (nota non auto_increment)

create table lookup (
id int not null, 
primary key (id), 
name varchar(25), 
unique k_name (name) 
) engine=innodb; 

inserire alcuni dati in ordine non alfabetico casuale

insert into lookup values(1, "Zebra"),(2, "Aardvark"),(3, "Fish"),(4,"Dog"),(5,"Cat"),(6,"Mouse"); 

Query dei dati (questo è dove mi aspetto che i dati vengano restituiti in ordine di chiave primaria)

mysql> select * from lookup; 
+----+----------+ 
| id | name  | 
+----+----------+ 
| 2 | Aardvark | 
| 5 | Cat  | 
| 4 | Dog  | 
| 3 | Fish  | 
| 6 | Mouse | 
| 1 | Zebra | 
+----+----------+ 
6 rows in set (0.00 sec) 

Dove come non è - sembra che sia stata eseguita una scansione dei nodi foglia k_name. qui

mysql> explain select * from lookup; 
+----+-------------+--------+-------+---------------+--------+---------+------+------+-------------+ 
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra  | 
+----+-------------+--------+-------+---------------+--------+---------+------+------+-------------+ 
| 1 | SIMPLE  | lookup | index | NULL   | k_name | 28  | NULL | 6 | Using index | 
+----+-------------+--------+-------+---------------+--------+---------+------+------+-------------+ 
1 row in set (0.00 sec) 

Per me mostrato questo dice Mysql sta usando k_name come un indice di copertura per restituire i dati. Se si rilascia l'indice k_name, i dati vengono restituiti nell'ordine delle chiavi primarie. Se aggiungo un'altra colonna non indicizzata, i dati vengono restituiti nell'ordine delle chiavi primarie.

Alcune informazioni di base sulla mia configurazione.

mysql> show table status like 'lookup'\G 
*************************** 1. row *************************** 
      Name: lookup 
     Engine: InnoDB 
     Version: 10 
    Row_format: Compact 
      Rows: 6 
Avg_row_length: 2730 
    Data_length: 16384 
Max_data_length: 0 
    Index_length: 16384 
     Data_free: 0 
Auto_increment: NULL 
    Create_time: 2011-11-15 10:42:35 
    Update_time: NULL 
    Check_time: NULL 
     Collation: latin1_swedish_ci 
     Checksum: NULL 
Create_options: 
     Comment: 
1 row in set (0.00 sec) 

mysql> select version(); 
+------------+ 
| version() | 
+------------+ 
| 5.5.15-log | 
+------------+ 
1 row in set (0.00 sec) 

risposta

4

In realtà, l'indice cluster (noto anche come gen_clust_index) viene popolato in un ordine che non ha alcuna rima o ragione diversa dall'ordine rowid. è praticamente impossibile ordinare le rowid nell'ordine id.

In InnoDB, i record negli indici non cluster (detti anche indici secondari) contengono le colonne chiave primarie per la riga che non si trovano nell'indice secondario. InnoDB utilizza questo valore di chiave primaria per cercare la riga nell'indice cluster.

L'indice secondario regola l'ordine. Tuttavia, ogni voce di indice secondario ha una voce di chiave primaria nella riga corretta. Inoltre, pensa allo scenario indice di copertura che hai menzionato per k_name.

Ora, cerchiamo di cambiare marcia per un momento e discutere le la chiave primaria e k_name:

DOMANDA: chi ha più colonne richiesto dalla query originale, la chiave primaria o k_name?

ANSWER: k_name, perché contiene sia il nome sia l'id (ID interno perché è il PRIMARY KEY). L'indice di copertura k_name soddisfa la query meglio della chiave primaria.

Ora, se la query era SELECT * FROM ORDER BY id, il vostro SPIEGARE piano dovrebbe apparire come questo:

mysql> explain select * from lookup order by id; 
+----+-------------+--------+-------+---------------+---------+---------+------+------+-------+ 
| id | select_type | table | type | possible_keys | key  | key_len | ref | rows | Extra | 
+----+-------------+--------+-------+---------------+---------+---------+------+------+-------+ 
| 1 | SIMPLE  | lookup | index | NULL   | PRIMARY | 4  | NULL | 6 |  | 
+----+-------------+--------+-------+---------------+---------+---------+------+------+-------+ 

1 row in set (0.00 sec) 

senza ordine specfiying, Query Optimizer MySQL raccoglie l'indice che meglio soddisfa la query. Naturalmente, k_name ha il vantaggio ingiusto perché

  • ogni colonna della tabella viene indicizzata individualmente
  • ogni colonna della tabella è un k_name Candidate Key
  • NON È un indice secondario perché è un candidato Chiave proprio come la CHIAVE PRIMARIA.
  • indici cluster definiti dall'utente non possono avere l'ordine fila alterata una volta stabilito

Non è possibile manipolare l'ordine delle righe affatto. Ecco la prova che:

mysql> alter table lookup order by name; 
Query OK, 6 rows affected, 1 warning (0.23 sec) 
Records: 6 Duplicates: 0 Warnings: 1 

mysql> show warnings; 
+---------+------+-----------------------------------------------------------------------------------+ 
| Level | Code | Message                   | 
+---------+------+-----------------------------------------------------------------------------------+ 
| Warning | 1105 | ORDER BY ignored as there is a user-defined clustered index in the table 'lookup' | 
+---------+------+-----------------------------------------------------------------------------------+ 
1 row in set (0.00 sec) 

mysql> alter table lookup order by id; 
Query OK, 6 rows affected, 1 warning (0.19 sec) 
Records: 6 Duplicates: 0 Warnings: 1 

mysql> show warnings; 
+---------+------+-----------------------------------------------------------------------------------+ 
| Level | Code | Message                   | 
+---------+------+-----------------------------------------------------------------------------------+ 
| Warning | 1105 | ORDER BY ignored as there is a user-defined clustered index in the table 'lookup' | 
+---------+------+-----------------------------------------------------------------------------------+ 
1 row in set (0.00 sec) 
+2

k_name è un indice secondario. La definizione della chiave Candidate non ha nulla a che fare con gli indici. Anche una chiave primaria per definizione non ha nulla a che fare con gli indici. Tuttavia, la maggior parte dei DBMS crea automaticamente un indice per te quando definisci una chiave primaria. –

+0

k_name NON È UN INDICE SECONDARIO A TUTTI.Poiché si tratta di una chiave univoca, si comporta esattamente come fa una CHIAVE PRIMARIA. Le chiavi univoche sono, in realtà, indici cluster definiti dall'utente. È tanto un indice cluster quanto una PRIMARY KEY. Anche se non esiste una chiave primaria, un gen_clust_index basato su rowid viene generato internamente. La prova di ciò si trova in InnoDB. Puoi fare ORDER BY colonne chiave primaria contro MyISAM tutto il giorno. In InnoDB, non puoi né riordinare CHIAVI UNICHE né mitigare le query SELECT senza ORDER in favore di PRIMARY KEY rispetto ad altri CHIAVI UNICHE. Gli indici secondari non sono mai unici. – RolandoMySQLDBA

+1

L'indice cluster è quello della chiave PRIMARY. MySQL utilizza solo indici UNICI non nulli come indice cluster quando non esiste la chiave primaria. Inoltre, solo gli indici secondari come k_name includono anche dati di chiavi primarie per fare riferimento alle righe corrispondenti. Lo vedrai quando aggiungi un'altra colonna e k_name non è in grado di fornire tutti i dati come fa l'indice cluster. http://dev.mysql.com/doc/innodb/1.1/en/glossary.html#glos_secondary_index k_name diventa un indice cluster quando viene rimossa la chiave primaria. –

1

Bene sia indice è altrettanto efficiente in termini di ottenere i dati per quella query, quindi sto cercando di indovinare l'ottimizzatore appena lasciato con un "questo farà"

Aggiungere un altro indice univoco, potrebbe essere come sono tutti ugualmente efficienti, qualche routine "FindBestIndex" si abbandona con l'ultima che legge.

Non è il comportamento che mi sarei aspettato, anche se mi importava dell'ordine, aggiungevo un ordine per ID e loro permettevano all'ottimizzatore di scegliere la chiave primaria invece di passare due passaggi e fare un ordinamento.

1

È perché InnoDB indici secondari includono anche la colonna chiave primaria. Pertanto MySQL è in grado di recuperare tutti i dati rilevanti direttamente dall'indice secondario senza toccare le righe di dati e quindi sta salvando il disco IO.

Riferimenti:

+0

I collegamenti forniti meglio spiegano i concetti che dovrebbero essere noti prima di progettare la tabella e le query rispetto all'esempio grezzo nella mia risposta. +1 !!! – RolandoMySQLDBA

0

penso che non hai capito la colonna tipo. Digitare 'indice' colonna indica una scansione dell'indice completa. Quando questo è il caso e se la colonna 'extra' ha 'using index', significa che mysql può ottenere dall'indice tutti i dati richiesti per la query, e non è necessario ricorrere alle righe della tabella effettiva. Quindi qui il motore, invece di andare alle file (che di solito è costoso) ricorre all'uso dell'indice che ha tutti i dati richiesti dalla query. Gli indici secondari hanno la chiave primaria (id, nel tuo caso) come i dati. Cioè se cerchi una chiave nell'indice secondario, ottieni le chiavi primarie dei record della tabella. Dato che hai appena richiesto tutti i valori, è sufficiente scorrere l'indice secondario per ottenere ciò che ti serve.

Se il motore ha scelto di scorrere sulla chiave primaria, le chiavi primarie portano direttamente alle righe della tabella effettiva. Mysql cerca di evitare quel comportamento perché di solito è inefficiente. È inefficiente perché in genere le righe contengono più dati di quelli contenuti negli indici e potenzialmente è necessario eseguire più IO.

http://dev.mysql.com/doc/refman/5.0/en/explain-output.html