1、explain的基本介紹
有時候我們可能需要知道 mysql 是如何解析執行我們的 SQL 語句的,比如有時候某些語句寫在前面並不一定意味着它就會先執行,有沒有使用到索引等待,此時我們可以通過 explain 語句來分析出 SQL 優化器是如何解析執行我們的 SQL 語句的。
使用 EXPLAIN 關鍵字可以模擬優化器執行 SQL 查詢語句,從而知道 MySQL 是如何處理你的 SQL 語句的,explain 主要用於分析查詢語句或表結構的性能瓶頸。
1.1、explain的作用
通過explain+sql語句可以知道如下內容:
- 表的讀取順序(對應id)。
- 數據讀取操作的操作類型(對應select_type)。
- 哪些索引可以使用(對應possible_keys)。
- 哪些索引被實際使用(對應key)。
- 表直接的引用(對應ref)。
- 每張表有多少行被優化器查詢(對應rows)。
2、explain的基本使用
使用 explain 關鍵字來分析 SQL 語句的性能,只需要在 SQL 語句的前面加上 explain 即可,語法如下:
explain sql語句; -- 示例 explain select * from tbl_emp,tbl_dept where tbl_emp.deptId = tbl_dept.id;
執行結果:
3、explain的字段意思
使用 expain 分析 SQL 性能時,列出來的信息有10列,分別是id、select_type、table、type、possible_keys、key、key_len、ref、rows、Extra。
概要描述:
- id:表的讀取順序
- select_type:數據讀取操作的操作類型。
- table:該查詢是基於哪張表的
- partitions:匹配的分區
- type:表示查詢的訪問類型
- possible_keys:哪些索引可以使用
- key:哪些索引被實際使用
- key_len:索引字段的長度
- ref:表直接的引用
- rows:掃描出的行數(估算的行數)
- filtered:按表條件過濾的行百分比
- Extra:執行情況的描述和說明
3.1、id(表的讀取順序)
explain 分析出來的 id 字段表示查詢中執行 select 子句或操作表的順序。比如,當查詢的 SQL 語句涉及到多個表時,explain 分析出來的數據就有多條,其中 id 值越大的,表示SQL優化器會越早執行這個表。
其中,有三種情況:id全部相同、id全部不同、id部分相同。
3.1.1、id全部相同(執行順序由上至下)
比如我們執行 explain 來分析某條SQL語句,其中涉及到三個表。如下圖:
可以看到結果中,id全部都相同,此時意味着 SQL 優化器的執行順序為由上至下。也就是 SQL 優化器實際上的執行計划是:先執行 t1 表的查詢,再執行 t3 表,最后執行 t2 表。
3.1.2、id全部不同(id越大,越先被執行)
比如我們執行 explain 來分析某條SQL語句,其中涉及到三個表。如下圖:
可以看到結果中,id全部不同,id 值越大優先級越高,越先被執行。也就是 SQL 優化器實際上的執行計划是:先執行 t3 表的查詢,再執行 t2 表,最后執行 t1 表。
一般當涉及到子查詢時,id 的序號會遞增,id 值越大優先級越高,越先被執行.
3.1.3、id部分相同(相同id由上至下執行)
比如我們執行 explain 來分析某條SQL語句,其中涉及到三個表。如下圖:
可以看到結果中,id部分相同,部分不同,此時仍然適用 id 值越大越先被執行的原則,id 相同的可以認為是同一組,從上往下順序執行。也就是 SQL 優化器實際上的執行計划是:先執行 t3 表的查詢,再執行衍生表 derived2,最后執行 t2 表。
(衍生表 derived2 的意思是該衍生表是 id 為2,即 t3 表的虛擬表)
3.2、select_type(查詢操作的類型)
select_type 代表查詢的類型,主要是用於區別普通查詢、聯合查詢、子查詢等的復雜查詢。
select_type 的值和含義如下:
- SIMPLE:簡單的 select 查詢,查詢中不包含子查詢或者 UNION。
- PRIMARY:查詢中若包含任何復雜的子部分,最外層查詢則被標記為 Primary。
- DERIVED:在 FROM 列表中包含的子查詢被標記為 DERIVED(衍生) MySQL 會遞歸執行這些子查詢,,把結果放在臨時表里。
- SUBQUERY:在SELECT或WHERE列表中包含了子查詢。
- DEPEDENT SUBQUERY:在SELECT或WHERE列表中包含了子查詢,子查詢基於外層。
都是 where 后面的條件,subquery 是單個值,dependent subquery 是一組值。
- UNCACHEABLE SUBQUERY:無法使用緩存的子查詢。
當使用了@@來引用系統變量的時候,不會使用緩存。
- UNION:若第二個SELECT出現在UNION之后,則被標記為UNION; 若UNION包含在FROM子句的子查詢中,外層SELECT將被標記為:DERIVED。
- UNION RESULT:從UNION表獲取結果的SELECT。
3.3、TYPE(查詢類型)
type 是查詢的訪問類型,表示該查詢使用了哪種查詢類型,是較為重要的一個指標。
結果值從最好到最壞依次是:
system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL。常見的有:system > const > eq_ref > ref > range > index > ALL
一般來說,得保證查詢至少達到 range 級別,最好能達到 ref。
3.3.1、system 查詢類型
當某個表只有一行記錄時(等於系統表),查詢該表的查詢類型會是 system。這是 const 類型的特例,平時很少會出現。
3.3.2、const 查詢類型
常量查詢,表示通過索引Index一次就找到了。用於比較 primary key=常量 和 unique=常量 這種索引。
比如將主鍵置於 where 查詢條件中,MySQL 就能將該查詢轉換為一個常量,也就是使用常量查詢。因為只匹配一行數據,所以該查詢類型查詢效率很快。
比如下面 t1 表中 id 字段建了索引,執行計划如下:
3.3.3、eq_ref 查詢類型
唯一性索引掃描,對於每個索引鍵,表中只對應一行數據,常見於主鍵或唯一索引掃描。
3.3.4、ref 查詢類型
非唯一性索引掃描,返回匹配某個單獨值的所有行。本質上也是一種索引訪問,它返回所有匹配某個單獨值的行, 然而,它可能會找到多個符合條件的行,所以他應該屬於查找和掃描的混合體。
示例:
沒用索引前:
建立索引后:
3.3.5、range 查詢類型
只檢索給定范圍的行,使用一個索引來選擇行,key 列顯示使用了哪個索引,一般就是在你的 where 語句中出現了 between、<、>、in 等的查詢。這種范圍掃描索引掃描比全表掃描要好,因為它只需要開始於索引的某一點,而結束於另一點,不用掃描全部索引。
3.3.6、index 查詢類型
全索引掃描(FULL INDEX SCAN),只遍歷索引樹,通常比ALL快,因為索引文件通常比數據文件小。index 與 ALL 都是全表查詢,但 index 是從索引中讀取,而 ALL是從全表中讀取。
出現 index 是 sql 使用了索引但是沒有通過索引進行過濾,一般是使用了覆蓋索引或者是利用索引進行了排序分組。
3.3.7、all 查詢類型
全表掃描(FULL TABLE SCAN),遍歷全表找到匹配的行。速度最慢,應避免使用。
3.4、possible_keys和key
possible_keys:顯示可能應用在這張表中的索引,一個或多個。查詢涉及到的字段上若存在索引,則該索引將被列出,一個或多個,但不一定被查詢實際使用。
key:實際使用的索引。如果為NULL,則表示沒有使用到索引。常見的可能原因:1、沒有建索引。2、sql語句寫法錯誤,索引失效。3、possible_key也為NULL時,表示用不到索引。
3.5、key_len
key_len表示索引使用的字節數,根據這個值可以判斷索引的使用情況,特別是在使用聯合索引的時候,判斷該索引有多少部分被使用到非常重要。
key_len的長度計算公式很重要。如果是單列索引不用去計算,因為沒有意義,如果是組合索引,知道key_len的長度是非常有意義的,key_len越小,說明索引效果越好。
key_len 計算公式可參考:https://blog.csdn.net/u012068483/article/details/105270813
3.6、ref
顯示索引的哪一列被使用了,有可能是一個常數。顯示哪些列或常量被用於查找索引列上的值。
3.7、rows(行數)
rows 列顯示 MySQL 根據表統計信息及索引選用情況,大致估算出找到所需的記錄所需要讀取的行數。該值越少越好。
3.8、Extra
該列用來顯示其他的一些額外的重要的信息。
其取值有以下幾個:
- Using filesort(文件排序)
當Query中包含 order by 操作,而且無法利用索引完成的排序操作稱為“文件排序”。
Using filesort表明 mysql 會對數據使用一個外部的索引排序,而不是按照表內的索引順序進行讀取。mysql中無法利用索引完成的排序操作稱為“文件排序”。出現Using filesort就非常危險了,在數據量非常大的時候幾乎“九死一生”,出現Using filesort需盡快優化sql語句。
示例:
當 deptname字段未建索引時:
為deptname字段創建索引后:
- Using temporary
使用了臨時表保存中間結果,常見於排序order by和分組查詢group by。出現這種情況非常危險,“十死無生”,急需對SQL進行優化。
示例:
將tb_emp中name的索引先刪除,出現如下圖結果,非常爛,Using filesort和Using temporary,“十死無生”。
為name字段創建索引后:
- Using index
表明相應的 select 操作中使用了覆蓋索引,避免訪問表的額外數據行。出現這個提示是好兆頭,表明該SQL的效率不錯。
如果同時出現了Using where,表明索引被用來執行索引鍵值的查找。如果沒有同時出現Using where,表明索引用來讀取數據而非執行查找動作。
- Using where
表明使用了 where 過濾。
- Using join buffer
使用了連接緩存。該值強調了在獲取連接條件時沒有使用索引,並且需要連接緩沖區來存儲中間結果。如果出現了這個值,那應該注意,根據查詢的具體情況可能需要添加索引來改進。
- Impossible where
出現這個值說明 where 語句的條件總是 false,無法查詢到任何符合條件的行。此時可能需要檢查 SQL 語句是否正確。