高性能MySQL(EXPLAIN 附錄D)

調(diào)用EXPLAIN

要使用EXPLAIN吼肥,只需要在查詢的select關(guān)鍵字前面機上explain這個詞舆逃。下面是一個簡單的explain結(jié)果:

mysql> explain select 1\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: NULL
         type: NULL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: NULL
        Extra: No tables used
1 row in set (0.00 sec)

EXPLAIN有兩個主要變種重绷。EXPLAIN EXTENDED,EXPLAIN PARTITIONS。EXPLAIN 時 肯骇,SELECT語句也會實際執(zhí)行圃泡。EXPLAIN只是個近似結(jié)果碟案,以下是一些相關(guān)限制。

  • EXPLAIN不會顯示觸發(fā)器颇蜡、存儲過程价说、UDF的性能。
  • 不會顯示一個MySQL的內(nèi)置優(yōu)化
  • 不區(qū)分具有相同名字的事物风秤。例如內(nèi)存排序和臨時文件排序都使用"filesort"鳖目,對于磁盤和內(nèi)存中的臨時表都表示"Using temporary"

EXPLAIN中的列

  • id列

編號標(biāo)識SELECT所屬的行。若語句中沒有子查詢或聯(lián)合那么id應(yīng)該是相同的1缤弦。否則內(nèi)層的select語句一般會順序編號领迈,對應(yīng)其在原始語句的位置。MySQL將SELECT分為簡單和復(fù)雜兩種,復(fù)雜查詢主要三大類:簡單子查詢狸捅、from子句中的子查詢衷蜓、UNION查詢。下面是一個簡單的子查詢尘喝,

#簡單子查詢
mysql> explain select (select 1 from actor limit 1) from film;
+----+-------------+-------+-------+---------------+---------------------+---------+------+------+-------------+
| id | select_type | table | type  | possible_keys | key                 | key_len | ref  | rows | Extra       |
+----+-------------+-------+-------+---------------+---------------------+---------+------+------+-------------+
|  1 | PRIMARY     | film  | index | NULL          | idx_fk_language_id  | 1       | NULL | 1000 | Using index |
|  2 | SUBQUERY    | actor | index | NULL          | idx_actor_last_name | 137     | NULL |  200 | Using index |
+----+-------------+-------+-------+---------------+---------------------+---------+------+------+-------------+

#from子句子查詢 這是有一個der臨時表
mysql> explain select film_id from (select film_id from film) AS der;
+----+-------------+------------+-------+---------------+--------------------+---------+------+------+-------------+
| id | select_type | table      | type  | possible_keys | key                | key_len | ref  | rows | Extra       |
+----+-------------+------------+-------+---------------+--------------------+---------+------+------+-------------+
|  1 | PRIMARY     | <derived2> | ALL   | NULL          | NULL               | NULL    | NULL | 1000 | NULL        |
|  2 | DERIVED     | film       | index | NULL          | idx_fk_language_id | 1       | NULL | 1000 | Using index |
+----+-------------+------------+-------+---------------+--------------------+---------+------+------+-------------+

#union查詢的結(jié)果總是放到一個臨時表里磁浇,因為不在原生SQL中,所以id列為null
mysql> explain select 1 union all select 1;
+----+--------------+------------+------+---------------+------+---------+------+------+-----------------+
| id | select_type  | table      | type | possible_keys | key  | key_len | ref  | rows | Extra           |
+----+--------------+------------+------+---------------+------+---------+------+------+-----------------+
|  1 | PRIMARY      | NULL       | NULL | NULL          | NULL | NULL    | NULL | NULL | No tables used  |
|  2 | UNION        | NULL       | NULL | NULL          | NULL | NULL    | NULL | NULL | No tables used  |
| NULL | UNION RESULT | <union1,2> | ALL  | NULL          | NULL | NULL    | NULL | NULL | Using temporary |
+----+--------------+------------+------+---------------+------+---------+------+------+-----------------+
  • select_type

