MySQL explain的使用

1.explain的作用

explain命令可以查看SQL語句的執行計劃。當explain於SQL語句一起使用時, MySQL將顯示來自優化器的有關語句執行計劃的信息。

2.實例

首先創建3張表並插入數據

DROP TABLE
IF
	EXISTS students;
CREATE TABLE students (
id INT ( 10 ) UNSIGNED NOT NULL AUTO_INCREMENT,
學號 INT ( 7 ) UNSIGNED NOT NULL,
姓名 VARCHAR ( 20 ) NOT NULL,
PRIMARY KEY ( `id` ) 
);
DROP TABLE
IF
	EXISTS achievement;
CREATE TABLE achievement (
id INT ( 10 ) UNSIGNED NOT NULL AUTO_INCREMENT,
課程 VARCHAR ( 20 ) NOT NULL,
學號 INT ( 7 ) UNSIGNED NOT NULL,
分數 INT ( 3 ) NOT NULL,
PRIMARY KEY ( `id` ) 
);

DROP TABLE
IF
	EXISTS studying_student;
CREATE TABLE studying_student (
  id INT (10) UNSIGNED NOT NULL AUTO_INCREMENT,
  學號 INT (7) NOT NULL,
  PRIMARY KEY (`id`)
)
INSERT INTO students ( 學號, 姓名 )
VALUES
	( 2016001, '張三' ),
	( 2016002, '李四' ),
	( 2016003, '王五' ),
	( 2016004, '吳六' );
INSERT INTO achievement ( 課程, 學號, 分數 )
VALUES
	( '計算機', 2016001, 99 ),
	( '計算機', 2016002, 89 ),
	( '計算機', 2016003, 79 ),
	( '計算機', 2016005, 69 );

INSERT INTO studying_student ( 學號 )
VALUES
	( 2016003 ),
	( 2016004 )

students表中的數據:
在這裏插入圖片描述
studying_student表中的數據:
在這裏插入圖片描述

studying_student表中的數據:
在這裏插入圖片描述
然後執行:

EXPLAIN SELECT
	* 
FROM
	achievement 
WHERE
	`學號` IN (
						SELECT
							students.`學號` 
						FROM
							students
						LEFT JOIN studying_student
						ON
							students.`學號` = studying_student.`學號`
	);

結果:
在這裏插入圖片描述
id操作表的順序

跟據id的值出現的情況不同,操作表的順序或者執行select字句的順序也會有所不同

id值出現的情況 執行的順序
id值相同的時候 由上之下執行
id值不同的時候 id值越大越是先執行
id值有相同也有不同的時候 id值越大的先執行,id值相同的由上之下執行

所以上圖中的例子先執行查詢序號爲2,table爲students的select語句,然後是查詢序號爲2,table爲studying_student的語句,接着是執行table爲achievement和table爲 subquery2語句

select_type:查詢的類型(來源於Mysql5.7文檔)

Value JSON Name Meaning
SIMPLE None 簡單查詢(不適用union和子查詢的)
PRIMARY None 最外層的查詢
UNION None UNION中的第二個或者後面的SELECT語句
DEPENDENT UNION dependent (true) UNION中的第二個或者後面的SELECT語句,依賴於外部查詢
UNION RESULT union_result UNION結果
SUBQUERY None 子查詢中的第一個SELECT語句
DEPENDENT SUBQUERY dependent (true) 子查詢中的第一個SELECT語句,依賴於外部查詢
DERIVED None 派生表的SELECT(FROM子句的子查詢)
MATERIALIZED materialized_from_subquery 物化子查詢
UNCACHEABLE SUBQUERY cacheable (false) 對於該結果不能被緩存,必須重新評估外部查詢的每一行子查詢
UNCACHEABLE UNION cacheable (false) UNION中的第二個或者後面的SELECT語句屬於不可緩存子查詢 (see UNCACHEABLE SUBQUERY)

上圖中出現的MATERIALIZED:物化子查詢
先了解一下什麼是物化子查詢

什麼是物化策略

如果子查詢執行一次即可以得到結果,即子查詢的結果是穩定的,則這樣的子查詢可以被緩存起來,多次使用緩存即是物化。緩存到內存中,如果內存中放不下,則會寫外存。在MySQL中,這個緩存對應的是臨時表(即:物化利用了臨時表的機制)。(參考自: MySQL子查詢優化—詳解–2.)
即上圖的兩個MATERIALIZED是把查詢結果進行了緩存供外部查詢使用

table:顯示當前的一行數據是關於哪一個表的

如果查詢使用了別名,那麼這裏顯示的是別名,如果不涉及對數據表的操作,那麼這顯示爲null,如果顯示爲尖括號括起來的就表示這個是臨時表,後邊的N就是執行計劃中的id,表示結果來自於這個查詢產生。如果是尖括號括起來的<union M,N>,與類似,也是一個臨時表,表示這個結果來自於union查詢的id爲M,N的結果集。如果是尖括號括起來的,這個表示子查詢結果被物化,之後子查詢結果可以被複用(參考自: Mysql優化之explain詳解.)

