Mysql-Explain(八):輸出列-extra
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
簡介
extra | 包含其explain字段不適合顯示但又十分重要的額外信息 | using filesort | 使用文件內排序 |
using tmporary | 使用臨時表保存中間結果,常見於排序order by和分組group by | ||
Using index | 表示覆蓋索引即可滿足查詢要求,因而無需再回表查詢 | ||
Using index for group by | 讀取和分組都使用了覆蓋索引 | ||
Using where | Server層對存儲引擎層返回的數據做where條件過濾 | ||
impossiable where | where的值總fasle,不能獲取任何記錄 | ||
Using join buffer | 聯表查詢時使用的緩存策略,有Block Nested-Loop Join和Batched Key Access兩種策略 | ||
Select tables optimized away | 在沒有group by子句的情況下,基於索引優化的MAX/MIN操作,或者基於MyISAM存儲引擎優化的COUNT(*)操作,不必等到執行階段再進行計算,在查詢計劃生成階段既可以完成優化 | ||
Distinct | 優化Distinct操作,在找到匹配的第一行記錄後,立馬停止查找同樣的值 |
演示
-
using filesort:使用文件內排序
mysql> explain select * from student where school_id = 1 order by name; +----+-------------+---------+------------+------+---------------------+---------------------+---------+-------+------+----------+----------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+---------+------------+------+---------------------+---------------------+---------+-------+------+----------+----------------+ | 1 | SIMPLE | student | NULL | ref | ik_schoolId_majorId | ik_schoolId_majorId | 5 | const | 1960 | 100.00 | Using filesort | +----+-------------+---------+------------+------+---------------------+---------------------+---------+-------+------+----------+----------------+ 1 row in set, 1 warning (0.00 sec)
ik_schoolId_majorId只能用於查詢,無幫助name字段完成排序,所以MySQL需要藉助臨時文件來完成排序。Using filesort一般在是我們不太願意看見的,我們寫Sql語句的時候要儘量避免出現文件內排序。
mysql> explain select count(*) as count,school_id from student group by school_id order by count; +----+-------------+---------+------------+-------+---------------------+---------------------+---------+------+---------+----------+----------------------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+---------+------------+-------+---------------------+---------------------+---------+------+---------+----------+----------------------------------------------+ | 1 | SIMPLE | student | NULL | index | ik_schoolId_majorId | ik_schoolId_majorId | 10 | NULL | 1994142 | 100.00 | Using index; Using temporary; Using filesort | +----+-------------+---------+------------+-------+---------------------+---------------------+---------+------+---------+----------+----------------------------------------------+ 1 row in set, 1 warning (0.00 sec)
當然我們無法說有Using filesort的Sql語句就一定有問題,而且有些時候也無法完全消除Using filesort。
例如上面例子中的語句,需要對分組統計的數量進行排序,這個就無法通過索引來完成排序,必須通過文件內排序來完成。如果Using filesort真的就那麼十惡不赦的話,Mysql早就將它清除了。但是我們還是必須要將文件類排序的消耗降到最低,儘量多用索引,儘量減少需要排序的行數。 -
using tmporary:使用臨時表保存中間結果,常見於排序order by和分組group by
mysql> explain select count(*) as count,school_id from student group by school_id order by count; +----+-------------+---------+------------+-------+---------------------+---------------------+---------+------+---------+----------+----------------------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+---------+------------+-------+---------------------+---------------------+---------+------+---------+----------+----------------------------------------------+ | 1 | SIMPLE | student | NULL | index | ik_schoolId_majorId | ik_schoolId_majorId | 10 | NULL | 1994142 | 100.00 | Using index; Using temporary; Using filesort | +----+-------------+---------+------------+-------+---------------------+---------------------+---------+------+---------+----------+----------------------------------------------+ 1 row in set, 1 warning (0.00 sec)
還是上面的結果,MySQL使用臨時表保持了分組統計的結果,再對結果進行了排序。using tmporary和Using filesort一樣不是一個我們樂意見到的東西,因此和對待Using filesort一樣,可以避免就儘量避免,如果無法避免,就儘量將臨時表的記錄行數降到最少,減少其開銷。而且實在不行了,有時候可以通過程序層面去消除它,不一定非要糾結在MySQL語句上,比如把一個很長的語句分成幾個。
-
Using index:表示覆蓋索引即可滿足查詢要求,因而無需再回表查詢。
mysql> explain select id,school_id,major_id from student; +----+-------------+---------+------------+-------+---------------+---------------------+---------+------+---------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+---------+------------+-------+---------------+---------------------+---------+------+---------+----------+-------------+ | 1 | SIMPLE | student | NULL | index | NULL | ik_schoolId_majorId | 10 | NULL | 1994142 | 100.00 | Using index | +----+-------------+---------+------------+-------+---------------+---------------------+---------+------+---------+----------+-------------+ 1 row in set, 1 warning (0.00 sec)
-
Using where:表示MySQL將對存儲引擎層提取的結果進行過濾,它表示的是Server層對存儲引擎層返回的數據所做的過濾。
mysql> explain select * from student where school_id = 1 and name like '%N%'; +----+-------------+---------+------------+------+---------------------+---------------------+---------+-------+------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+---------+------------+------+---------------------+---------------------+---------+-------+------+----------+-------------+ | 1 | SIMPLE | student | NULL | ref | ik_schoolId_majorId | ik_schoolId_majorId | 5 | const | 1960 | 11.11 | Using where | +----+-------------+---------+------------+------+---------------------+---------------------+---------+-------+------+----------+-------------+ 1 row in set, 1 warning (0.00 sec)
索引ik_schoolId_majorId在存儲引擎層僅僅能完成shcool_id的條件讀取,然後存儲引擎返回結果到Server層,然後Server層再對數據進行name條件的篩選。
-
Using index condition:使用了Index Condition Pushdown (IPC) ,這是Mysql 5.6開始支持的一種根據索引進行查詢的優化方式。其優化支持range、ref、eq_ref、ref_or_null類型的查詢,當前支持MyISAM和InnoDB存儲引擎。
mysql> explain select * from student where school_id < 5 and major_id < 5; +----+-------------+---------+------------+-------+--------------------------------+---------------------+---------+------+-------+----------+-----------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+---------+------------+-------+--------------------------------+---------------------+---------+------+-------+----------+-----------------------+ | 1 | SIMPLE | student | NULL | range | ik_schoolId_majorId,ik_majorId | ik_schoolId_majorId | 5 | NULL | 19444 | 1.90 | Using index condition | +----+-------------+---------+------------+-------+--------------------------------+---------------------+---------+------+-------+----------+-----------------------+ 1 row in set, 1 warning (0.00 sec)
根據Mysql的最左原則,“where school_id < 5 and major_id < 5”,索引k_schoolId_majorId 只有第一個字段school_id起作用,按照上面Using where的說法,應該是存儲層返回符合“ school_id < 5 ”條件的數據到server層,然後再對“major_id < 5”條件進行匹配,然而extra並沒有出現Using where。
這是因爲Mysql使用了IPC,在上面的例子中雖然無法直接通過索引讀取所需的記錄,但是where條件都在ik_schoolId_majorId中,因此Mysql在讀取符合“ school_id < 5 ”條件的索引結果後,並沒有利用索引結果直接去讀取源表,然後返回server層進行第二次的篩選。而是直接利用索引樹再對上面索引結果進行一次篩選,然後再去讀取源表數據,從而減少了對源表讀取消耗。
在上面Using where的例子中無法使用IPC,那是因爲name條件不是索引ik_schoolId_majorId所包含的條件,無法利用索引樹進行篩選。
-
Using join buffer:聯表查詢緩存。
Block Nested-Loop Join算法:將外層循環的行/結果集存入join buffer, 內層循環的每一行與整個buffer中的記錄做比較,從而減少內層循環的次數。優化器管理參數optimizer_switch中中的block_nested_loop參數控制着BNL是否被用於優化器。默認條件下是開啓,若果設置爲off,優化器在選擇 join方式的時候會選擇NLJ(Nested Loop Join)算法。
Batched Key Access原理:對於多表join語句,當MySQL使用索引訪問第二個join表的時候,使用一個join buffer來收集第一個操作對象生成的相關列值。BKA構建好key後,批量傳給引擎層做索引查找。key是通過MRR接口提交給引擎的(mrr目的是較正順序)MRR使得查詢更有效率,要使用BKA,必須調整系統參數optimizer_switch的值,batched_key_access設置爲on,因爲BKA使用了MRR,因此也要打開MRR。 -
impossiable where:where的值總fasle,不能獲取任何記錄
mysql> explain select * from student where name = 'NnbtPQLjMf' and name = 'aa'; +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+------------------+ | 1 | SIMPLE | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | Impossible WHERE | +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+------------------+ 1 row in set, 1 warning (0.00 sec)
-
Select tables optimized away:在沒有group by子句的情況下,基於索引優化的MAX/MIN操作,或者基於MyISAM存儲引擎優化的COUNT(*)操作,不必等到執行階段再進行計算,在查詢計劃生成階段既可以完成優化
mysql> explain select max(school_id) from student; +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+------------------------------+ | 1 | SIMPLE | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | Select tables optimized away | +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+------------------------------+ 1 row in set, 1 warning (0.02 sec)
-
Distinct:優化Distinct操作,在找到匹配的第一行記錄後,立馬停止查找同樣的值
mysql> explain select distinct major_id from student left join school on school.id = student.school_id where major_id = 1; +----+-------------+---------+------------+--------+--------------------------------+------------+---------+------------------------+------+----------+-----------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+---------+------------+--------+--------------------------------+------------+---------+------------------------+------+----------+-----------------------+ | 1 | SIMPLE | student | NULL | ref | ik_schoolId_majorId,ik_majorId | ik_majorId | 5 | const | 4027 | 100.00 | Using temporary | | 1 | SIMPLE | school | NULL | eq_ref | PRIMARY | PRIMARY | 4 | mydb.student.school_id | 1 | 100.00 | Using index; Distinct | +----+-------------+---------+------------+--------+--------------------------------+------------+---------+------------------------+------+----------+-----------------------+ 2 rows in set, 1 warning (0.00 sec)
查詢所有有專業1(major_id=1)的學校Id,由於有多個學生是同一個學校同一個專業的,但是對於查詢而言只需要查詢到一條記錄即可。