最完整的Explain總結(jié),SQL優(yōu)化不再困難
先看看具體有哪些字段:
mysql>?EXPLAIN?SELECT?1;

其實除了以SELECT開頭的查詢語句,其余的DELETE、INSERT、REPLACE以及UPDATE語句前邊都可以加上EXPLAIN這個詞兒,用來查看這些語句的執(zhí)行計劃
建兩張測試表:
CREATE?TABLE?t1?(
????id?INT?NOT?NULL?AUTO_INCREMENT,
????key1?VARCHAR(100),
????key2?VARCHAR(100),
????key3?VARCHAR(100),
????name?VARCHAR(100),
????PRIMARY?KEY?(id),
????KEY?idx_key1?(key1),
????KEY?idx_key2_key3(key2,?key3)
)?Engine=InnoDB?CHARSET=utf8;
CREATE?TABLE?t2?(
????id?INT?NOT?NULL?AUTO_INCREMENT,
????key1?VARCHAR(100),
????key2?VARCHAR(100),
????key3?VARCHAR(100),
????name?VARCHAR(100),
????PRIMARY?KEY?(id),
????KEY?idx_key1?(key1),
????KEY?idx_key2_key3(key2,?key3)
)?Engine=InnoDB?CHARSET=utf8;
兩個變種
explain extended
會在 explain 的基礎(chǔ)上額外提供一些查詢優(yōu)化的信息。緊隨其后通過 show warnings 命令可以 得到優(yōu)化后的查詢語句,從而看出優(yōu)化器優(yōu)化了什么
explain?extended?SELECT?*?FROM?t1?where?key1?=?'11';
show?warnings;
explain partitions
相比 explain 多了個 partitions 字段,如果查詢是基于分區(qū)表的話,會顯示查詢將訪問的分區(qū)。
EXPLAIN?PARTITIONS?SELECT?*?FROM?t1?INNER?JOIN?t2?ON?t1.key3?=?t2.key3;

table列
這一列表示 explain 的一行正在訪問哪個表
mysql>?EXPLAIN?SELECT?*?FROM?t1;

這個查詢語句只涉及對t1表的單表查詢,所以EXPLAIN輸出中只有一條記錄,其中的table列的值是t1,表明這條記錄是用來說明對t1表的單表訪問。
mysql>?EXPLAIN?SELECT?*?FROM?t1?INNER?JOIN?t2;

可以看到這個連接查詢的執(zhí)行計劃中有兩條記錄,這兩條記錄的table列分別是t1和t2,這兩條記錄用來分別說明對t1表和t2表的訪問
注意:
當(dāng) from 子句中有子查詢時,table列是?
?格式,表示當(dāng)前查詢依賴 id=N 的查詢,于是先執(zhí)行 id=N 的查詢。當(dāng)有 union 時,UNION RESULT 的 table 列的值為,1和2表示參與 union 的 select 行id。
id列
id列的編號是 select 的序列號,有幾個 select 就有幾個id,并且id的順序是按 select 出現(xiàn)的順序增長的。
id列越大執(zhí)行優(yōu)先級越高,id相同則從上往下執(zhí)行,id為NULL最后執(zhí)行
比如下邊這個查詢中只有一個SELECT關(guān)鍵字,所以EXPLAIN的結(jié)果中也就只有一條id列為1的記錄:
mysql>?EXPLAIN?SELECT?*?FROM?t1?WHERE?key1?=?'e038f672a8';

對于連接查詢來說,一個SELECT關(guān)鍵字后邊的FROM子句中可以跟隨多個表,所以在連接查詢的執(zhí)行計劃中,每個表都會對應(yīng)一條記錄,但是這些記錄的id值都是相同的,比如:
mysql>?EXPLAIN?SELECT?*?FROM?t1?INNER?JOIN?t2;

