<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          什么是MySQL的執(zhí)行計劃(Explain關(guān)鍵字)?

          共 6846字,需瀏覽 14分鐘

           ·

          2020-10-17 03:16

          本文作者王良辰,京東中臺架構(gòu)師,擅長分布式系統(tǒng)及高可用、高并發(fā)系統(tǒng)架構(gòu)與設(shè)計。曾經(jīng)為企業(yè)開發(fā)過多個通用腳手架,推崇以技術(shù)手段提升開發(fā)效率、約束開發(fā)行為。


          什么是Explain


          Explain被稱為執(zhí)行計劃,在語句之前增加 explain 關(guān)鍵字,MySQL 會在查詢上設(shè)置一個標(biāo)記,模擬MySQL優(yōu)化器來執(zhí)行SQL語句,執(zhí)行查詢時,會返回執(zhí)行計劃的信息,并不執(zhí)行這條SQL。(注意,如果 from 中包含子查詢,仍會執(zhí)行該子查詢,將結(jié)果放入臨時表中)。


          Explain可以用來分析SQL語句和表結(jié)構(gòu)的性能瓶頸。通過explain的結(jié)果,可以了解到如數(shù)據(jù)表的查詢順序、數(shù)據(jù)查詢操作的操作類型、哪些索引可以被命中、哪些索引實際會命中、每個數(shù)據(jù)表有多少行記錄被查詢等信息。



          Explain命令擴展


          explain extended


          在explain的基礎(chǔ)上提供一些額外的查詢信息,在explian extended執(zhí)行以后,通過show warnings命令可以得到優(yōu)化后的查詢語句,可以看出優(yōu)化器做了哪些工作,還可以通過某些數(shù)據(jù)估算表連接的行數(shù)。


          explain partitions


          用于分析使用了分區(qū)的表,會顯示出可能用到的分區(qū)。



          兩點重要提示


          1. Explain結(jié)果是基于數(shù)據(jù)表中現(xiàn)有數(shù)據(jù)的。


          2. Explain結(jié)果與MySQL版本有很大的關(guān)系,不同版本的優(yōu)化器的優(yōu)化策略不同。

          ?


          本文示例使用的數(shù)據(jù)庫表




          Explain命令(關(guān)鍵字)


          explain簡單示例

          mysql>explain select * from t_user;


          在查詢中的每個”表”會輸出一行,這里的“表”的意義非常廣泛,不僅僅是數(shù)據(jù)庫表,還可以是子查詢、一個union 結(jié)果等。


          explain結(jié)果列說明


          【id列】


          id列是一個有順序的編號,是查詢的順序號,有幾個 select 就顯示幾行。id的順序是按 select 出現(xiàn)的順序增長的。id列的值越大執(zhí)行優(yōu)先級越高越先執(zhí)行,id列的值相同則從上往下執(zhí)行,id列的值為NULL最后執(zhí)行。


          【select_type列】


          select_type列的值標(biāo)明查詢的類型:


          1)simple:表明當(dāng)前行對應(yīng)的select是簡單查詢,不包含子查詢和union


          2)primary:表明當(dāng)前行對應(yīng)的select是復(fù)雜查詢中最外層的 select


          3)subquery:表明當(dāng)前行對應(yīng)的select是包含在 select 中的子查詢(不在 from 子句中)


          4)derived:表明當(dāng)前行對應(yīng)的select是包含在 from 子句中的子查詢。


          MySQL會創(chuàng)建一個臨時表來存放子查詢的查詢結(jié)果。用如下的語句示例說明:


          explain select (select 1 fromt_user where user_id=1) from (select * from t_group where group_id=1) tmp;



          *注意,在資料收集過程中,發(fā)現(xiàn)不同版本的MySQL表現(xiàn)不一致,經(jīng)反復(fù)對比,5.7及以后版本的輸出如下:



          很顯然,MySQL在這方面進(jìn)行了優(yōu)化.


          *注意,MySQL不同版本Explain表現(xiàn)差異很大,有些場景,從語句層面看,是要使用到索引,但經(jīng)過優(yōu)化器分析,結(jié)合表中現(xiàn)有數(shù)據(jù),如果MySQL認(rèn)為全表掃描性能更優(yōu),則會使用全表掃描。


          5)union:表明當(dāng)前行對應(yīng)的select是在 union 中的第二個和隨后的 select


          6)union result:表明當(dāng)前行對應(yīng)的select是從 union 臨時表檢索結(jié)果的 select


          explain select 1 union all select 2 fromdual;



          ?????? MySQL5.7及以后同樣做了優(yōu)化



          【table列】


          table列的結(jié)果表明當(dāng)前行對應(yīng)的select正在訪問哪個表。當(dāng)查詢的子句中有子查詢時,table列是??格式,表示當(dāng)前的select依賴 id=N結(jié)果行對應(yīng)的查詢,要先執(zhí)行 id序號=N 的查詢。當(dāng)存在 union 時,UNION RESULT 的 table 列的值為,N1和N2表示參與 union 的select 行的id序號。


          【type列】


          type列的結(jié)果表明當(dāng)前行對應(yīng)的select的關(guān)聯(lián)類型或訪問類型,也就是優(yōu)化器決定怎么查找數(shù)據(jù)表中的行,以及查找數(shù)據(jù)行記錄的大概范圍。該列的取值優(yōu)化程度的優(yōu)劣,從最優(yōu)到最差依次為:null>system> const > eq_ref > ref > range > index > ALL。一般來說,要保證查詢達(dá)到range級別,最好達(dá)到ref。


          1)null,MySQL優(yōu)化器在優(yōu)化階段分解查詢語句,在優(yōu)化過程中就已經(jīng)可以得到結(jié)果,那么在執(zhí)行階段就不用再訪問表或索引。

          explain select min(user_id) from t_user;



          這時的函數(shù)min,在索引列user_id中選取最小值,可以直接查找索引來完成,不需要執(zhí)行時再訪問數(shù)據(jù)表。


          2)const和system:const出現(xiàn)在用 primary key(主鍵) 或 unique key(唯一鍵) 的所有列與常數(shù)比較時,優(yōu)化器對查詢進(jìn)行優(yōu)化并將其部分查詢轉(zhuǎn)化成一個常量。最多有一個匹配行,讀取1次,速度非常快。而system是const的特例,表中數(shù)據(jù)只有一條匹配時為system。此時可以用explain extended+show warnings查看執(zhí)行結(jié)果。


          explain extended select * from (select * from t_user where user_id = 1) tmp;


          show warnings;




          MySQL5.7及以后版本優(yōu)化后:




          3)eq_ref:primary key(主鍵)或 unique key(唯一鍵) 索引的所有構(gòu)成部分被join使用 ,只會返回一條符合條件的數(shù)據(jù)行。這是僅次于const的連接類型。


          explain select * from t_group_user gu left join t_group g ong.group_id = gu.group_id;



          4) ref:與eq_ref相比,ref類型不是使用primary key(主鍵) 或 unique key(唯一鍵)等唯一索引,而是使用普通索引或者聯(lián)合唯一性索引的部分前綴,索引和某個值相比較,可能會找到符合條件的多個數(shù)據(jù)行。


          1. 如下示例,使用的group_name是普通索引


          explain select * from t_group where group_name= 'group1';



          2.關(guān)聯(lián)表查詢


          explain select g.group_id from t_group gleft join t_group_user gu on gu.group_id = g.group_id;



          5)range:出現(xiàn)在 in(),between ,> ,<, >= 等操作符中。使用一個索引來查詢給定范圍的行。


          6)index:掃描全表索引(index是從索引中讀取的,所有字段都有索引,而all是從硬盤中讀取),比ALL要快。


          explain select * from t_group;



          7)all:即全表掃描,需要從頭到尾去查找所需要的行。一般這種情況下這需要增加索引來進(jìn)行查詢優(yōu)化了


          explain select * from t_user;



          【possible_keys列】


          這一列的結(jié)果表明查詢可能使用到哪些索引。但有些時候也會出現(xiàn)出現(xiàn)possible_keys 列有結(jié)果,而 后面的key列顯示 null 的情況,這是因為此時表中數(shù)據(jù)不多,優(yōu)化器認(rèn)為查詢索引對查詢幫助不大,所以沒有走索引查詢而是進(jìn)行了全表掃描。?


          如果possible_keys列的結(jié)果是null,則表明沒有相關(guān)的索引。這時,可以通過優(yōu)化where子句,增加恰當(dāng)?shù)乃饕齺硖嵘樵冃阅堋?/span>


          【key列】


          這一列表明優(yōu)化器實際采用哪個索引來優(yōu)化對該表的訪問。如果沒有使用索引,則該列是 null。


          【key_len列】


          這一列表明了在索引里使用的字節(jié)數(shù),通過這個值可以大致估算出具體使用了聯(lián)合索引中的前幾個列。?

          key_len計算規(guī)則這里不再贅述,不同的數(shù)據(jù)類型所占的字節(jié)數(shù)是不一致的。


          【ref列】

          這一列表明了在key列記錄的索引中,表查找值所用到的列或常量,常見的有:const(常量),字段名,如user.user_id


          【rows列】


          這一列表明優(yōu)化器大概要讀取并檢測的行數(shù)。跟實際的數(shù)據(jù)行數(shù)大部分情況是不一致的。


          【Extra列】


          顧名思義,這一列表明的是額外信息,這一列的取值對優(yōu)化SQL非常有參考意義。常見的重要取值如下:?


          1)using index:所有被查詢的字段都是索引列(稱為覆蓋索引),并且where條件是索引的前導(dǎo)列,出現(xiàn)這樣的結(jié)果,是性能高的表現(xiàn)。

          explainselect group_id,group_name from t_group;



          2)using where:被查詢的列未被索引覆蓋,where條件也并非索引的前導(dǎo)列,表示 MySQL 執(zhí)行器從存儲引擎接收到查詢數(shù)據(jù),再進(jìn)行“后過濾”(Post-filter)。所謂“后過濾”,就是先讀取整行數(shù)據(jù),再檢查此行是否符合 where 句的條件,符合就留下,不符合便丟棄。

          explain select * from t_user whereuser_name='user1';



          3)using where Using index:被查詢的列被索引覆蓋,并且where條件是索引列之一但是不是索引的前導(dǎo)列,也就是沒有辦法直接通過索引來查詢到符合條件的數(shù)據(jù)

          explain select * from t_group where group_name = 'group1';



          4)null:被查詢的列沒有被索引覆蓋,但where條件是索引的前導(dǎo)列,此時用到了索引,但是部分列未被索引覆蓋,必須通過“回表查詢”來實現(xiàn),不是純粹地用到了索引,也不是完全沒用到索引

          explain select * from t_user where user_id='1';



          5)using index condition:與using where類似,查詢的列不完全被索引覆蓋,where條件中是一個前導(dǎo)列的范圍;這種情況未能通過示例顯現(xiàn),可能跟MySQL版本有關(guān)系。


          6) using temporary:這表明需要通過創(chuàng)建臨時表來處理查詢。出現(xiàn)這種情況一般是要進(jìn)行優(yōu)化的,用索引來優(yōu)化。創(chuàng)建臨時表的情況:distinct,group by,orderby,子查詢等


          explain select distinct user_name from t_user;



          explain select distinct group_name fromt_group; --group_name是索引列



          7) usingfilesort:在使用order by的情況下出現(xiàn),mysql會對結(jié)果使用一個外部索引排序,而不是按索引次序從表里讀取行。此時mysql會根據(jù)聯(lián)接類型瀏覽所有符合條件的記錄,并保存排序關(guān)鍵字和行指針,然后排序關(guān)鍵字并按順序檢索行信息。這種情況下要考慮使用索引來優(yōu)化的。


          explain select * from t_user orderby user_name;



          explain select * from t_group order bygroup_name;? --group_name是索引列




          查詢優(yōu)化建議


          結(jié)合前面的描述,首先看 type列的結(jié)果,如果有類型是 all 時,表示預(yù)計會進(jìn)行全表掃描(fulltable scan)。通常全表掃描的代價是比較大的,建議創(chuàng)建適當(dāng)?shù)乃饕ㄟ^索引檢索避免全表掃描。


          再來看下 Extra 列的結(jié)果,如果有出現(xiàn) Using temporary 或者 Using filesort 則要多加關(guān)注:

          Using temporary,表示需要創(chuàng)建臨時表以滿足需求,通常是因為GROUP BY的列沒有索引,或者GROUP BY和ORDER BY的列不一樣,也需要創(chuàng)建臨時表,建議添加適當(dāng)?shù)乃饕?/span>

          Using filesort,表示無法利用索引完成排序,也有可能是因為多表連接時,排序字段不是驅(qū)動表中的字段,因此也沒辦法利用索引完成排序,建議添加適當(dāng)?shù)乃饕?/span>

          Using where,通常是因為全表掃描或全索引掃描時(type 列顯示為 ALL 或index),又加上了WHERE條件,建議添加適當(dāng)?shù)乃饕?/span>



          索引使用情況分析


          數(shù)據(jù)庫表


          主鍵索引:demo_id

          聯(lián)合索引:c1,c2,c3



          實例說明


          實例一:

          explain select * from t_demo where c1='d1'and c2='d2' and c3='d3';

          explain select * from t_demo where c2='d2'and c1='d1' and c3='d3';

          explain select * from t_demo where c3='d3'and c1='d1' and c2='d3';



          幾個Sql表現(xiàn)一致

          type=ref,ref=const,const,const

          執(zhí)行常量等值查詢時,改變索引列的順序并不會更改explain的執(zhí)行結(jié)果,優(yōu)化器會進(jìn)行優(yōu)化,推薦按照索引順序列編寫sql語句。


          實列二:

          explain select * from t_demo where c1='d1'and c2>'d2' and c3='d3';



          explain select * from t_demo where c1='d1'and c3>'d3' and c2='d2';



          第一個例子范圍右側(cè)索引失效,使用到了兩個索引。

          第二個例子,由于優(yōu)化器優(yōu)化的原因,使用到了全部的三個索引。


          實例三:

          explain select * from t_demo wherec1>'c' and c2='d2' and c3='d3';



          explain select * from t_demo wherec1>'e' and c2='d2' and c3='d3';



          從上面兩個實例可以發(fā)現(xiàn),同樣使用最左的索引列范圍查詢,有些情況未用到索引,做了全表掃描(第一個例子);有些情況使用到了索引(第二個例子)。


          經(jīng)反復(fù)驗證,發(fā)現(xiàn)如下規(guī)律(不一定可靠),也可能與數(shù)據(jù)的第一行或最小值相關(guān)。

          1. 跟存儲的數(shù)據(jù)有關(guān)

          2. 在大于條件下,如果條件數(shù)據(jù)小于列數(shù)據(jù),則索引無效;如果條件數(shù)據(jù)大于列數(shù)據(jù),則索引有效;


          在設(shè)計查詢條件時,請注意規(guī)避。

          針對第一個例子,可以采用覆蓋索引的方式優(yōu)化。


          實例四:

          explain select * from t_demo where c1='d1'and c2='d2' order by c3;


          explain select * from t_demo where c1='d1'order by c3;


          explain select * from t_demo where c1='d1'and c3='d3' order by c2;


          order by排序使用到索引和沒使用到索引的情況


          實例五:

          explain select * from t_demo where c1='d1'and c4='d4' order by c1,c2;


          條件列包含沒有索引的列,出現(xiàn)了Using filesort


          實例六:

          explain select * from t_demo where c1='d1'and c4='d4' group by c1,c2;


          性能非常差的場景,同時出現(xiàn)了Using temporary和Using filesort



          總結(jié)


          1. 兩種方式的排序filesort和index,Usingindex是指MySQL掃描索引本身完成排序。index效率高,filesort效率低。


          2. order by滿足兩種情況會使用Using index。

          1)order by語句使用索引最左前列。

          2)使用where子句與order by子句條件列組合滿足索引最左前列。


          3. 盡量在索引列上完成排序,遵循索引建立(索引創(chuàng)建的順序)時的最佳左前綴法則。


          4. group by與order by很類似,都是先排序后分組,遵照索引創(chuàng)建順序的最佳左前綴法則。



          —————END—————


          掃碼關(guān)注公眾號,訂閱更多精彩內(nèi)容。



          你點的每個贊,我都認(rèn)真當(dāng)成了喜歡
          瀏覽 44
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产成人小视频 | 人人色人人摸人人 | 欧美成人性爱视频网站 | 波多野结衣中文字幕在线视频 | 丁香婷婷视频 |