您的位置:首页 > 数据库 > MySQL

MySQL的EXPLAIN指令使用例解

2016-01-25 20:31 393 查看
数库库一共5个表(tab1,tab2等等),表结构如下:

mysql> desc tab1; -- 共有400行记录
+-------+-------------+------+-----+---------+----------------+
| Field | Type        | Null | Key | Default | Extra          |
+-------+-------------+------+-----+---------+----------------+
| id    | int(11)     | NO   | PRI | NULL    | auto_increment |
| col1  | varchar(15) | YES  |     | NULL    |                |
| col2  | datetime    | YES  | MUL | NULL    |                |
+-------+-------------+------+-----+---------+----------------+
3 rows in set (0.01 sec)

mysql> desc tab2; -- 共有4000行记录
+---------+-------------+------+-----+---------+----------------+
| Field   | Type        | Null | Key | Default | Extra          |
+---------+-------------+------+-----+---------+----------------+
| id      | int(11)     | NO   | PRI | NULL    | auto_increment |
| col1    | varchar(15) | YES  |     | NULL    |                |
| col2    | datetime    | YES  | MUL | NULL    |                |
| tab1_id | int(11)     | YES  |     | NULL    |                |
+---------+-------------+------+-----+---------+----------------+
4 rows in set (0.01 sec)

-- tab3,tab4,tab5结构类似于tab2,分别有40000、400000,4000000行记录;

看第一个例子:

mysql> EXPLAIN
->    SELECT COUNT(*)
->      FROM tab5 JOIN tab4 ON tab4.id = tab5.tab4_id
->     WHERE tab5.id <= 500000;
+----+-------------+-------+--------+---------------+---------+---------+-------------------+--------+-------------+
| id | select_type | table | type   | possible_keys | key     | key_len | ref               | rows   | Extra       |
+----+-------------+-------+--------+---------------+---------+---------+-------------------+--------+-------------+
|  1 | SIMPLE      | tab5  | range  | PRIMARY       | PRIMARY | 4       | NULL              | 504980 | Using where |
|  1 | SIMPLE      | tab4  | eq_ref | PRIMARY       | PRIMARY | 4       | test.tab5.tab4_id |      1 | Using index |
+----+-------------+-------+--------+---------------+---------+---------+-------------------+--------+-------------+
2 rows in set (0.00 sec)

查看上面EXPLAIN指令的输出结果,一共有两行,前后顺序也是MySQL扫描表的先后顺序‍(1)‍‍。共有两种扫描类型“range”、“eq_ref”,说明MySQL先使用tab5表的主键索引来过滤条件(<=500000),然后再利用参照tab4的主键索引来找到关联表的记录。

再来看一个例子:

mysql> EXPLAIN
->    SELECT COUNT(*)
->      FROM tab5 JOIN tab4 ON tab4.id = tab5.tab4_id
->     WHERE tab5.id <= 1000000;
+----+-------------+-------+--------+---------------+---------+---------+-------------------+---------+-------------+
| id | select_type | table | type   | possible_keys | key     | key_len | ref               | rows    | Extra       |
+----+-------------+-------+--------+---------------+---------+---------+-------------------+---------+-------------+
|  1 | SIMPLE      | tab5  | ALL    | PRIMARY       | NULL    | NULL    | NULL              | 4000000 | Using where |
|  1 | SIMPLE      | tab4  | eq_ref | PRIMARY       | PRIMARY | 4       | test.tab5.tab4_id |       1 | Using index |
+----+-------------+-------+--------+---------------+---------+---------+-------------------+---------+-------------+
2 rows in set (0.00 sec)

和前面的EXPLAIN语句相比,把查询的范围扩大了一倍。再看扫描类型,变化为“ALL”,“eq_ref”,这说明MySQL再扫描tab5时不使用主键索引了,直接扫描全表,然后拿记录来过滤条件(<=1000000),然后再利用参照tab4的主键索引来找到关联表的记录。为什么不使用tab5的主键索引了呢?想必是MySQL判定在索引(二叉树)中查找一个范围所用的时间比扫描全表还慢,所以选择扫描全表(2)