可以看到,上述連接查詢中參與連接的t1和t2表分別對應(yīng)一條記錄,但是這兩條記錄對應(yīng)的id值都是1。
注意:
在連接查詢的執(zhí)行計劃中,每個表都會對應(yīng)一條記錄,這些記錄的id列的值是相同的,出現(xiàn)在前邊的表表示驅(qū)動表,出現(xiàn)在后邊的表表示被驅(qū)動表。所以從上邊的EXPLAIN輸出中我們可以看出,查詢優(yōu)化器準備讓t2表作為驅(qū)動表,讓t1表作為被驅(qū)動表來執(zhí)行查詢
對于包含子查詢的查詢語句來說,就可能涉及多個SELECT關(guān)鍵字,所以在包含子查詢的查詢語句的執(zhí)行計劃中,每個SELECT關(guān)鍵字都會對應(yīng)一個唯一的id值,比如這樣:
mysql>?EXPLAIN?SELECT?*?FROM?t1?WHERE?key1?IN?(SELECT?key1?FROM?t2)?OR?key3?=?'a1b6cee57a';

從輸出結(jié)果中我們可以看到,t1表在外層查詢中,外層查詢有一個獨立的SELECT關(guān)鍵字,所以第一條記錄的id值就是1,t2表在子查詢中,子查詢有一個獨立的SELECT關(guān)鍵字,所以第二條記錄的id值就是2。
但是這里大家需要特別注意,查詢優(yōu)化器可能對涉及子查詢的查詢語句進行重寫,從而轉(zhuǎn)換為連接查詢。所以如果我們想知道查詢優(yōu)化器對某個包含子查詢的語句是否進行了重寫,直接查看執(zhí)行計劃就好了,比如說:
mysql>?EXPLAIN?SELECT?*?FROM?t1?WHERE?key1?IN?(SELECT?key3?FROM?t2?WHERE?t1.key1?=?'a1b6cee57a');

可以看到,雖然我們的查詢語句是一個子查詢,但是執(zhí)行計劃中t1和t2表對應(yīng)的記錄的id值全部是1,這就表明了查詢優(yōu)化器將子查詢轉(zhuǎn)換為了連接查詢。
對于包含UNION子句的查詢語句來說,每個SELECT關(guān)鍵字對應(yīng)一個id值也是沒錯的,不過還是有點兒特別的東西,比方說下邊這個查詢:
mysql>?EXPLAIN?SELECT?*?FROM?t1?UNION?SELECT?*?FROM?t2;

UNION子句是為了把id為1的查詢和id為2的查詢的結(jié)果集合并起來并去重,所以在內(nèi)部創(chuàng)建了一個名為的臨時表(就是執(zhí)行計劃第三條記錄的table列的名稱),id為NULL表明這個臨時表是為了合并兩個查詢的結(jié)果集而創(chuàng)建的。
跟UNION對比起來,UNION ALL就不需要為最終的結(jié)果集進行去重,它只是單純的把多個查詢的結(jié)果集中的記錄合并成一個并返回給用戶,所以也就不需要使用臨時表。所以在包含UNION ALL子句的查詢的執(zhí)行計劃中,就沒有那個id為NULL的記錄,如下所示:
mysql>?EXPLAIN?SELECT?*?FROM?t1?UNION?ALL?SELECT?*?FROM?t2;

select_type列
MySQL每一個SELECT關(guān)鍵字代表的小查詢都定義了一個稱之為select_type的屬性,意思是我們只要知道了某個小查詢的select_type屬性,就知道了這個小查詢在整個大查詢中扮演了一個什么角色
下面是官方文檔介紹:
https://dev.mysql.com/doc/refman/5.7/en/explain-output.html#explain_select_type

SIMPLE
查詢語句中不包含UNION或者子查詢的查詢都算作是SIMPLE類型,比方說下邊這個單表查詢的select_type的值就是SIMPLE:
mysql>?EXPLAIN?SELECT?*?FROM?t1;

PRIMARY
對于包含UNION、UNION ALL或者子查詢的大查詢來說,它是由幾個小查詢組成的,其中最左邊的那個查詢的select_type值就是PRIMARY,比方說:
mysql>?EXPLAIN?SELECT?*?FROM?t1?UNION?SELECT?*?FROM?t2;

