2010-05-05 3 views
3

J'ai téléchargé le employees database et j'ai exécuté certaines requêtes à des fins d'analyse comparative.
Ensuite, j'ai remarqué qu'une requête n'utilisait pas d'index de couverture, bien qu'il y ait un index correspondant que j'ai créé précédemment. Seulement quand j'ai ajouté une clause FORCE INDEX à la requête, il a utilisé un indice de couverture.
J'ai téléchargé deux fichiers, l'un est the executed SQL queries et l'autre est the results.
Pouvez-vous indiquer pourquoi la requête utilise un indice de couverture uniquement lorsqu'une clause FORCE INDEX est ajoutée? L'EXPLAIN montre que dans les deux cas, l'index dept_no_from_date_idx est utilisé de toute façon.La requête n'utilise pas d'index de couverture le cas échéant

Pour me adapter aux normes de SO, je suis également écrit le contenu des deux fichiers ici:

Les requêtes SQL:

USE employees; 

/* Creating an index for an index-covered query */ 
    CREATE INDEX dept_no_from_date_idx ON dept_emp (dept_no, from_date); 

/* Show `dept_emp` table structure, indexes and generic data */ 
    SHOW TABLE STATUS LIKE "dept_emp"; 
    DESCRIBE dept_emp; 
    SHOW KEYS IN dept_emp; 

/* The EXPLAIN shows that the subquery doesn't use a covering-index */ 
EXPLAIN SELECT SQL_NO_CACHE * FROM dept_emp INNER JOIN (
     /* The subquery should use a covering index, but isn't */ 
     SELECT SQL_NO_CACHE emp_no, dept_no FROM dept_emp WHERE dept_no="d001" ORDER BY from_date DESC LIMIT 20000,50 
    ) AS `der` USING (`emp_no`, `dept_no`); 

/* The EXPLAIN shows that the subquery DOES use a covering-index, 
     thanks to the FORCE INDEX clause */ 
EXPLAIN SELECT SQL_NO_CACHE * FROM dept_emp INNER JOIN (
     /* The subquery use a covering index */ 
     SELECT SQL_NO_CACHE emp_no, dept_no FROM dept_emp FORCE INDEX(dept_no_from_date_idx) WHERE dept_no="d001" ORDER BY from_date DESC LIMIT 20000,50 
    ) AS `der` USING (`emp_no`, `dept_no`); 

Les résultats:

-------------- 
/* Creating an index for an index-covered query */ 
    CREATE INDEX dept_no_from_date_idx ON dept_emp (dept_no, from_date) 
-------------- 

Query OK, 331603 rows affected (33.95 sec) 
Records: 331603 Duplicates: 0 Warnings: 0 

-------------- 
/* Show `dept_emp` table structure, indexes and generic data */ 
    SHOW TABLE STATUS LIKE "dept_emp" 
-------------- 

+----------+--------+---------+------------+--------+----------------+-------------+-----------------+--------------+-----------+----------------+---------------------+-------------+------------+-----------------+----------+----------------+---------+ 
| Name  | Engine | Version | Row_format | Rows | Avg_row_length | Data_length | Max_data_length | Index_length | Data_free | Auto_increment | Create_time   | Update_time | Check_time | Collation  | Checksum | Create_options | Comment | 
+----------+--------+---------+------------+--------+----------------+-------------+-----------------+--------------+-----------+----------------+---------------------+-------------+------------+-----------------+----------+----------------+---------+ 
| dept_emp | InnoDB |  10 | Compact | 331883 |    36 | 12075008 |    0 |  21544960 | 29360128 |   NULL | 2010-05-04 13:07:49 | NULL  | NULL  | utf8_general_ci |  NULL |    |   | 
+----------+--------+---------+------------+--------+----------------+-------------+-----------------+--------------+-----------+----------------+---------------------+-------------+------------+-----------------+----------+----------------+---------+ 
1 row in set (0.47 sec) 

-------------- 
    DESCRIBE dept_emp 
-------------- 

+-----------+---------+------+-----+---------+-------+ 
| Field  | Type | Null | Key | Default | Extra | 
+-----------+---------+------+-----+---------+-------+ 
| emp_no | int(11) | NO | PRI | NULL |  | 
| dept_no | char(4) | NO | PRI | NULL |  | 
| from_date | date | NO |  | NULL |  | 
| to_date | date | NO |  | NULL |  | 
+-----------+---------+------+-----+---------+-------+ 
4 rows in set (0.05 sec) 

-------------- 
    SHOW KEYS IN dept_emp 
-------------- 

+----------+------------+-----------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+ 
| Table | Non_unique | Key_name    | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | 
+----------+------------+-----------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+ 
| dept_emp |   0 | PRIMARY    |   1 | emp_no  | A   |  331883 |  NULL | NULL |  | BTREE  |   | 
| dept_emp |   0 | PRIMARY    |   2 | dept_no  | A   |  331883 |  NULL | NULL |  | BTREE  |   | 
| dept_emp |   1 | emp_no    |   1 | emp_no  | A   |  331883 |  NULL | NULL |  | BTREE  |   | 
| dept_emp |   1 | dept_no    |   1 | dept_no  | A   |   7 |  NULL | NULL |  | BTREE  |   | 
| dept_emp |   1 | dept_no_from_date_idx |   1 | dept_no  | A   |   13 |  NULL | NULL |  | BTREE  |   | 
| dept_emp |   1 | dept_no_from_date_idx |   2 | from_date | A   |  165941 |  NULL | NULL |  | BTREE  |   | 
+----------+------------+-----------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+ 
6 rows in set (0.23 sec) 