這一列顯示了對應(yīng)航是簡單還是復(fù)雜SELECT瞧省。SIMPLE 指簡單查詢扯夭,不熬擴子查詢和UNION。如果包含復(fù)雜查詢鞍匾,則最外層顯示PRIMARY,內(nèi)層部分可能顯示如下:
SUBQUERY 包含在select 查詢列中的子查詢。
DERIVED 包含在from子句的子查詢骑科,MySQL會遞歸執(zhí)行并將結(jié)果放到一個臨時表中橡淑。
UNION 在UNION中的第二個和隨后的select被標(biāo)記為union。如果union被from 子句中的子查詢包含咆爽,那第一個select會被標(biāo)記為DERIVED梁棠。
UNION RESULT 用來標(biāo)記UNION的匿名臨時表檢索結(jié)果的select。

  • table 列

這一列顯示了對應(yīng)航正在訪問表的表名或別名斗埂。如下看一看到這條SQL的查詢順序符糊,先關(guān)聯(lián)actor&film_actor,再關(guān)聯(lián)actor

mysql> explain select film.film_id from film inner join film_actor using(film_id) inner join actor using(actor_id);
+----+-------------+------------+--------+------------------------+---------------------+---------+---------------------------+------+-------------+
| id | select_type | table      | type   | possible_keys          | key                 | key_len | ref                       | rows | Extra       |
+----+-------------+------------+--------+------------------------+---------------------+---------+---------------------------+------+-------------+
|  1 | SIMPLE      | actor      | index  | PRIMARY                | idx_actor_last_name | 137     | NULL                      |  200 | Using index |
|  1 | SIMPLE      | film_actor | ref    | PRIMARY,idx_fk_film_id | PRIMARY             | 2       | sakila.actor.actor_id     |   13 | Using index |
|  1 | SIMPLE      | film       | eq_ref | PRIMARY                | PRIMARY             | 2       | sakila.film_actor.film_id |    1 | Using index |
+----+-------------+------------+--------+------------------------+---------------------+---------+---------------------------+------+-------------+

派生表和聯(lián)合呛凶。FROM子句有子查詢時男娄,table列時<derivedN>的形式,其中N是子查詢的id,指向下一個查詢的行漾稀。
當(dāng)有UNION時模闲,UNION RESULT的table包含所有參與UNION的查詢行id。下面我們來看一個復(fù)雜的查詢例子,:

mysql> explain select actor_id, 
(select 1 from film_actor where film_actor.actor_id = der_1.actor_id limit 1) 
from  
(select actor_id from actor limit 5) as der_1 
union all select film_id, 
(select @var1 from rental limit 1) 
from 
(select film_id,
(select 1 from store limit 1) from film limit 5) as der_2;
+----+----------------------+------------+-------+---------------+---------------------+---------+----------------+-------+-----------------+
| id | select_type          | table      | type  | possible_keys | key                 | key_len | ref            | rows  | Extra           |
+----+----------------------+------------+-------+---------------+---------------------+---------+----------------+-------+-----------------+
|  1 | PRIMARY              | <derived3> | ALL   | NULL          | NULL                | NULL    | NULL           |     5 | NULL            |
|  3 | DERIVED              | actor      | index | NULL          | idx_actor_last_name | 137     | NULL           |   200 | Using index     |
|  2 | DEPENDENT SUBQUERY   | film_actor | ref   | PRIMARY       | PRIMARY             | 2       | der_1.actor_id |    13 | Using index     |
|  4 | UNION                | <derived6> | ALL   | NULL          | NULL                | NULL    | NULL           |     5 | NULL            |
|  6 | DERIVED              | film       | index | NULL          | idx_fk_language_id  | 1       | NULL           |  1000 | Using index     |
|  7 | SUBQUERY             | store      | index | NULL          | idx_unique_manager  | 1       | NULL           |     2 | Using index     |
|  5 | UNCACHEABLE SUBQUERY | rental     | index | NULL          | idx_fk_staff_id     | 1       | NULL           | 16008 | Using index     |
| NULL | UNION RESULT         | <union1,4> | ALL   | NULL          | NULL                | NULL    | NULL           |  NULL | Using temporary |
+----+----------------------+------------+-------+---------------+---------------------+---------+----------------+-------+-----------------+

