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;
¿Fue útil?

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.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top