Question

De temps en temps, je rencontre un comportement étrange MySQL. Supposons que je index (type, rel, créé), (type), (rel). Le meilleur choix pour une requête comme celle-ci:

SELECT id FROM tbl
WHERE rel = 3 AND type = 3
ORDER BY created;

serait d'indice d'utilisation (type, rel, created). Mais MySQL décide de Intersection des index (type) et (rel), et qui mène à la pire perfomance. Voici un exemple:

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

Et la même requête, mais avec un soupçon ajouté:

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

Je pense que MySQL prend un plan d'exécution qui contient moins de nombre dans la colonne « lignes » de la commande EXPLAIN. De ce point de vue, intersection d'index avec 4343 lignes ressemble vraiment mieux que d'utiliser mon index combiné avec 8906 lignes. Donc, le problème est peut-être dans ces chiffres?

mysql> SELECT COUNT(*) FROM tbl WHERE type=3 AND rel=3;
+----------+
| COUNT(*) |
+----------+
|     3056 |
+----------+

De cela, je peux conclure que MySQL est erronée au calcul le nombre approximatif de lignes pour l'indice combiné.

Alors, qu'est-ce que je peux faire ici pour faire MySQL prendre le bon plan d'exécution?

Je ne peux pas utiliser des notes optimiseur, parce que je dois en tenir à Django ORM La seule solution que je trouve encore est de supprimer les index d'un champ.

MySQL version est 1.5.49.

La structure de la table est le suivant:

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;
Était-ce utile?

La solution

Il est difficile de dire exactement pourquoi MySQL choisit index_merge_intersection sur l'analyse d'index, mais il faut noter que les indices composites, les statistiques jusqu'à la colonne donnée sont stockés pour les indices composites.

La valeur de information_schema.statistics.cardinality pour la type colonne de l'indice composite montrera la cardinalité de (rel, type), non type lui-même.

S'il existe une corrélation entre rel et type, puis cardinalité de (rel, type) sera moins produit de cardinalités de rel et type pris séparément des index sur les colonnes correspondantes.

C'est la raison pour laquelle le nombre de lignes est calculé de façon incorrecte (une intersection ne peut pas être plus grande taille que l'union).

Vous pouvez interdire index_merge_intersection en la mettant hors service en @@optimizer_switch:

SET optimizer_switch = 'index_merge_intersection=off'

Autres conseils

Une autre chose est à noter: vous n'auriez pas le problème si vous avez supprimé l'index du type uniquement. l'indice est pas nécessaire, car elle reproduit une partie de l'indice composite.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top