题
我已经下载了 员工数据库 并执行了一些用于基准目的的查询。
然后我注意到一个查询没有使用覆盖索引,尽管我之前创建了一个相应的索引。只有当我添加一个 FORCE INDEX
条款到查询, 它使用了覆盖索引.
我已经上传了两个文件,一个是 执行的SQL查询 另一个是 结果.
您能说出为什么查询仅在a时才使用覆盖索引 FORCE INDEX
添加条款?解释表明,在这两种情况下,索引 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:
我认为执行速度差异来自OS缓存以及其他因素。当反复执行上述2个查询时,执行时间差异可以忽略不计。我反复执行上述2个查询,并得到以下结果:
#1: 0.08 sec
#2: 0.03 sec
#1: 0.05 sec
#2: 0.05 sec
#1: 0.03 sec
#2: 0.05 sec
解决方案
实际上,您的两个查询都使用覆盖索引。
您的索引定义不包括 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
引擎。