#從上往下看崭捍,第一行id=1 table=<derived3> 指向id=3 的行建立的臨時表尸折,即原SQL 第一行從臨時表der_1查詢部分;
#第二行id=3 ,嵌套在from 子句中的子查詢派生表殷蛇,所以select_tyoe=DERIVED, 即原SQL 第四行 建立der_1臨時表這部分 实夹;
#第三行id=2,DEPENDENT SUBQUERY依賴子查詢粒梦,這里即原SQL中第二行依賴der_1即id=3的子查詢派生表,所以她在id=3更高的行后面亮航,暗示了在第二行后面執(zhí)行;
#第四行id=4為UNION谍倦,意味著是原生SQL中UNION 后的第二個或之后的select塞赂,即原SQL第六行檢索@var1這部分,table=<derived6>指向id=6的DRIVERD行;
#第五行id=6在原生SQL中第九行昼蛀,定義der_2臨時表的派生表子查詢宴猾;
#第六行id=7 是一個簡單子查詢SUBQUERY圆存,原生SQL定義der_2的select store 部分,另外因為下面一行id=5,所以id=6的DERIVED嵌套子查詢到這一行結(jié)束;
#第七行id=5即原生SQL有用戶變量@var1部分仇哆,這里已經(jīng)不是派生表嵌套子查詢部分沦辙,而逝檢索派生表部分;
#最后一行UNION RESULT表明了從臨時表中讀取行的讹剔,這里1 ,4說明是讀取id=1,4的行的臨時表即<derived3>油讯、<derived6> 
  • type列

訪問類型,換言之就是MySQL如何查找表中的行延欠。
ALL: 俗稱的全表掃描陌兑,通常意味著MySQL必須掃描整張表,去找到需要的行由捎。如果查詢中使用了LIMIT關(guān)鍵字兔综,或者在Extra列中顯示"Using distinct/not exists"。
index: 全索引掃描狞玛,跟全表掃描一樣软驰,不過是按索引次序進(jìn)行而不是行順序。優(yōu)點是避免了排序心肪,缺點是要按照索引次序回表讀取的開銷锭亏,因為此時回表很可能是隨機訪問,開銷非常大硬鞍。不過如果Extra列顯示的是Using index慧瘤,說明使用了覆蓋索引,只掃描索引的數(shù)據(jù)膳凝,沒有回表操作碑隆,開銷要大大減少。
range: 范圍掃描是一個有限制的索引掃描它開始于索引里的某一點蹬音,返回匹配這個值域的行上煤,這筆全索引掃描好一些,因為用不著全部遍歷著淆,顯然是帶有BETWEEN或WHERE 子句帶>的查詢劫狠。MySQL中使用索引去查一系列值時,IN()或OR列表永部,也會顯示為范圍查詢独泞,但這兩者其實是不同的訪問類型。
ref: 索引訪問苔埋,它返回所有匹配某個單個值的行懦砂,可能會找到多個符合條件的行。此類索引訪問只有使用非唯一索引或者唯一性索引的非唯一性前綴時才會發(fā)生。取名ref因為索引要跟某個參考值作比較荞膘,這個參考值或者是一個常數(shù)或者是來自多表查詢里一個表的結(jié)果值罚随。ref_or_null是ref的一個變體,它表示MySQL必須在初次查找的結(jié)果中查找出NULL條目羽资。
eq_ref: 對多只返回一條記錄的索引查找淘菩。在使用主鍵或者唯一性索引查找時看到,它會將索引與某個參考值作比較屠升。
const, system: 當(dāng)MySQL能對查詢的某部分進(jìn)行優(yōu)化并轉(zhuǎn)換成一個常量時潮改,就會使用這些訪問類型。如將主鍵置于where列表中腹暖,MySQL就能將該查詢轉(zhuǎn)換為一個常量,system是const類型的特例汇在,當(dāng)查詢的表只有一行的情況下,使用system脏答。
NULL: 這種訪問方式意味著MySQL能在優(yōu)化階段分解語句趾疚,在執(zhí)行階段甚至用不著在訪問表或者索引。例如以蕴,從一個索引列里選取最小值可以單獨通過查找索引來完成。

  • possible_keys