從結(jié)果中可以看到,最左邊的小查詢SELECT * FROM t1對應(yīng)的是執(zhí)行計劃中的第一條記錄,它的select_type值就是PRIMARY。
UNION
對于包含UNION或者UNION ALL的大查詢來說,它是由幾個小查詢組成的,其中除了最左邊的那個小查詢以外,其余的小查詢的select_type值就是UNION,可以對比上一個例子的效果
UNION RESULT
MySQL選擇使用臨時表來完成UNION查詢的去重工作,針對該臨時表的查詢的select_type就是UNION RESULT,同樣對比上面的例子
SUBQUERY
如果包含子查詢的查詢語句不能夠轉(zhuǎn)為對應(yīng)的semi-join的形式,并且該子查詢是不相關(guān)子查詢,并且查詢優(yōu)化器決定采用將該子查詢物化的方案來執(zhí)行該子查詢時,該子查詢的第一個SELECT關(guān)鍵字代表的那個查詢的select_type就是SUBQUERY,比如下邊這個查詢:
概念解釋:
semi-join子查詢,是指當(dāng)一張表在另一張表找到匹配的記錄之后,半連接(semi-jion)返回第一張表中的記錄。與條件連接相反,即使在右節(jié)點中找到幾條匹配的記錄,左節(jié)點 的表也只會返回一條記錄。另外,右節(jié)點的表一條記錄也不會返回。半連接通常使用IN 或 EXISTS 作為連接條件
物化:這個將子查詢結(jié)果集中的記錄保存到臨時表的過程稱之為物化(Materialize)。那個存儲子查詢結(jié)果集的臨時表稱之為物化表。正因為物化表中的記錄都建立了索引(基于內(nèi)存的物化表有哈希索引,基于磁盤的有B+樹索引),通過索引執(zhí)行IN語句判斷某個操作數(shù)在不在子查詢結(jié)果集中變得非???,從而提升了子查詢語句的性能。
mysql>?EXPLAIN?SELECT?*?FROM?t1?WHERE?key1?IN?(SELECT?key1?FROM?t2)?OR?key3?=?'a1b6cee57a';

可以看到,外層查詢的select_type就是PRIMARY,子查詢的select_type就是SUBQUERY。
DEPENDENT SUBQUERY
如果包含子查詢的查詢語句不能夠轉(zhuǎn)為對應(yīng)的semi-join的形式,并且該子查詢是相關(guān)子查詢,則該子查詢的第一個SELECT關(guān)鍵字代表的那個查詢的select_type就是DEPENDENT SUBQUERY,比如下邊這個查詢:
mysql>?EXPLAIN?SELECT?*?FROM?t1?WHERE?key1?IN?(SELECT?key1?FROM?t2?WHERE?t1.key2?=?t2.key2)?OR?key3?=?'a1b6cee57a';

DEPENDENT UNION
在包含UNION或者UNION ALL的大查詢中,如果各個小查詢都依賴于外層查詢的話,那除了最左邊的那個小查詢之外,其余的小查詢的select_type的值就是DEPENDENT UNION。比方說下邊這個查詢:
mysql>?EXPLAIN?SELECT?*?FROM?t1?WHERE?key1?IN?(SELECT?key1?FROM?t2?WHERE?key1?=?'a1b6cee57a'?UNION?SELECT?key1?FROM?t1?WHERE?key1?=?'a1b6cee57a');

這個查詢比較復(fù)雜啊,大查詢里包含了一個子查詢,子查詢里又是由UNION連起來的兩個小查詢。從執(zhí)行計劃中可以看出來,SELECT key1 FROM t2 WHERE key1 = 'a1b6cee57a'這個小查詢由于是子查詢中第一個查詢,所以它的select_type是DEPENDENT SUBQUERY,而SELECT key1 FROM t1 WHERE key1 = 'a1b6cee57a'這個查詢的select_type就是DEPENDENT UNION。
DERIVED
對于采用物化的方式執(zhí)行的包含派生表的查詢,該派生表對應(yīng)的子查詢的select_type就是DERIVED,比方說下邊這個查詢:
mysql>?EXPLAIN?SELECT?*?FROM?(SELECT?key1,?count(*)?as?t?FROM?t1?GROUP?BY?key1)?AS?derived_t1?where?t?>?1;

