Je dirige un EXPLAIN
:
mysql> explain select last_name from employees order by last_name;
+----+-------------+-----------+------+---------------+------+---------+------+-------+----------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-----------+------+---------------+------+---------+------+-------+----------------+
| 1 | SIMPLE | employees | ALL | NULL | NULL | NULL | NULL | 10031 | Using filesort |
+----+-------------+-----------+------+---------------+------+---------+------+-------+----------------+
1 row in set (0.00 sec)
Les index de ma table:
mysql> show index from employees;
+-----------+------------+---------------+--------------+---------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-----------+------------+---------------+--------------+---------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| employees | 0 | PRIMARY | 1 | subsidiary_id | A | 6 | NULL | NULL | | BTREE | | |
| employees | 0 | PRIMARY | 2 | employee_id | A | 10031 | NULL | NULL | | BTREE | | |
| employees | 1 | idx_last_name | 1 | last_name | A | 10031 | 700 | NULL | | BTREE | | |
| employees | 1 | date_of_birth | 1 | date_of_birth | A | 10031 | NULL | NULL | YES | BTREE | | |
| employees | 1 | date_of_birth | 2 | subsidiary_id | A | 10031 | NULL | NULL | | BTREE | | |
+-----------+------------+---------------+--------------+---------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
5 rows in set (0.02 sec)
Il existe un index sur last_name mais l'optimiseur ne l'utilise pas.
Moi aussi:
mysql> explain select last_name from employees force index(idx_last_name) order by last_name;
+----+-------------+-----------+------+---------------+------+---------+------+-------+----------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-----------+------+---------------+------+---------+------+-------+----------------+
| 1 | SIMPLE | employees | ALL | NULL | NULL | NULL | NULL | 10031 | Using filesort |
+----+-------------+-----------+------+---------------+------+---------+------+-------+----------------+
1 row in set (0.00 sec)
Mais encore l'indice est pas utilisé! Qu'est-ce que je fais mal ici?
Cela a-t-il à voir avec le fait que l'indice est NON_UNIQUE
? BTW le nom estVARCHAR(1000)
Mise à jour demandée par @RolandoMySQLDBA
mysql> SELECT COUNT(DISTINCT last_name) DistinctCount FROM employees;
+---------------+
| DistinctCount |
+---------------+
| 10000 |
+---------------+
1 row in set (0.05 sec)
mysql> SELECT COUNT(1) FROM (SELECT COUNT(1) Count500,last_name FROM employees GROUP BY last_name HAVING COUNT(1) > 500) A;
+----------+
| COUNT(1) |
+----------+
| 0 |
+----------+
1 row in set (0.15 sec)
SELECT COUNT(DISTINCT last_name) DistinctCount FROM employees;
2)SELECT COUNT(1) FROM (SELECT COUNT(1) Count500,last_name FROM employees GROUP BY last_name HAVING COUNT(1) > 500) A;
. Quel est le résultat de chaque comptage?SELECT COUNT(1) FullTableCount FROM employees;
et 2)SELECT * FROM (SELECT COUNT(1) Count500,last_name FROM employees GROUP BY last_name HAVING COUNT(1) > 500) A LIMIT 10;
.Réponses:
PROBLÈME # 1
Regardez la requête
Je ne vois pas de clause WHERE significative, pas plus que MySQL Query Optimizer. Il n'y a aucune incitation à utiliser un indice.
PROBLÈME # 2
Regardez la requête
Vous lui avez donné un index, mais le Query Opitmizer a pris le relais. J'ai déjà vu ce comportement ( Comment puis-je forcer un JOIN à utiliser un index spécifique dans MySQL? )
Pourquoi cela devrait-il arriver?
Sans
WHERE
clause, Query Optimizer se dit ce qui suit:WHERE
clause?L'optimiseur de requête a choisi le chemin de moindre résistance.
Vous allez être sous le choc, mais ça y est: saviez-vous que l'Optimiseur de requête traitera MyISAM de manière très différente?
Vous dites probablement HUH ???? COMMENT ????
MyISAM stocke les données dans un
.MYD
fichier et tous les index dans le.MYI
fichier.La même requête produira un plan EXPLAIN différent car l'index réside dans un fichier différent des données. Pourquoi ? Voici pourquoi:
last_name
colonne) sont déjà commandées dans le.MYI
last_name
partir de l'indexComment en être si sûr? J'ai testé cette théorie de travail sur la façon dont l'utilisation d'un stockage différent générera un plan EXPLAIN différent (parfois un meilleur): un index doit-il couvrir toutes les colonnes sélectionnées pour qu'il puisse être utilisé pour ORDER BY?
la source
En fait, le problème ici est que cela ressemble à un index de préfixe. Je ne vois pas la définition du tableau dans la question, mais
sub_part
= 700? Vous n'avez pas indexé la colonne entière, donc l'index ne peut pas être utilisé pour le tri et n'est pas utile non plus comme index de couverture. Il ne pouvait être utilisé que pour trouver les lignes qui "pouvaient" correspondre à aWHERE
et la couche serveur (au-dessus du moteur de stockage) devrait filtrer davantage les lignes correspondantes. Avez-vous vraiment besoin de 1000 caractères pour un nom de famille?mise à jour pour illustrer: J'ai une table de test de table avec un peu plus de 500 lignes, chacune avec le nom de domaine d'un site Web dans une colonne
domain_name VARCHAR(254) NOT NULL
et sans index.Avec la colonne complète indexée, la requête utilise l'index:
Donc, maintenant, je vais supprimer cet index et simplement indexer les 200 premiers caractères de nom_domaine.
Voila.
Notez également que l'index, à 200 caractères, est plus long que la valeur la plus longue de la colonne ...
... mais cela ne fait aucune différence. Un index déclaré avec une longueur de préfixe ne peut être utilisé que pour les recherches, pas pour le tri et pas comme index de couverture, car il ne contient pas la valeur de colonne complète, par définition.
En outre, les requêtes ci-dessus ont été exécutées sur une table InnoDB, mais leur exécution sur une table MyISAM donne des résultats pratiquement identiques. La seule différence dans ce cas est que le nombre d'InnoDB pour
rows
est légèrement désactivé (541) tandis que MyISAM affiche le nombre exact de lignes (563), ce qui est un comportement normal car les deux moteurs de stockage gèrent les plongées d'index très différemment.J'affirmerais toujours que la colonne last_name est probablement plus grande que nécessaire, mais il est toujours possible d'indexer la colonne entière, si vous utilisez InnoDB et exécutez MySQL 5.5 ou 5.6:
la source
varchar(1000)
mais cela dépasse le maximum autorisé pour l' indice qui est ~ 750EXPLAIN SELECT ...
, ainsi queSHOW CREATE TABLE ...
etSELECT @@VERSION;
puisque les modifications apportées à l'optimiseur entre les versions peuvent être pertinentes.J'ai fait une réponse car un commentaire ne prend pas en charge le formatage et RolandoMySQL DBA a parlé de gen_clust_index et innodb. Et cela est très important sur une table basée sur innodb. Cela va plus loin que la connaissance DBA normale, car vous devez être en mesure d'analyser le code C.
Vous devez TOUJOURS TOUJOURS créer une CLÉ PRIMAIRE ou une CLÉ UNIQUE si vous utilisez Innodb. Si vous ne le faites pas, Innodb utilisera son propre ROW_ID généré qui pourrait vous faire plus de mal que de bien.
Je vais essayer de l'expliquer facilement car la preuve est basée sur le code C.
Premier problème
mutex_enter (& (dict_sys-> mutex));
Cette ligne garantit qu'un seul thread peut accéder à dict_sys-> mutex en même temps. Et si la valeur était déjà mutée ... oui un thread doit attendre donc vous obtenez quelque chose comme une fonctionnalité aléatoire sympa comme le verrouillage de thread ou si vous avez plus de tables sans votre propre CLÉ PRIMAIRE ou CLÉ UNIQUE alors vous auriez une fonctionnalité intéressante avec Innodb ' verrouillage de table ' n'est-ce pas la raison pour laquelle MyISAM a été remplacé par InnoDB parce que cette fonctionnalité intéressante appelée verrouillage basé sur les enregistrements / lignes ..
Deuxième problème
(0 == (id% DICT_HDR_ROW_ID_WRITE_MARGIN))
les calculs modulo (%) sont lents, pas bons si vous insérez par lot car il doit être recalculé à chaque fois ..., et parce que DICT_HDR_ROW_ID_WRITE_MARGIN (valeur 256) est une puissance de deux, cela pourrait être fait beaucoup plus rapidement ..
(0 == (id & (DICT_HDR_ROW_ID_WRITE_MARGIN - 1))))
Note latérale si le compilateur C a été configuré pour optimiser et que c'est un bon optimiseur, l'optimiseur C corrigera le code "lourd" à la version plus légère
devise de l'histoire toujours créer votre propre CLÉ PRIMAIRE ou assurez-vous d'avoir un index UNIQUE lorsque vous créez une table depuis le début
la source
UNIQUE
soit suffisant - il doit également inclure uniquement des colonnes non NULL pour que l'index unique soit promu en PK.INSERT
consacré à cette fonction. Je soupçonne que c'est insignifiant. Contrastez l'effort de pelleter les colonnes, effectuez des opérations BTree, y compris un fractionnement de bloc occasionnel, divers mutex sur le pool de tampons, des éléments de tampon de changement, etc.