2010-05-05 62 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