從執(zhí)行計劃中可以看出,id為2的記錄就代表子查詢的執(zhí)行方式,它的select_type是DERIVED,說明該子查詢是以物化的方式執(zhí)行的。id為1的記錄代表外層查詢,大家注意看它的table列顯示的是,表示該查詢是針對將派生表物化之后的表進行查詢的。
MATERIALIZED
當(dāng)查詢優(yōu)化器在執(zhí)行包含子查詢的語句時,選擇將子查詢物化之后與外層查詢進行連接查詢時,該子查詢對應(yīng)的select_type屬性就是MATERIALIZED,比如下邊這個查詢:
mysql>?EXPLAIN?SELECT?*?FROM?t1?WHERE?key1?IN?(SELECT?key1?FROM?t2);

執(zhí)行計劃的第三條記錄的id值為2,說明該條記錄對應(yīng)的是一個單表查詢,從它的select_type值為MATERIALIZED可以看出,查詢優(yōu)化器是要把子查詢先轉(zhuǎn)換成物化表。然后看執(zhí)行計劃的前兩條記錄的id值都為1,說明這兩條記錄對應(yīng)的表進行連接查詢,需要注意的是第二條記錄的table列的值是,說明該表其實就是id為2對應(yīng)的子查詢執(zhí)行之后產(chǎn)生的物化表,然后將s1和該物化表進行連接查詢。
type列
這一列表示關(guān)聯(lián)類型或訪問類型,即MySQL決定如何查找表中的行,查找數(shù)據(jù)行記錄的大概范圍。依次從最優(yōu)到最差分別為:system > const > eq_ref > ref > range > index > ALL一般來說,得保證查詢達到range級別,最好達到ref
NULL
mysql能夠在優(yōu)化階段分解查詢語句,在執(zhí)行階段用不著再訪問表或索引。例如:在索引列中選取最小值,可以單獨查找索引來完成,不需要在執(zhí)行時訪問表
mysql>?explain?select?min(id)?from?t1;

eq_ref
primary key 或 unique key 索引的所有部分被連接使用 ,最多只會返回一條符合條件的記錄。這可能是在 const 之外最好的聯(lián)接類型了,簡單的 select 查詢不會出現(xiàn)這種 type。
在連接查詢時,如果被驅(qū)動表是通過主鍵或者唯一二級索引列等值匹配的方式進行訪問的(如果該主鍵或者唯一二級索引是聯(lián)合索引的話,所有的索引列都必須進行等值比較),則對該被驅(qū)動表的訪問方法就是eq_ref,比方說:
mysql>?EXPLAIN?SELECT?*?FROM?t1?INNER?JOIN?t2?ON?t1.id?=?t2.id;

從執(zhí)行計劃的結(jié)果中可以看出,MySQL打算將t2作為驅(qū)動表,t1作為被驅(qū)動表,重點關(guān)注t1的訪問方法是eq_ref,表明在訪問t1表的時候可以通過主鍵的等值匹配來進行訪問。
ref
當(dāng)通過普通的二級索引列與常量進行等值匹配時來查詢某個表,那么對該表的訪問方法就可能是ref
相比?eq_ref,不使用唯一索引,而是使用普通索引或者唯一性索引的部分前綴,索引要和某個值相比較,可能會找到多個符合條件的行。
mysql>?EXPLAIN?SELECT?*?FROM?t1?WHERE?key1?=?'a';

可以看到type列的值是ref,表明MySQL即將使用ref訪問方法來執(zhí)行對t1表的查詢
system,const
mysql能對查詢的某部分進行優(yōu)化并將其轉(zhuǎn)化成一個常量(可以看show warnings 的結(jié)果)。用于 primary key 或 unique key 的所有列與常數(shù)比較時,所以表最多有一個匹配行,讀取1次,速度比較快。system是const的特例,表里只有一條元組匹配時為system
mysql>?EXPLAIN?SELECT?*?FROM?t1?WHERE?id?=?5;

ref_or_null
當(dāng)對普通二級索引進行等值匹配查詢,該索引列的值也可以是NULL值時,那么對該表的訪問方法就可能是ref_or_null,比如說:
mysql>?EXPLAIN?SELECT?*?FROM?t1?WHERE?key1?=?'a'?OR?key1?IS?NULL;