-------------- 
/* The EXPLAIN shows that the subquery doesn't use a covering-index */ 
EXPLAIN SELECT SQL_NO_CACHE * FROM dept_emp INNER JOIN (
     /* The subquery should use a covering index, but isn't */ 
     SELECT SQL_NO_CACHE emp_no, dept_no FROM dept_emp WHERE dept_no="d001" ORDER BY from_date DESC LIMIT 20000,50 
    ) AS `der` USING (`emp_no`, `dept_no`) 
-------------- 

+----+-------------+------------+--------+----------------------------------------------+-----------------------+---------+------------------------+-------+-------------+ 
| id | select_type | table  | type | possible_keys        | key     | key_len | ref     | rows | Extra  | 
+----+-------------+------------+--------+----------------------------------------------+-----------------------+---------+------------------------+-------+-------------+ 
| 1 | PRIMARY  | <derived2> | ALL | NULL           | NULL     | NULL | NULL     | 50 |    | 
| 1 | PRIMARY  | dept_emp | eq_ref | PRIMARY,emp_no,dept_no,dept_no_from_date_idx | PRIMARY    | 16  | der.emp_no,der.dept_no |  1 |    | 
| 2 | DERIVED  | dept_emp | ref | dept_no,dept_no_from_date_idx    | dept_no_from_date_idx | 12  |      | 21402 | Using where | 
+----+-------------+------------+--------+----------------------------------------------+-----------------------+---------+------------------------+-------+-------------+ 
3 rows in set (0.09 sec) 

-------------- 
/* The EXPLAIN shows that the subquery DOES use a covering-index, 
     thanks to the FORCE INDEX clause */ 
EXPLAIN SELECT SQL_NO_CACHE * FROM dept_emp INNER JOIN (
     /* The subquery use a covering index */ 
     SELECT SQL_NO_CACHE emp_no, dept_no FROM dept_emp FORCE INDEX(dept_no_from_date_idx) WHERE dept_no="d001" ORDER BY from_date DESC LIMIT 20000,50 
    ) AS `der` USING (`emp_no`, `dept_no`) 
-------------- 

+----+-------------+------------+--------+----------------------------------------------+-----------------------+---------+------------------------+-------+--------------------------+ 
| id | select_type | table  | type | possible_keys        | key     | key_len | ref     | rows | Extra     | 
+----+-------------+------------+--------+----------------------------------------------+-----------------------+---------+------------------------+-------+--------------------------+ 
| 1 | PRIMARY  | <derived2> | ALL | NULL           | NULL     | NULL | NULL     | 50 |       | 
| 1 | PRIMARY  | dept_emp | eq_ref | PRIMARY,emp_no,dept_no,dept_no_from_date_idx | PRIMARY    | 16  | der.emp_no,der.dept_no |  1 |       | 
| 2 | DERIVED  | dept_emp | ref | dept_no_from_date_idx      | dept_no_from_date_idx | 12  |      | 37468 | Using where; Using index | 
+----+-------------+------------+--------+----------------------------------------------+-----------------------+---------+------------------------+-------+--------------------------+ 
3 rows in set (0.05 sec) 

Bye 

Edit:
Je remarque qu'il ya un certain différences importantes de vitesse d'exécution entre ce deux dernières requêtes, les résultats sont placés avant:

SELECT SQL_NO_CACHE * FROM dept_emp INNER JOIN (
    SELECT SQL_NO_CACHE emp_no, dept_no FROM dept_emp WHERE dept_no="d001" ORDER BY from_date DESC LIMIT 20000,50 
) AS `der` USING (`emp_no`, `dept_no`) 
-------------- 

+--------+---------+------------+------------+ 
| emp_no | dept_no | from_date | to_date | 
+--------+---------+------------+------------+ 
| 38552 | d001 | 1985-04-16 | 2000-10-20 | 
      ... omitted ... 
| 98045 | d001 | 1985-03-28 | 9999-01-01 | 
+--------+---------+------------+------------+ 
50 rows in set (0.31 sec) 

-------------- 
SELECT SQL_NO_CACHE * FROM dept_emp INNER JOIN (
    SELECT SQL_NO_CACHE emp_no, dept_no FROM dept_emp FORCE INDEX(dept_no_from_date_idx) WHERE dept_no="d001" ORDER BY from_date DESC LIMIT 20000,50 
) AS `der` USING (`emp_no`, `dept_no`) 
-------------- 

+--------+---------+------------+------------+ 
| emp_no | dept_no | from_date | to_date | 
+--------+---------+------------+------------+ 
| 38552 | d001 | 1985-04-16 | 2000-10-20 | 
      ... omitted ... 
