MySQL的EXPLAIN指令使用例解

數庫庫一共5個表(tab1,tab2等等),表結構以下:mysql

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行記錄;

看第一個例子:
sql

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的主鍵索引來找到關聯表的記錄。spa

再來看一個例子:code

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

下面用「強制使用索引」來驗證下上面的想法:rem

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)

結果和設想的一致,索引的較大範圍斷定仍是比較耗時的。io

看下一條EXPLAIN語句:table

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的條件。來看下這個查詢語句須要的時間:class

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字段作一個索引,會不會讓上面語句查詢速度有很大的提升呢?試一下,再看查詢速度:test

-- 原來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
+----+-------------+-------+-------+---------------------+-------------+---------+-------+------+-------------
2 rows in set (0.00 sec)

此時的掃描類型已經變爲「const」,「ref」,也就是說MySQL先根據tab4的索引找到id等於200000的記錄,而後再參照tab4的id,利用tab5表中的對應索引劃定一個範圍,在這個範圍中再比較其是否知足條件(<=2000000)。也提示咱們關聯字段要不要建索引,視關聯表在查詢時的使用條件而定(3)

相關文章
相關標籤/搜索