index_merge
一般情況下對于某個表的查詢只能使用到一個索引,但在某些場景下可以使用多種索引合并的方式來執(zhí)行查詢,我們看一下執(zhí)行計劃中是怎么體現(xiàn)MySQL使用索引合并的方式來對某個表執(zhí)行查詢的:
mysql>?EXPLAIN?SELECT?*?FROM?t1?WHERE?key1?=?'a'?OR?key2?=?'a';

從執(zhí)行計劃的type列的值是index_merge就可以看出,MySQL打算使用索引合并的方式來執(zhí)行對t1表的查詢。
unique_subquery
類似于兩表連接中被驅(qū)動表的eq_ref訪問方法,unique_subquery是針對在一些包含IN子查詢的查詢語句中,如果查詢優(yōu)化器決定將IN子查詢轉(zhuǎn)換為EXISTS子查詢,而且子查詢可以使用到主鍵進行等值匹配的話,那么該子查詢執(zhí)行計劃的type列的值就是unique_subquery,比如下邊的這個查詢語句:
mysql>?EXPLAIN?SELECT?*?FROM?t1?WHERE?key2?IN?(SELECT?id?FROM?t2?where?t1.key1?=?t2.key1)?OR?key3?=?'a';

可以看到執(zhí)行計劃的第二條記錄的type值就是unique_subquery,說明在執(zhí)行子查詢時會使用到id列的索引。
range
范圍掃描通常出現(xiàn)在 in(), between ,> ,<, >= 等操作中。使用一個索引來檢索給定范圍的行。
mysql>?EXPLAIN?SELECT?*?FROM?t1?WHERE?key1?IN?('a',?'b',?'c');

index
當(dāng)我們可以使用索引覆蓋,但需要掃描全部的索引記錄時,該表的訪問方法就是index
掃描全表索引,這通常比ALL快一些。(index是從索引中讀取的,而all是從硬盤中讀取)
ALL
最熟悉的全表掃描
mysql>?explain?select?*?from?t2;
一般來說,這些訪問方法按照我們介紹它們的順序性能依次變差。其中除了All這個訪問方法外,其余的訪問方法都能用到索引,除了index_merge訪問方法外,其余的訪問方法都最多只能用到一個索引。
possible_keys和key列
possible_keys列顯示查詢可能使用哪些索引來查找。
explain 時可能出現(xiàn)?possible_keys?有列,而 key 顯示 NULL 的情況,這種情況是因為表中數(shù)據(jù)不多,mysql認為索引對此查詢幫助不大,選擇了全表查詢。
如果possible_keys列是NULL,則沒有相關(guān)的索引。在這種情況下,可以通過檢查 where 子句看是否可以創(chuàng)造一個適當(dāng)?shù)乃饕齺硖岣卟樵冃阅?,然后?explain 查看效果。
key列顯示mysql實際采用哪個索引來優(yōu)化對該表的訪問。如果沒有使用索引,則該列是 NULL。如果想強制mysql使用或忽視possible_keys列中的索引,在查詢中使用 force index、ignore index。
比方說下邊這個查詢:
mysql>?EXPLAIN?SELECT?*?FROM?t1?WHERE?key1?>?'z'?AND?key2?=?'a';

上述執(zhí)行計劃的possible_keys列的值是idx_key1,idx_key2_key3,表示該查詢可能使用到idx_key1,idx_key2_key3兩個索引,然后key列的值是idx_key3,表示經(jīng)過查詢優(yōu)化器計算使用不同索引的成本后,最后決定使用idx_key3來執(zhí)行查詢比較劃算。
需要注意的一點是,possible_keys列中的值并不是越多越好,可能使用的索引越多,查詢優(yōu)化器計算查詢成本時就得花費更長時間,所以如果可以的話,盡量刪除那些用不到的索引。
key_len列
這一列顯示了mysql在索引里使用的字節(jié)數(shù),通過這個值可以算出具體使用了索引中的哪些列
對于使用固定長度類型的索引列來說,它實際占用的存儲空間的最大長度就是該固定值,對于指定字符集的變長類型的索引列來說,比如某個索引列的類型是VARCHAR(100),使用的字符集是utf8,那么該列實際占用的最大存儲空間就是100 × 3 = 300個字節(jié)。
如果該索引列可以存儲NULL值,則key_len比不可以存儲NULL值時多1個字節(jié)。
對于變長字段來說,都會有2個字節(jié)的空間來存儲該變長列的實際長度。
當(dāng)字符串過長時,mysql會做一個類似左前綴索引的處理,將前半部分的字符提取出來做索引。
key_len計算規(guī)則如下:字符串 char(n):n字節(jié)長度 varchar(n):2字節(jié)存儲字符串長度,如果是utf-8,則長度 3n + 2 數(shù)值類型 tinyint:1字節(jié) smallint:2字節(jié) int:4字節(jié) bigint:8字節(jié) 時間類型 date:3字節(jié) timestamp:4字節(jié) datetime:8字節(jié)
比如下邊這個查詢:
mysql>?EXPLAIN?SELECT?*?FROM?s1?WHERE?id?=?5;

