Pourquoi utiliser MySQL serait intersection d'index au lieu de l'indice combiné?
-
13-10-2019 - |
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;
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.