Mysql-Explain(五):輸出列-type
Mysql-Explain(一):explain簡介和輸出列解釋
Mysql-Explain(二):explain實驗數據準備
Mysql-Explain(三):輸出列-id
Mysql-Explain(四):輸出列-select_type
Mysql-Explain(五):輸出列-type
Mysql-Explain(六):輸出列-possiable_keys、key、key_len
Mysql-Explain(七):輸出列-ref、rows
Mysql-Explain(八):輸出列-extra
簡介
type | 訪問類型排序,顯示查詢使用了何種類型,從最好到最差依次是:system>const>eq_ref>ref>range>index>ALL | system | 表只有一行記錄,這是const類型的特例,這個平時很少出現 |
const | 表最多有一個匹配行,它將在查詢開始時被讀取。因爲僅有一行記錄匹配,所以這行的列值可被優化器剩餘部分認爲是常數。const表很快,因爲它們只讀取一次! | ||
eq_ref | 驅動表和關聯表中的每行進行組合並且僅有一行記錄。這是除了system 和 const 類型之外, 這是最好的聯接類型。當連接使用索引的所有部分時, 索引是主鍵或唯一非 NULL 索引時, 將使用該值 | ||
ref | 非唯一性索引掃描,返回符合某個索引值的所有記錄,可能會有多條記錄匹配 | ||
range | 使用一個索引來檢索給定範圍的行,這種範圍索引掃描比全表掃描效率要高 | ||
index | 使用覆蓋索引 | ||
all | 全表掃描(full table scan) |
演示
-
system:表只有一行記錄,這是const類型的特例
mysql> explain select * from (select * from student limit 1) s; +----+-------------+------------+------------+--------+---------------+------+---------+------+---------+----------+-------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+------------+------------+--------+---------------+------+---------+------+---------+----------+-------+ | 1 | PRIMARY | <derived2> | NULL | system | NULL | NULL | NULL | NULL | 1 | 100.00 | NULL | | 2 | DERIVED | student | NULL | ALL | NULL | NULL | NULL | NULL | 1800825 | 100.00 | NULL | +----+-------------+------------+------------+--------+---------------+------+---------+------+---------+----------+-------+ 2 rows in set, 1 warning (0.00 sec)
上圖所示from列表子查詢派生出的臨時表<derived2>只有一行記錄,因此外層臨時表的select查詢的type值就是system。其實這種情況在實際的應用中非常少見,上面例子的Sql語句也僅僅是爲了演示這樣寫。
-
const:表最多有一個匹配行,它將在查詢開始時被讀取
mysql> explain select * from student where id = 1; +----+-------------+---------+------------+-------+---------------+---------+---------+-------+------+----------+-------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+---------+------------+-------+---------------+---------+---------+-------+------+----------+-------+ | 1 | SIMPLE | student | NULL | const | PRIMARY | PRIMARY | 4 | const | 1 | 100.00 | NULL | +----+-------------+---------+------------+-------+---------------+---------+---------+-------+------+----------+-------+ 1 row in set, 1 warning (0.00 sec)
where條件是id=常量,所以肯定只有一行記錄被匹配,所以type的值爲const。
mysql> SELECT * FROM mydb.student where name = 'NnbtPQLjMf'; +----+------------+------+-----------+----------+ | id | name | age | school_id | major_id | +----+------------+------+-----------+----------+ | 1 | NnbtPQLjMf | 23 | 321 | 314 | +----+------------+------+-----------+----------+ 1 row in set (0.60 sec) mysql> explain select * from student where name = 'NnbtPQLjMf'; +----+-------------+---------+------------+------+---------------+------+---------+------+---------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+---------+------------+------+---------------+------+---------+------+---------+----------+-------------+ | 1 | SIMPLE | student | NULL | ALL | NULL | NULL | NULL | NULL | 1800825 | 10.00 | Using where | +----+-------------+---------+------------+------+---------------+------+---------+------+---------+----------+-------------+ 1 row in set, 1 warning (0.00 sec)
上圖所示第一條SQL語句查詢結果只有一條記錄,但是下面explain的結果顯示type值卻是all。
這裏要理解清楚,const是表示mysql優化器在執行語句之前判斷其結果肯定只有一條記錄,因爲id作爲主鍵具有唯一性,id=常量肯定只有一條結果,因此mysql只有讀取到第一條記錄就可以停止查詢直接返回結果。而第二個例子中,name字段既不是主鍵也不是唯一性索引,所以優化器無法判斷其結果的數量(哪怕實際結果只是一條記錄),所以要掃描所有記錄。explain是sql語句執行前的優化和分析,不是執行後的統計! -
驅動表和關聯表中的每行進行組合並且僅有一行記錄
mysql> explain select * from student left join school on student.school_id = school.id; +----+-------------+---------+------------+--------+---------------+---------+---------+------------------------+---------+----------+-------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+---------+------------+--------+---------------+---------+---------+------------------------+---------+----------+-------+ | 1 | SIMPLE | student | NULL | ALL | NULL | NULL | NULL | NULL | 1800825 | 100.00 | NULL | | 1 | SIMPLE | school | NULL | eq_ref | PRIMARY | PRIMARY | 4 | mydb.student.school_id | 1 | 100.00 | NULL | +----+-------------+---------+------------+--------+---------------+---------+---------+------------------------+---------+----------+-------+ 2 rows in set, 1 warning (0.02 sec)
以表student爲驅動表關聯查詢表school的時候,表student和表school是一對一關係,因爲student.school_id對於的是school的主鍵(如果是唯一索引也可以),所以一個學生只能關聯一所學校,所以表student的每一行記錄都只能和關聯表school組合成最多一條記錄,所以第二行記錄中type是eq_ref,表示驅動表每行記錄只需要和關聯表組合到第一行記錄即可,你可以理解成student表的每一行記錄在關聯表school都是進行了一次type=const的查詢。eq_ref是一種比較理想的查詢方式,是我們在性能優化中比較原因看到的結果。
-
ref:非唯一性索引掃描,返回符合某個索引值的所有記錄,可能會有多條記錄匹配。
mysql> ALTER TABLE `mydb`.`student` ADD INDEX `ik_name` USING BTREE (`name`) VISIBLE; Query OK, 0 rows affected (20.56 sec) Records: 0 Duplicates: 0 Warnings: 0
先創建普通索引ik_name,該索引只有name一個字段。
mysql> explain select * from student where name = 'NnbtPQLjMf'; +----+-------------+---------+------------+------+---------------+---------+---------+-------+------+----------+-------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+---------+------------+------+---------------+---------+---------+-------+------+----------+-------+ | 1 | SIMPLE | student | NULL | ref | ik_name | ik_name | 1023 | const | 1 | 100.00 | NULL | +----+-------------+---------+------------+------+---------------+---------+---------+-------+------+----------+-------+ 1 row in set, 1 warning (0.00 sec)
單表查詢,表game的type_id字段是普通索引,所以優化器判斷其可能會有多條匹配的記錄,所以type=ref,會讀取所有匹配的記錄,而不會像const一樣碰到第一行記錄匹配便返回。但是ref是使用索引去查詢記錄,比全表掃描效率還是要高出很多,所以在性能優化分析中ref也是一種比較理想的結果。
-
range:使用一個索引來檢索給定範圍的行,這種範圍索引掃描比全表掃描效率要高。
mysql> explain select * from student where id between 1 and 10; +----+-------------+---------+------------+-------+---------------+---------+---------+------+------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+---------+------------+-------+---------------+---------+---------+------+------+----------+-------------+ | 1 | SIMPLE | student | NULL | range | PRIMARY | PRIMARY | 4 | NULL | 10 | 100.00 | Using where | +----+-------------+---------+------------+-------+---------------+---------+---------+------+------+----------+-------------+ 1 row in set, 1 warning (0.04 sec)
range和ref同樣是使用了所以進行查詢,但是range是範圍刷選,所以效率要比ref要低,但是還是要比全表掃描效率要高,而且範圍查詢在實際應用中是一種很普遍的存在,因此在性能優化分析中,range也是一種比較不錯的結果。
-
index:使用了覆蓋索引
mysql> explain select id,name from student; +----+-------------+---------+------------+-------+---------------+---------+---------+------+---------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+---------+------------+-------+---------------+---------+---------+------+---------+----------+-------------+ | 1 | SIMPLE | student | NULL | index | NULL | ik_name | 1023 | NULL | 1800825 | 100.00 | Using index | +----+-------------+---------+------------+-------+---------------+---------+---------+------+---------+----------+-------------+ 1 row in set, 1 warning (0.00 sec)
select列表只包含id,name兩個字段,而所以索引ik_name就包含這兩列,所以Mysql只需要掃描索引樹既可以找到所需要的所有值,不需要去掃描表。這是一種非常高效的查詢方式,在性能優化分析中,是我們非常樂意看到的結果。
-
all:全表掃描
mysql> explain select * from student where age < 25; +----+-------------+---------+------------+------+---------------+------+---------+------+---------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+---------+------------+------+---------------+------+---------+------+---------+----------+-------------+ | 1 | SIMPLE | student | NULL | ALL | NULL | NULL | NULL | NULL | 1800825 | 33.33 | Using where | +----+-------------+---------+------------+------+---------------+------+---------+------+---------+----------+-------------+ 1 row in set, 1 warning (0.00 sec)
掃描全表,讀取數據。如果表的數據不大的情況,這是一種可以接受的情況,但是數據量增大,尤其上百萬之後,那就要小心了。如果explain返回這個值,那就要考慮是否要優化了。