由于id列的類型是INT,并且不可以存儲NULL值,所以在使用該列的索引時key_len大小就是4。
對于可變長度的索引列來說,比如下邊這個查詢:
mysql>?EXPLAIN?SELECT?*?FROM?t1?WHERE?key1?=?'a';

由于key1列的類型是VARCHAR(100),所以該列實際最多占用的存儲空間就是300字節(jié),又因為該列允許存儲NULL值,所以key_len需要加1,又因為該列是可變長度列,所以key_len需要加2,所以最后ken_len的值就是303。
rows列
這一列是mysql估計要讀取并檢測的行數(shù),注意這個不是結(jié)果集里的行數(shù)。
如果查詢優(yōu)化器決定使用全表掃描的方式對某個表執(zhí)行查詢時,執(zhí)行計劃的rows列就代表預(yù)計需要掃描的行數(shù),如果使用索引來執(zhí)行查詢時,執(zhí)行計劃的rows列就代表預(yù)計掃描的索引記錄行數(shù)。比如下邊這個查詢:
mysql>?EXPLAIN?SELECT?*?FROM?t1?WHERE?key1?>?'a';

我們看到執(zhí)行計劃的rows列的值是113,這意味著查詢優(yōu)化器在經(jīng)過分析使用idx_key1進行查詢的成本之后,覺得滿足key1 > 'a'這個條件的記錄只有113條。
ref列
這一列顯示了在key列記錄的索引中,表查找值所用到的列或常量,常見的有:const(常量),字段名(例:t1.id)
ref列展示的就是與索引列作等值匹配的值什么,比如只是一個常數(shù)或者是某個列。大家看下邊這個查詢:
mysql>?EXPLAIN?SELECT?*?FROM?t1?WHERE?key1?=?'a';

可以看到ref列的值是const,表明在使用idx_key1索引執(zhí)行查詢時,與key1列作等值匹配的對象是一個常數(shù),當(dāng)然有時候更復(fù)雜一點:
mysql>?EXPLAIN?SELECT?*?FROM?t1?INNER?JOIN?t2?ON?t1.id?=?t2.id;

可以看到對被驅(qū)動表t1的訪問方法是eq_ref,而對應(yīng)的ref列的值是canal_manager.t2.id,這說明在對被驅(qū)動表進行訪問時會用到PRIMARY索引,也就是聚簇索引與一個列進行等值匹配的條件,于t2表的id作等值匹配的對象就是canal_manager.t2.id列(注意這里把數(shù)據(jù)庫名也寫出來了)。
有的時候與索引列進行等值匹配的對象是一個函數(shù),比方說下邊這個查詢
mysql>?EXPLAIN?SELECT?*?FROM?t1?INNER?JOIN?t2?ON?t2.key1?=?UPPER(t1.key1);

我們看執(zhí)行計劃的第二條記錄,可以看到對t2表采用ref訪問方法執(zhí)行查詢,然后在查詢計劃的ref列里輸出的是func,說明與t2表的key1列進行等值匹配的對象是一個函數(shù)。
Extra列
顧名思義,Extra列是用來說明一些額外信息的,我們可以通過這些額外信息來更準確的理解MySQL到底將如何執(zhí)行給定的查詢語句。
Using index
查詢的列被索引覆蓋,并且where篩選條件是索引的前導(dǎo)列,是性能高的表現(xiàn)。一般是使用了覆蓋索引(索引包含了所有查詢的字段)。對于innodb來說,如果是輔助索引性能會有不少提高
mysql>?EXPLAIN?SELECT?key1?FROM?t1?WHERE?key1?=?'a';
Using where
當(dāng)我們使用全表掃描來執(zhí)行對某個表的查詢,并且該語句的WHERE子句中有針對該表的搜索條件時,在Extra列中會提示上述額外信息。比如下邊這個查詢
mysql>?EXPLAIN?SELECT?*?FROM?t1?WHERE?name=?'a1b6cee57a';

