使用EXPLAIN關鍵字可以模拟優化器執行SQL語句,分析你的查詢語句或是結構的性能瓶頸 在 select 語句之前增加 explain 關鍵字,MySQL 會在查詢上設定一個标記,執行查詢會返 回執行計劃的資訊,而不是執行這條SQL
注意:如果 from 中包含子查詢,仍會執行該子查詢,将結果放入臨時表中
比如存在以下三個表:

一、簡單用法+介紹
explain select * from actor;
在查詢中的每個表會輸出一行,如果有兩個表通過 join 連接配接查詢,那麼會輸出兩行
1.1 explain 兩個變種
1)explain extended
會在 explain 的基礎上額外提供一些查詢優化的資訊。(5.7版本之後可以直接使用explain即可)
filtered 列,是一個半分比的值,rows * filtered/100 可以估算出将要和 explain 中前一個表 進行連接配接的行數(前一個表指 explain 中的id值比目前表id值小的表)。
比如現在是單表查詢,filtered是100,那麼估算出來的行數就是3*100/100,也就是本表的行數
explain extended和過show warnings 指令可以得到優化後的查詢語句,進而看出優化器優化了什麼。
注意:show warnings指令獲得優化後的SQL可以說是僞SQL,有時可以執行,有時不可以執行,因為不是真正的SQL
2)explain partitions
相比 explain 多了個 partitions 字段,如果查詢是基于分區表的話,會顯示查詢将通路的分區。(5.7版本之後可以直接使用explain即可)
一般網際網路公司很少用到分區,要用一般用分庫分表
二、explain中的列
2.1 id列
id列的編号是 select 的序列号,不一定是唯一的,有幾個 select 就有幾個id,并且id的順序是按 select 出現的順序增長的。
id列越大執行優先級越高,id相同則從上往下執行,id為NULL最後執行。
2.2 select_type列
select_type 表示對應行是簡單還是複雜的查詢。
simple
簡單查詢。查詢不包含子查詢和union
primary
複雜查詢中最外層的 select
subquery
包含在 select 中的子查詢(不在 from 子句中)
derived
包含在 from 子句中的子查詢。MySQL會将結果存放在一個臨時表中,也稱為 派生表(derived的英文含義)
union
在 union 中的第二個和随後的 select
2.3 table列
這一清單示 explain 的一行正在通路哪個表。
當 from 子句中有子查詢時,table列是 <derivenN>格式,表示目前查詢依賴 id=N 的查 詢,于是先執行 id=N 的查詢。
當有 union 時,UNION RESULT 的 table 列的值為<union1,2>,1和2表示參與 union 的 select 行id
2.4 type列
這一清單示關聯類型或通路類型,即MySQL決定如何查找表中的行,查找資料行記錄的大概範圍。
依次從最優到最差分别為:system > const > eq_ref > ref > range > index > ALL
一般來說,得保證查詢達到range級别,最好達到ref
NULL
mysql能夠在優化階段分解查詢語句,在執行階段用不着再通路表或索引(特别是針對MySQL的函數),性能是非常非常高的,但是很少有這種情況
例如:在索引列中選取最小值,可以單獨查找索引來完成,不需要在執行時通路表
為什麼會這樣呢?前面我們詳細提到過索引的結構(詳細可進入深入了解Mysql索引底層資料結構與算法檢視),id是主鍵索引,而索引在底層的B+ Tree中是有序排列的,如果現在求最小值,那麼直接在索引樹中找到第一個值即為最終要查詢的結果
const, system
mysql能對查詢的某部分進行優化并将其轉化成一個常量(可以看show warnings 的結果)。
用于 primary key (主鍵索引)或 unique key(唯一索引) 的所有列與常數比較時,是以表最多有一個比對行,讀取1次,速度比較快。
system是const的特例,表裡隻有一條記錄比對時為 system
我們可以用 show warnings;來看下MySQL優化後的結果
eq_ref
primary key(主鍵索引) 或 unique key 索引(唯一索引)的所有部分被連接配接使用 ,最多隻會傳回一條符合條件的記錄。這可能是在 const 之外最好的聯接類型了,簡單的 select 查詢不會出現這種 type。
直白的說:如果有表關聯,查詢或者關聯的時候用的是主鍵或唯一索引這種類型就叫eq_ref(eq其實就是equals),這種查詢也是非常快的
如果SQL語句,通過explain最終展示出的type是eq_ref,不用太多優化,沒有太多可優化的空間
ref
相比 eq_ref,不使用唯一索引,而是使用普通索引或者唯一性索引的部分字首,索引要和某個值相比較,可能會找到多個符合條件的行。
簡單 select 查詢,name是普通索引(非唯一索引)
關聯表查詢,idx_film_actor_id是film_id和actor_id的聯合索引,這裡使用到了film_actor 的左邊字首film_id部分。
range
範圍掃描通常出現在 in(), between ,> ,<, >= 等操作中。使用一個索引來檢索給定範圍的行。
range的效率也算還可以,用了索引,但最終是一個範圍查找,如果是大于某個值需要在索引樹中把後面對應的資料全部找出來。
雖然使用了索引,但是如果結果集太大,效率也不會很高,是以,對于結果集進行一些優化,比如分頁等。
index
掃描全表索引,這通常比ALL快一些。
掃描全索引就能拿到結果,一般是掃描某個二級索引(非主鍵索引),這種掃描不會從索引樹根節點開始快速查找,而是直接對二級索引樹的葉子節點進行掃描,速度還是比較慢的,這種查詢一般為使用覆寫索引,二級索引一般比較小,是以這種通常比All快一些。
上述SQL語句執行的時候明明有兩個索引,為什麼真正插叙的時候走了二級索引而不是主鍵索引,主鍵索引不是更快嗎??
從圖中看出film表有兩個字段,在普通的索引樹中存在着索引字段和主鍵的ID。mysql内部有這麼一個優化原則,凡是通過查找結果集的分析,查找出來結果集的所有字段在任何索引樹中都有,會優先選用二級索引樹來使用,因為二級索引相對于主鍵索引會小很多(主鍵索引葉子節點存儲的是所用的行資料,二級索引存儲的是對應的主鍵)。
ps:index雖然使用到了索引,但是性能不高。類似上述這種查詢( select * from film;),是不推薦的,至少要加個條件,盡量不要把所有的資料都查出來,在Java裡面,如果資料量比較大就可能直接挂掉。就二級索引樹的結構,雖然index是用到了索引,但是其實是在周遊索引,這些索引也是在磁盤上的,在周遊的過程中也是需要做IO的,是以效率是不高的
index類型的查詢一般是需要優化的。
ALL
即全表掃描,意味着mysql需要從頭到尾去查找所需要的行。通常情況下這需要增加索 引來進行優化了
All類型的查詢,底層實際上是全表掃描主鍵索引(聚簇索引,即ibd檔案)的所有葉子節點(從第一個節點開始掃描到最後一個節點)。
ps:index類型比All類型的查詢效率要高一點,index掃描的是二級索引(非主鍵索引),而all掃描的是主鍵索引,由于主鍵索引的檔案比二級索引的檔案要大,是以掃描二級索引的效率要高一點
2.5 possible_keys列
這一列顯示查詢可能使用哪些索引來查找。
explain 時可能出現 possible_keys 有列,而 key 顯示 NULL 的情況,這種情況是因為表中資料不多,mysql認為索引對此查詢幫助不大,選擇了全表查詢。
如果該列是NULL,則沒有相關的索引。在這種情況下,可以通過檢查 where 子句看是否可 以創造一個适當的索引來提高查詢性能,然後用 explain 檢視效果。
2.6 key列
這一列顯示mysql實際采用哪個索引來優化對該表的通路。 如果沒有使用索引,則該列是 NULL。
如果想強制mysql使用或忽視possible_keys列中的索 引,在查詢中使用 force index、ignore index。
2.7 key_len列
這一列顯示了mysql在索引裡使用的位元組數,通過這個值可以算出具體使用了索引中的哪些列
特别是聯合索引的情況下,由于聯合索引不可能所有字段都用到,,大部分情況下都是用了部分字段,可以通過key_len來判斷用到了聯合索引的哪些字段
key_len計算規則如下:
字元串char(n):n位元組長度
varchar(n):2位元組存儲字元串長度,如果是utf-8,則長度 3n + 2
數值類型tinyint:1位元組
smallint:2位元組
int:4位元組
bigint:8位元組
時間類型date:3位元組
timestamp:4位元組
datetime:8位元組
如果字段允許為 NULL,需要1位元組記錄是否為 NULL
索引最大長度是768位元組,當字元串過長時,mysql會做一個類似左字首索引的處理,将前半 部分的字元提取出來做索引。
2.8 ref 列
這一列顯示了在key列記錄的索引中,表查找值所用到的列或常量,常見的有:const(常 量),字段名(例:film.id)
簡單說就是索引關聯查詢的字段是哪個或者那些列
2.9. rows 列
這一列是mysql估計要讀取并檢測的行數,注意這個不是結果集裡的行數。
mysql通過執行計劃explain大概計算下,可能要掃描多少行,但是rows的資料不代表最終查詢的結果就是那麼多行,隻是一個預估值。
2.10 Extra列
這一列展示的是額外資訊。常見的重要值如下:
Using index:使用覆寫索引
覆寫索引的定義:不是一種索引,而是一種查詢的方式。
mysql執行計劃explain結果裡的key有走索引,如果select後面查詢的字段都可以從這個索引的樹中擷取,這種情況一般可以說是用到了覆寫索引,extra裡一般都有using index;覆寫索引一般針對輔助索引,整個查詢結果隻通過輔助索引就能拿到結果,不需要通過輔助索引樹(即非主鍵索引)找到主鍵,再通過主鍵去主鍵索引樹裡擷取其他字段值。
簡單說就是SQL中查詢的所有字段在被使用到的索引所對應那顆索引樹全部覆寫到了(不能跨其他的索引樹,隻能是目前使用到的這顆索引樹),隻需要通過索引樹就可以查到結果集。
Extra結果裡面有using index比NULL的效率要高一些,因為不用再回表查詢,直接在二級索引樹中就可找到需要的所有字段。
type類型為index就是一種覆寫索引掃描。
Using where
使用 where 語句來處理結果,查詢的列未被索引覆寫
沒有走索引,就是我們很普通的查詢這種情況一般都是需要優化的可以加個索引
Using index condition
查詢的列不完全被索引覆寫,where條件中是一個前導列的範圍
Using temporary
mysql需要建立一張臨時表來處理查詢。
出現這種情況一般是要進行優化的,首先是想到用索引來優化(優化成Using index)
actor.name沒有建立索引,是以是先把查出的結果集加載到記憶體中,然後在記憶體表中去重
film.name建立了索引,,在查出過程中,掃描索引的時候,mysql就幫忙把去重實作了
索引樹是有序的,掃描到相同資料是就不要繼續掃描
Using filesort
将用外部排序而不是索引排序,資料較小時從記憶體排序,否則需要在磁盤完成排序。
這種情況下一般也是要考慮使用索引來優化的。
actor.name未建立索引,會掃描actor整個表,儲存排序關鍵字name和對應的id,然後排序name并檢索行記錄。
結果集較小時在記憶體中進行排序;結果集較大時将在磁盤中進行排序
film.name建立了idx_name索引,此時查詢時extra是using index
由于索引樹是有序排列的,是以直接拿出即可,不用再次排序
Select tables optimized away
使用某些聚合函數(比如 max、min)來通路存在索引 的某個字段
MySQL在執行SQL之前内部優化的時候,檢測到主鍵,由于索引樹是有序的,是以,其實還未執行SQL,結果集已經出來了
mysql具體實踐可前往進行索引最佳實踐具體執行個體檢視