select_type:

select_type Value JSON Name Meaning
SIMPLE None Simple SELECT (not using UNION or subqueries) [表示不需要union操作或者不包含子查詢的簡單select查詢。有連接查詢時,外層的查詢爲simple,且只有一個]
PRIMARY None Outermost SELECT [一個需要union操作或者含有子查詢的select,位於最外層的單位查詢的select_type即爲primary。且只有一個]
UNION None Second or later SELECT statement in a UNION [union連接的select查詢,除了第一個表外,第二個及以後的表select_type都是union]
DEPENDENT UNION dependent (true) Second or later SELECT statement in a UNION, dependent on outer query [與union一樣,出現在union 或union all語句中,但是這個查詢要受到外部查詢的影響]
UNION RESULT union_result Result of a UNION. [包含union的結果集,在union和union all語句中,因爲它不需要參與查詢,所以id字段爲null]
SUBQUERY None First SELECT in subquery [除了from字句中包含的子查詢外,其他地方出現的子查詢都可能是subquery]
DEPENDENT SUBQUERY dependent (true) First SELECT in subquery, dependent on outer query [與dependent union類似,表示這個subquery的查詢要受到外部表查詢的影響]
DERIVED None Derived table SELECT (subquery in FROM clause) [from字句中出現的子查詢]
MATERIALIZED materialized_from_subquer Materialized subquery [被物化的子查詢]
UNCACHEABLE SUBQUERY cacheable (false) A subquery for which the result cannot be cached and must be re-evaluated for each row of the outer query [對於外層的主表,子查詢不可被物化,每次都需要計算(耗時操作]
UNCACHEABLE UNION cacheable (false) The second or later select in a UNION that belongs to an uncacheable subquery (see UNCACHEABLE SUBQUERY) [UNION操作中,內層的不可被物化的子查詢(類似於UNCACHEABLE SUBQUERY]

因此上例中的MATERIALIZED意思就是被物化的子查詢

type:
system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL
除了all之外,其他的type都可以使用到索引,除了index_merge之外,其他的type只可以用到一個索引

type 解釋
system 表中只有一行數據或者是空表,且只能用於myisam和memory表。如果是Innodb引擎表,type列在這個情況通常都是all或者index
const 使用唯一索引或者主鍵,返回記錄一定是1行記錄的等值where條件時,通常type是const。其他數據庫也叫做唯一索引掃描
eq_reft 出現在要連接過個表的查詢計劃中,驅動表只返回一行數據,且這行數據是第二個表的主鍵或者唯一索引,且必須爲not null,唯一索引和主鍵是多列時,只有所有的列都用作比較時纔會出現eq_ref
reft 不像eq_ref那樣要求連接順序,也沒有主鍵和唯一索引的要求,只要使用相等條件檢索時就可能出現,常見與輔助索引的等值查找。或者多列主鍵、唯一索引中,使用第一個列之外的列作爲等值查找也會出現,總之,返回數據不唯一的等值查找就可能出現。
fulltextt 全文索引檢索,要注意,全文索引的優先級很高,若全文索引和普通索引同時存在時,mysql不管代價,優先選擇使用全文索引
ref_or_nullt 與ref方法類似,只是增加了null值的比較。實際用的不多。例如:SELECT * FROM ref_table WHERE key_column=expr OR key_column IS NULL;
index_merget 表示查詢使用了兩個以上的索引,最後取交集或者並集,常見and ,or的條件使用了不同的索引,官方排序這個在ref_or_null之後,但是實際上由於要讀取所個索引,性能可能大部分時間都不如range
unique_subqueryt 用於where中的in形式子查詢,子查詢返回不重複值唯一值
index_subqueryt 用於in形式子查詢使用到了輔助索引或者in常數列表,子查詢可能返回重複值,可以使用索引將子查詢去重。
ranget 索引範圍掃描,常見於使用 =, <>, >, >=, <, <=, IS NULL, <=>, BETWEEN, IN()或者like等運算符的查詢中。
index 索引全表掃描,把索引從頭到尾掃一遍,常見於使用索引列就可以處理不需要讀取數據文件的查詢、可以使用索引排序或者分組的查詢。按照官方文檔的說法:

● If the index is a covering index for the queries and can be used to satisfy all data required from the table, only the index tree is scanned. In this case, the Extracolumn says Using index. An index-only scan usually is faster than ALL because the size of the index usually is smaller than the table data.● A full table scan is performed using reads from the index to look up data rows in index order. Uses index does not appear in the Extra column.

以上說的是索引掃描的兩種情況,一種是查詢使用了覆蓋索引,那麼它只需要掃描索引就可以獲得數據,這個效率要比全表掃描要快,因爲索引通常比數據表小,而且還能避免二次查詢。在extra中顯示Using index,反之,如果在索引上進行全表掃描,沒有Using index的提示。

all 這個就是全表掃描數據文件,然後再在server層進行過濾返回符合要求的記錄。

可以看到上例中的type全部都是all, 原因是字段都沒有建立索引(id除外)
我們嘗試給學號建立索引,由於學號是唯一的,因此我們可以建立唯一索引

CREATE INDEX stu_num_index ON students (`學號` ASC);
CREATE INDEX stu_num_index ON achievement (`學號` ASC);
CREATE INDEX stu_num_index ON studying_student (`學號` ASC);

給三張表的學號列個建立一個唯一索引,繼續用上面的explain語句查詢
結果:
在這裏插入圖片描述
對比上面沒有建立索引的結果:
在這裏插入圖片描述
可以發現查詢少了一行, 第三步執行消失,這說明主查詢的where語句括號內的執行結果沒有被物化。
possible_keys 三行都是新建立的索引stu_num_index

possible_keys
查詢可能使用到的索引都會在這裏列出來
可以看到第1、第3行都是我們剛剛給兩張表通過學號列建立的索引,
第2行<auto_key>是臨時表 subquery2 生成的索引

key
查詢真正使用到的索引,select_type爲index_merge時,這裏可能出現兩個以上的索引,其他的select_type這裏只會出現一個。

key_len
用於處理查詢的索引長度,如果是單列索引,那就整個索引長度算進去,如果是多列索引,那麼查詢不一定都能使用到所有的列,具體使用到了多少個列的索引,這裏就會計算進去,沒有使用到的列,這裏不會計算進去。留意下這個列的值,算一下你的多列索引總長度就知道有沒有使用到所有的列了。要注意,mysql的ICP特性使用到的索引不會計入其中。另外,key_len只計算where條件用到的索引長度,而排序和分組就算用到了索引,也不會計算到key_len中。

ref
如果是使用的常數等值查詢,這裏會顯示const,如果是連接查詢,被驅動表的執行計劃這裏會顯示驅動表的關聯字段,如果是條件使用了表達式或者函數,或者條件列發生了內部隱式轉換,這裏可能顯示爲func

rows
這裏是執行計劃中估算的掃描行數,不是精確值

extra
對於extra列,官網上有這樣一段話:

If you want to make your queries as fast as possible, look out for Extra column values of Using filesort and Using temporary, or, in JSON-formatted EXPLAINoutput, for using_filesort and using_temporary_table properties equal to true.

大概的意思就是說,如果你想要優化你的查詢,那就要注意extra輔助信息中的using filesort和using temporary,這兩項非常消耗性能,需要注意。

這個列可以顯示的信息非常多,有幾十種,常用的有:

extra 解釋
distinct 在select部分使用了distinc關鍵字
no tables used 不帶from字句的查詢或者From dual查詢。使用not in()形式子查詢或not exists運算符的連接查詢,這種叫做反連接。即,一般連接查詢是先查詢內表,再查詢外表,反連接就是先查詢外表,再查詢內表。
using filesort 排序時無法使用到索引時,就會出現這個。常見於order by和group by語句中
using index 查詢時不需要回表查詢,直接通過索引就可以獲取查詢的數據。
using join buffer(block nested loop),using join buffer(batched key accss) 5.6.x之後的版本優化關聯查詢的BNL,BKA特性。主要是減少內表的循環數量以及比較順序地掃描查詢。
using sort_union,using_union,using intersect,using sort_intersection using intersect 表示使用and的各個索引的條件時,該信息表示是從處理結果獲取交集
using union 表示使用or連接各個使用索引的條件時,該信息表示從處理結果獲取並集
using sort_union和using sort_intersection 與前面兩個對應的類似,只是他們是出現在用and和or查詢信息量大時,先查詢主鍵,然後進行排序合併後,才能讀取記錄並返回。
using temporary 表示使用了臨時表存儲中間結果。臨時表可以是內存臨時表和磁盤臨時表,執行計劃中看不出來,需要查看status變量,used_tmp_table,used_tmp_disk_table才能看出來。
using where 表示存儲引擎返回的記錄並不是所有的都滿足查詢條件,需要在server層進行過濾。查詢條件中分爲限制條件和檢查條件,5.6之前,存儲引擎只能根據限制條件掃描數據並返回,然後server層根據檢查條件進行過濾再返回真正符合查詢的數據。5.6.x之後支持ICP特性,可以把檢查條件也下推到存儲引擎層,不符合檢查條件和限制條件的數據,直接不讀取,這樣就大大減少了存儲引擎掃描的記錄數量。extra列顯示using index condition
firstmatch(tb_name) 5.6.x開始引入的優化子查詢的新特性之一,常見於where字句含有in()類型的子查詢。如果內表的數據量比較大,就可能出現這個
loosescan(m…n) 5.6.x之後引入的優化子查詢的新特性之一,在in()類型的子查詢中,子查詢返回的可能有重複記錄時,就可能出現這個

除了這些之外,還有很多查詢數據字典庫,執行計劃過程中就發現不可能存在結果的一些提示信息

filtered
使用explain extended時會出現這個列,5.7之後的版本默認就有這個字段,不需要使用explain extended了。這個字段表示存儲引擎返回的數據在server層過濾後,剩下多少滿足查詢的記錄數量的比例,注意是百分比,不是具體記錄數。

(參考自: Mysql優化之explain詳解.)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章