Using where Using index
查詢的列被索引覆蓋,并且where篩選條件是索引列之一但是不是索引的前導(dǎo)列,意味著無法直接通過索引查找來查詢到符合條件的數(shù)據(jù)
mysql>?EXPLAIN?SELECT?id?FROM?t1?WHERE?key3=?'a1b6cee57a';

NULL
查詢的列未被索引覆蓋,并且where篩選條件是索引的前導(dǎo)列,意味著用到了索引,但是部分字段未被索引覆蓋,必須通過“回表”來實現(xiàn),不是純粹地用到了索引,也不是完全沒用到索引
mysql>?EXPLAIN?SELECT?*?FROM?t1?WHERE?key2=?'a1b6cee57a';

Using index condition
與Using where類似,查詢的列不完全被索引覆蓋,where條件中是一個前導(dǎo)列的范圍;
mysql>??EXPLAIN?SELECT?*?FROM?t1?WHERE?key1?like?'1';

Using temporary
在許多查詢的執(zhí)行過程中,MySQL可能會借助臨時表來完成一些功能,比如去重、排序之類的,比如我們在執(zhí)行許多包含DISTINCT、GROUP BY、UNION等子句的查詢過程中,如果不能有效利用索引來完成查詢,MySQL很有可能尋求通過建立內(nèi)部的臨時表來執(zhí)行查詢。如果查詢中使用到了內(nèi)部的臨時表,在執(zhí)行計劃的Extra列將會顯示Using temporary提示,比方說這樣:
name沒有索引,此時創(chuàng)建了張臨時表來distinct
mysql>?explain?select?distinct?name?from?t1;

key1建立了idx_key1索引,此時查詢時extra是using index,沒有用臨時表
mysql>?explain?select?distinct?key1?from?t1;

Using filesort
mysql 會對結(jié)果使用一個外部索引排序,而不是按索引次序從表里讀取行。此時mysql會根據(jù)聯(lián)接類型瀏覽所有符合條件的記錄,并保存排序關(guān)鍵字和行指針,然后排序關(guān)鍵字并按順序檢索行信息。這種情況下一般也是要考慮使用索引來優(yōu)化的。
name未創(chuàng)建索引,會瀏覽t1整個表,保存排序關(guān)鍵字name和對應(yīng)的id,然后排序name并檢索行記錄
mysql>?explain?select?*?from?t1?order?by?name;

key1建立了idx_key1索引,此時查詢時extra是using index
mysql>?explain?select?*?from?t1?order?by?key1;
Using join buffer (Block Nested Loop)
在連接查詢執(zhí)行過程中,當(dāng)被驅(qū)動表不能有效的利用索引加快訪問速度,MySQL一般會為其分配一塊名叫join buffer的內(nèi)存塊來加快查詢速度,也就是我們所講的基于塊的嵌套循環(huán)算法,比如下邊這個查詢語句:
mysql>?EXPLAIN?SELECT?*?FROM?t1?INNER?JOIN?t2?ON?t1.key3?=?t2.key3;

No tables used
當(dāng)查詢語句的沒有FROM子句時將會提示該額外信息,比如:
mysql>?EXPLAIN?SELECT?1;

Impossible WHERE
查詢語句的WHERE子句永遠為FALSE時將會提示該額外信息,比方說:
mysql>?EXPLAIN?SELECT?*?FROM?t1?WHERE?1?!=?1;

參考:
https://dev.mysql.com/doc/refman/5.7/en/explain-output.html#explain-extra-information
往期推薦
掃一掃,關(guān)注我
一起學(xué)習(xí),一起進步
每周贈書,福利不斷
﹀
﹀
﹀
深度內(nèi)容
推薦加入
最近熱門內(nèi)容回顧? ?#技術(shù)人系列

