¿Por qué utilizar MySQL índice de cruce en lugar del índice combinado?
-
13-10-2019 - |
Pregunta
De vez en cuando me encuentro con un comportamiento extraño MySQL. Vamos a suponer que tengo índices (tipo, rel, creada), (Tipo), (REL). La mejor opción para una consulta como la siguiente:
SELECT id FROM tbl
WHERE rel = 3 AND type = 3
ORDER BY created;
sería índice de utilización (type, rel, created)
.
Pero MySQL decide índices se cruzan (type)
y (rel)
, y que conduce a la peor perfomance de. He aquí un ejemplo:
mysql> EXPLAIN
-> SELECT id FROM tbl
-> WHERE rel = 3 AND type = 3
-> ORDER BY created\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: tbl
type: index_merge
possible_keys: idx_type,idx_rel,idx_rel_type_created
key: idx_type,idx_rel
key_len: 1,2
ref: NULL
rows: 4343
Extra: Using intersect(idx_type,idx_rel); Using where; Using filesort
Y la misma consulta, pero con un toque añadido:
mysql> EXPLAIN
-> SELECT id FROM tbl USE INDEX (idx_type_rel_created)
-> WHERE rel = 3 AND type = 3
-> ORDER BY created\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: tbl
type: ref
possible_keys: idx_type_rel_created
key: idx_type_rel_created
key_len: 3
ref: const,const
rows: 8906
Extra: Using where
Creo que MySQL tiene un plan de ejecución que contiene menos cantidad en la columna "filas" de la EXPLICAR comando. Desde ese punto de vista, la intersección de índice con 4343 filas se ve realmente mejor que usar mi índice combinado con 8906 filas. Por lo tanto, tal vez el problema está dentro de esos números?
mysql> SELECT COUNT(*) FROM tbl WHERE type=3 AND rel=3;
+----------+
| COUNT(*) |
+----------+
| 3056 |
+----------+
A partir de este puedo concluir que MySQL se equivoca en el cálculo de número aproximado de filas de índice combinado.
Así que, ¿qué puedo hacer aquí para hacer MySQL aceptan el plan de ejecución correcto?
No se puede utilizar el optimizador consejos, porque tengo que atenerse a Django ORM La única solución que todavía encontrado es para eliminar esos índices de un campo.
versión de MySQL es 5.1.49.
La estructura de la tabla es:
CREATE TABLE tbl (
`id` int(11) NOT NULL AUTO_INCREMENT,
`type` tinyint(1) NOT NULL,
`rel` smallint(2) NOT NULL,
`created` datetime NOT NULL,
PRIMARY KEY (`id`),
KEY `idx_type` (`type`),
KEY `idx_rel` (`rel`),
KEY `idx_type_rel_created` (`type`,`rel`,`created`)
) ENGINE=MyISAM;
Solución
Es difícil decir exactamente por qué elige MySQL
index_merge_intersection
sobre el recorrido de índice, pero se debe tener en cuenta que con los índices compuestos, las estadísticas hasta la columna dada se almacenan los índices compuestos.
El valor de information_schema.statistics.cardinality
para la type
columna del índice compuesto mostrará la cardinalidad de (rel, type)
, no type
sí mismo.
Si hay una correlación entre rel
y type
, entonces cardinalidad de (rel, type)
será menor que el producto de cardinalidades de rel
y type
tomado por separado de los índices en las columnas correspondientes.
Es por eso que el número de filas se calcula de forma incorrecta (una intersección no puede ser más grande en tamaño que un sindicato).
Se puede prohibir index_merge_intersection
estableciéndolo en off en @@optimizer_switch
:
SET optimizer_switch = 'index_merge_intersection=off'
Otros consejos
Otra cosa vale la pena mencionar: usted no tendría el problema si eliminó el índice del tipo solamente. no se requiere el índice desde reproduzca una parte del índice compuesto.