| 98045 | d001 | 1985-03-28 | 9999-01-01 | 
+--------+---------+------------+------------+ 
50 rows in set (0.06 sec) 

MAIS, si je change l'ordre d'exécution (faire le dernier requête à exécuter d'abord, et la première requête à exécuter dernier), la vitesse d'exécution est le même:

-------------- 
SELECT SQL_NO_CACHE * FROM dept_emp INNER JOIN (
    SELECT SQL_NO_CACHE emp_no, dept_no FROM dept_emp FORCE INDEX(dept_no_from_date_idx) WHERE dept_no="d001" ORDER BY from_date DESC LIMIT 20000,50 
) AS `der` USING (`emp_no`, `dept_no`) 
-------------- 

+--------+---------+------------+------------+ 
| emp_no | dept_no | from_date | to_date | 
+--------+---------+------------+------------+ 
| 38552 | d001 | 1985-04-16 | 2000-10-20 | 
      ... omitted ... 
| 98045 | d001 | 1985-03-28 | 9999-01-01 | 
+--------+---------+------------+------------+ 
50 rows in set (0.08 sec) 

-------------- 
SELECT SQL_NO_CACHE * FROM dept_emp INNER JOIN (
    SELECT SQL_NO_CACHE emp_no, dept_no FROM dept_emp WHERE dept_no="d001" ORDER BY from_date DESC LIMIT 20000,50 
) AS `der` USING (`emp_no`, `dept_no`) 
-------------- 

+--------+---------+------------+------------+ 
| emp_no | dept_no | from_date | to_date | 
+--------+---------+------------+------------+ 
| 38552 | d001 | 1985-04-16 | 2000-10-20 | 
      ... omitted ... 
| 98045 | d001 | 1985-03-28 | 9999-01-01 | 
+--------+---------+------------+------------+ 
50 rows in set (0.08 sec) 

Il ne peut être que la deuxième requête est prise à partir du cache, parce que SQL_NO_CACHE est écrit dans les deux requêtes. Alors pourquoi dans le premier exemple la première requête a duré 0.31 sec et la seconde 0.06 sec, mais dans le second exemple, les deux requêtes prennent 0.08 sec?

Edit2:
Je pense que les différences de vitesse d'exécution dérive du cache du système d'exploitation et peut-être d'autres facteurs. Lors de l'exécution répétée des 2 requêtes ci-dessus, les différences de temps d'exécution deviennent négligeables. Je les 2 requêtes exécuté ci-dessus pour 3 fois à plusieurs reprises et a obtenu les résultats suivants:

#1: 0.08 sec 
#2: 0.03 sec 
#1: 0.05 sec 
#2: 0.05 sec 
#1: 0.03 sec 
#2: 0.05 sec 
+0

Rapport de bogue soumis: http://bugs.mysql.com/bug.php?id=53442 – Dor

Répondre

2

En fait, vos deux requêtes n'utilisez l'indice de couverture.

Votre définition de l'index ne comprend pas emp_no, donc dans MyISAM, Using index serait impossible même avec la clause FORCE INDEX.

Cependant, les tables InnoDB sont regroupées et chaque index contient implicitement le PRIMARY KEY en tant que pointeur d'enregistrement.

Cela signifie que votre index est en fait un index sur (dept_no, from_date, emp_no, dept_no) et contient donc tous les champs requis.

EXPLAIN PLAN ne reflète pas toujours cela correctement, mais le moteur InnoDB ne gère pas cela.

Vous pouvez le vérifier en comparant les performances de ces deux requêtes:

SELECT SQL_NO_CACHE * FROM dept_emp INNER JOIN (
     /* The subquery use a covering index */ 
     SELECT SQL_NO_CACHE from_date, emp_no, dept_no FROM dept_emp WHERE dept_no="d001" ORDER BY from_date DESC LIMIT 20000,50 
    ) AS `der` USING (`emp_no`, `dept_no`); 

et

SELECT SQL_NO_CACHE * FROM dept_emp INNER JOIN (
     /* The subquery use a covering index */ 
     SELECT SQL_NO_CACHE to_date, emp_no, dept_no FROM dept_emp WHERE dept_no="d001" ORDER BY from_date DESC LIMIT 20000,50 
    ) AS `der` USING (`emp_no`, `dept_no`); 

Vous verrez que, malgré le fait que les plans seront présentés comme identiques, la seconde requête prendra beaucoup plus de temps (exactement parce que to_date n'est pas couvert).

C'est un bug dans EXPLAIN PLAN, pas dans InnoDB moteur.

+0

En effet, il y avait une différence majeure: la première requête prenait 0,25 sec, la seconde prenait 2,17 sec. Je connais la structure d'InnoDB, etc. Je cherche le rapport de bug sur mysql.com mais je ne le trouve pas. Savez-vous où est le rapport? Merci :) – Dor

+0

@dor: http://bugs.mysql.com/report.php – Quassnoi

+0

Vous voulez dire que ** I ** devrait signaler ce bug? Jusqu'à présent, personne n'a remarqué? Neat = D – Dor

Questions connexes