下面用“强制使用索引”来验证下上面的想法:

mysql> SELECT COUNT(*) FROM tab5 JOIN tab4 ON tab4.id = tab5.tab4_id WHERE tab5.id <= 1000000;
1 row in set (1.73 sec)

mysql> SELECT COUNT(*) FROM tab5 FORCE INDEX (PRIMARY) JOIN tab4 ON tab4.id = tab5.tab4_id WHERE tab5.id <= 1000000;
1 row in set (4.02 sec)

结果和设想的一致,索引的较大范围判定还是比较耗时的。

看下一条EXPLAIN语句:

mysql> EXPLAIN
->    SELECT COUNT(*)
->      FROM tab5 JOIN tab4 ON tab4.id = tab5.tab4_id
->     WHERE tab5.id <= 2000000 AND tab4.id = 200000;
+----+-------------+-------+-------+---------------+---------+---------+-------+---------+-------------+
| id | select_type | table | type  | possible_keys | key     | key_len | ref   | rows    | Extra       |
+----+-------------+-------+-------+---------------+---------+---------+-------+---------+-------------+
|  1 | SIMPLE      | tab4  | const | PRIMARY       | PRIMARY | 4       | const |       1 | Using index |
|  1 | SIMPLE      | tab5  | ALL   | PRIMARY       | NULL    | NULL    | NULL  | 4000000 | Using where |
+----+-------------+-------+-------+---------------+---------+---------+-------+---------+-------------+
2 rows in set (0.00 sec)

如前面的相比,我指定查询条件tab4的一条(id = 200000)记录,此时MySQL的扫描顺序发生的变化,先扫描tab4,再扫描tab5,扫描类型也变为“const”,“ALL”。也就是说MySQL先根据tab4的索引找到id等于200000的记录,然后再对tab5表进行全表扫描,逐条比较是否满足关联条件和id小于等2000000的条件。来看下这个查询语句需要的时间:

mysql> SELECT COUNT(*) FROM tab5 JOIN tab4 ON tab4.id = tab5.tab4_id WHERE tab5.id <= 2000000 AND tab4.id = 200000;
1 row in set (0.52 sec)

如果,我对tab5表中的tab4_id字段做一个索引,会不会让上面语句查询速度有很大的提高呢?试一下,再看查询速度:

-- 原来tab5表tab4_id字段没有索引,这条语句是在加上索引后执行的
mysql> SELECT COUNT(*) FROM tab5 JOIN tab4 ON tab4.id = tab5.tab4_id WHERE tab5.id <= 2000000 AND tab4.id = 200000;
1 row in set (0.00 sec)

速度果然有很大提高,已经不超过10毫秒了。再执行下EXPLAIN语句:

mysql> EXPLAIN
->    SELECT COUNT(*)
->      FROM tab5 JOIN tab4 ON tab4.id = tab5.tab4_id
->     WHERE tab5.id <= 2000000 AND tab4.id = 200000;
+----+-------------+-------+-------+---------------------+-------------+---------+-------+------+-------------
| id | select_type | table | type  | possible_keys       | key         | key_len | ref   | rows | Extra
+----+-------------+-------+-------+---------------------+-------------+---------+-------+------+-------------
|  1 | SIMPLE      | tab4  | const | PRIMARY             | PRIMARY     | 4       | const |    1 | Using index
|  1 | SIMPLE      | tab5  | ref   | PRIMARY,idx_tab4_id | idx_tab4_id | 5       | const |   10 | Using where
+----+-------------+-------+-
3ff0
------+---------------------+-------------+---------+-------+------+-------------
2 rows in set (0.00 sec)

此时的扫描类型已经变为“const”,“ref”,也就是说MySQL先根据tab4的索引找到id等于200000的记录,然后再参照tab4的id,利用tab5表中的对应索引划定一个范围,在这个范围中再比较其是否满足条件(<=2000000)。也提示我们关联字段要不要建索引,视关联表在查询时的使用条件而定(3)。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: