2010-05-05 63 views
3

我已经下载了employees database并执行了一些查询以进行基准测试。
然后我注意到有一个查询没有使用覆盖索引,尽管我之前创建了相应的索引。只有当我在查询中添加了FORCE INDEX子句时,才使用覆盖索引
我上传了两个文件,一个是the executed SQL queries,另一个是the results
你能说出为什么查询仅在添加FORCE INDEX子句时才使用覆盖索引吗? EXPLAIN显示,在这两种情况下,无论如何都使用dept_no_from_date_idx索引。查询在适用时不使用覆盖索引

去适应,这样的标准,我也在这里写这两个文件的内容:

的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`); 

结果:

-------------- 
/* 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 

编辑:
我注意到,有相当的是最后两个查询之间的显著执行速度的差异,结果被放在你面前:

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) 

但是,如果我改变执行的顺序(使最后查询首先被执行,并且待最后执行的第一个查询),则执行速度是相同的:

-------------- 
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) 

它不能被该第二查询正在采取从高速缓存中,因为SQL_NO_CACHE写在两个查询中。那么为什么在第一个例子中,第一个查询需要0.31秒和第二个0.06秒,但在第二个例子中,两个查询都需要0.08秒?

EDIT2:
我认为,执行速度的差异从操作系统的缓存,也许还有其他因素得出。重复执行上述2个查询时,执行时间差异变得可以忽略不计。我重复执行了上述2个查询3次,得到如下结果:

#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

错误报告中提出:http://bugs.mysql.com/bug.php?id=53442 – Dor 2010-05-05 16:15:32

回答

2

实际上,你的查询都使用覆盖索引。

您的索引定义不包括emp_no,所以在MyISAM,Using index即使使用FORCE INDEX条款也是不可能的。

但是,InnoDB表被聚集在一起,并且每个索引隐含地包含作为记录指针的PRIMARY KEY

这意味着您的索引实际上是(dept_no, from_date, emp_no, dept_no)上的索引,因此包含所有需要的字段。

EXPLAIN PLAN并不总是正确地反映这一点,但InnoDB引擎确实应对这一点。

您可以通过比较这两个查询的性能检查:

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`); 

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`); 

你会看到,尽管该计划将显示为相同的事实,第二个查询将花费更多时间(完全因为to_date未被涵盖)。

这是在EXPLAIN PLAN的错误,而不是在InnoDB引擎。

+0

的确存在着较大的区别:第一个查询了0.25秒,第二个娶了2.17秒。我熟悉InnoDB的结构等。我正在寻找mysql.com中的错误报告,但找不到它。你知道报道在哪里吗?谢谢:) – Dor 2010-05-05 14:28:16

+0

@dor:http://bugs.mysql.com/report.php – Quassnoi 2010-05-05 14:31:20

+0

你的意思是我** **应在此报告的错误?到现在为止,没有人注意到?整洁= d – Dor 2010-05-05 14:35:16