2011-11-15 12 views
5

¿Por qué el optimizador de Mysql elige el índice secundario al hacer un 'select * from lookup' sin cláusula order by.¿Por qué MySQL Query Optimizer elige el índice secundario sobre el índice primario agrupado?

¿Es solo una casualidad o esta es una optimización entre bastidores que se supone, ya que agregó un índice secundario es más importante que la clave principal.

Espero que los resultados se ordenen por clave principal ya que un escaneo de todos los nodos hoja puede proporcionar todos los datos necesarios para responder esta consulta.

para reproducir creo una sencilla tabla de clave/valor par (Nota no AUTO_INCREMENT)

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

insertar algunos datos en orden no-alfabético al azar

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

consultar los datos (aquí es donde Esperaría que los datos se devuelvan en orden de clave principal)

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) 

Donde como no lo es, parece que se ha realizado un escaneo de los nodos de la hoja k_name. Se muestra aquí

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) 

Para mí esto dice MySQL está usando k_name como un índice de cobertura para devolver los datos. Si elimino el índice k_name, los datos se devuelven en orden de clave primaria. Si agrego otra columna no indexada, los datos se devuelven en orden de clave primaria.

Alguna información básica sobre mi configuración.

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) 

Respuesta

4

En realidad, el índice agrupado (también conocido como gen_clust_index) se completa en un orden que no tiene ninguna rima o motivo que no sea el orden rowid. es virtualmente imposible ordenar los rowids en orden de identificación.

En InnoDB, los registros en índices no agrupados (también llamados índices secundarios) contienen las columnas de clave principal para la fila que no están en el índice secundario. InnoDB usa este valor de clave principal para buscar la fila en el índice agrupado.

El índice secundario rige el orden. Sin embargo, cada entrada de índice secundario tiene una entrada de clave primaria a la fila correcta. Además, piense en el escenario de índice de cobertura que mencionó para k_name.

Ahora, vamos a cambiar de marcha por un momento y debatir sobre la clave principal y k_name:

PREGUNTA: ¿De quién tiene más columnas solicitada por su búsqueda original, la clave principal o k_name?

RESPUESTA: k_name, porque tiene tanto el nombre como el ID (el ID es interno porque es la LLAVE PRINCIPAL). El índice de cubierta k_name cumple mejor la consulta que la clave principal.

Ahora bien, si la consulta fue SELECT * FROM ORDER BY id, su plan EXPLICAR debería tener este aspecto:

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) 

Sin specfiying fin, el optimizador de consultas MySQL recoge el índice que mejor cumple con la consulta. Por supuesto, k_name tiene la ventaja injusta porque

  • cada columna de la tabla está indexada por separado
  • cada columna de la tabla es un k_name Candidate Key
  • NO ES un índice secundario porque es un candidato Clave al igual que la CLAVE PRIMARIA.
  • índices agrupados definidos por el usuario no pueden tener el orden de las filas altera en vez establecida

No se puede manipular el orden de las filas en absoluto. Aquí hay una prueba de eso:

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 es un índice secundario. La definición de clave Candidato no tiene nada que ver con los índices. Incluso una clave principal por definición no tiene nada que ver con los índices. Sin embargo, la mayoría de los DBMS crean automáticamente un índice para usted cuando define una clave principal. –

+0

k_name NO ES UN ÍNDICE SECUNDARIO.Como es una clave única, se comporta exactamente como lo hace una PRIMARY KEY. Las claves únicas son, en realidad, índices agrupados definidos por el usuario. Es tanto un índice agrupado como una PRIMARY KEY. Incluso si no existe una clave principal, se genera internamente un gen_clust_index basado en rowid. La prueba de esto yace en InnoDB. Puede hacer ORDER BY columnas de clave primaria contra MyISAM durante todo el día. En InnoDB, no puede reordenar las TECLAS ÚNICAS ni mitigar las consultas SELECCIONAR SIN PEDIDOS a favor de la LLAVE PRIMARIA sobre otras LLAVES ÚNICAS. Los índices secundarios nunca son únicos. – RolandoMySQLDBA

+1

El índice agrupado es el de la tecla PRIMARIO. MySQL solo usa índices ÚNICOS no nulos como índice agrupado cuando NO hay ninguna clave principal. Además, solo los índices secundarios como k_name también incluyen datos de clave primaria para referenciar las filas correspondientes. Verá que cuando agrega otra columna y k_name no puede proporcionar todos los datos como lo hace el índice agrupado. http://dev.mysql.com/doc/innodb/1.1/en/glossary.html#glos_secondary_index k_name se convierte en un índice agrupado cuando se elimina la clave principal. –

1

Bien sea el índice es igual de eficiente en términos de conseguir los datos para esa consulta, así que supongo que el optimizador acaba de salir con un "esto va a hacer"

Agregar otro índice único, podría ser que todos son igual de eficientes, alguna rutina de "FindBestIndex" se cierra con la última que lee.

No es el comportamiento que esperaría, aunque si me preocupaba el pedido, agregaría un pedido por identificación y ellos dejarían que el optimizador elija la clave principal en lugar de pasar dos veces y hacer una clasificación.

1

Esto se debe a que los índices secundarios de InnoDB también incluyen la columna de la clave principal. Por lo tanto, MySQL puede obtener todos los datos relevantes directamente del índice secundario sin tocar las filas de datos y, por lo tanto, está guardando el disco IO.

Referencias:

+0

Los enlaces que suministró explican mejor los conceptos que deben conocerse antes de diseñar la tabla y las consultas que el ejemplo sin procesar en mi respuesta. +1 !!! – RolandoMySQLDBA

0

creo que no entendía la columna de tipo. Tipo columna 'índice' significa un análisis de índice completo. Cuando este es el caso y si la columna 'extra' tiene 'usar índice', significa que mysql puede obtener todos los datos requeridos para la consulta desde el índice, y no necesita recurrir a las filas reales de la tabla. Así que aquí el motor, en lugar de ir a las filas (lo que es costoso por lo general) recurre al índice que contiene todos los datos requeridos por la consulta. Los índices secundarios tienen la clave principal (id, en su caso) como datos. Es decir, si busca una clave en el índice secundario, obtiene las claves principales de los registros de la tabla. Como acaba de solicitar todos los valores, basta con recorrer el índice secundario para obtener lo que necesita.

Si el motor eligió iterar sobre la clave principal, las claves principales conducen directamente a las filas reales de la tabla. Mysql intenta evitar ese comportamiento porque generalmente es ineficiente. Es ineficiente porque generalmente las filas contienen más datos que los contenidos en los índices y es posible que tenga que hacer más IO.

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

Cuestiones relacionadas