這一列顯示了查詢可以使用那些索引辛孵。

  • key列

這一列顯示了MySQL決定采用哪個索引來優(yōu)化對該表的訪問丛肮。如果該索引沒有出現(xiàn)在poosible_keys中,那么選用它是出于另外的原因魄缚,可能是選擇了覆蓋索引宝与。
換句話說,possible_keys揭示了哪一個索引能有助于高效的行查找冶匹,key顯示的是優(yōu)化采用什么索引減少查詢成本习劫。

  • key_len列

該列顯示了MySQL在索引里使用的的字節(jié)數(shù)。

  • ref列

這一列顯示了之前的表在key列記錄的索引中查找值所用的列或常量嚼隘。

  • rows列

這一列是MySQL為了找到所需的行而要讀取的行數(shù)诽里,注意并不是結(jié)果集里的行數(shù)。

  • filtered列

顯示的是針對表里符合某個調(diào)價你的記錄數(shù)的百分比悲觀估算飞蛹。

  • Extra

"Using index" 表示MySQL將使用覆蓋索引谤狡,以避免表訪問。
"Using where" 表示MySQL服務(wù)器將在存儲引擎檢索行后在進(jìn)行過濾卧檐。有些WHERE條件里涉及索引當(dāng)中的列墓懂,存儲引擎讀取索引時就可以進(jìn)行校驗,此時便不會顯示Using Where霉囚。
"Using temporary" 這意味MySQL在對結(jié)果排序時會使用一個臨時表捕仔。
"Using filesort" 這意味著MySQL會對結(jié)果使用一個外部索引排序,而不是按照索引次序。
"Range checked for each record(index map:N)" 這個值意味著沒有好用的索引榜跌,新的索引在連接的每一行上重新估算闪唆。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市斜做,隨后出現(xiàn)的幾起案子苞氮,更是在濱河造成了極大的恐慌,老刑警劉巖瓤逼,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件笼吟,死亡現(xiàn)場離奇詭異,居然都是意外死亡霸旗,警方通過查閱死者的電腦和手機贷帮,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來诱告,“玉大人撵枢,你說我怎么就攤上這事【樱” “怎么了锄禽?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長靴姿。 經(jīng)常有香客問我沃但,道長,這世上最難降的妖魔是什么佛吓? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任宵晚,我火速辦了婚禮,結(jié)果婚禮上维雇,老公的妹妹穿的比我還像新娘淤刃。我一直安慰自己,他們只是感情好吱型,可當(dāng)我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布逸贾。 她就那樣靜靜地躺著,像睡著了一般唁影。 火紅的嫁衣襯著肌膚如雪耕陷。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天据沈,我揣著相機與錄音哟沫,去河邊找鬼。 笑死锌介,一個胖子當(dāng)著我的面吹牛嗜诀,可吹牛的內(nèi)容都是我干的猾警。 我是一名探鬼主播,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼隆敢,長吁一口氣:“原來是場噩夢啊……” “哼发皿!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起拂蝎,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤穴墅,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后温自,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體玄货,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年悼泌,在試婚紗的時候發(fā)現(xiàn)自己被綠了松捉。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡馆里,死狀恐怖隘世,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情鸠踪,我是刑警寧澤丙者,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站营密,受9級特大地震影響蔓钟,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜卵贱,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望侣集。 院中可真熱鬧键俱,春花似錦、人聲如沸世分。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽臭埋。三九已至踪央,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間瓢阴,已是汗流浹背畅蹂。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留荣恐,地道東北人液斜。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓累贤,卻偏偏與公主長得像,于是被迫代替她去往敵國和親少漆。 傳聞我的和親對象是個殘疾皇子臼膏,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,901評論 2 345

推薦閱讀更多精彩內(nèi)容