MySQL Explain

explain是對MySQL的select, update, insert的這三種操做執行計劃的描述,可根據其反饋的信息作MySQL的優化。mysql

SQL執行過程和優化器

首先看一下MySQL中,一條sql的執行過程,這裏主要是引用了《高性能MySQL》中的內容:算法

一、客戶端發送一條查詢給服務器 ;
二、服務器先檢查查詢緩存,若是命中緩存則馬上返回存儲在緩存中的結果,不然進入下一階段;
三、服務器端進行SQL解析、預處理,再由優化器生成對應的執行計劃;
四、MYSQL根據優化器生成的執行計劃調用存儲引擎的API來執行查詢 ;
五、將結果返回給客戶端;sql

對於mysql來講,底層的存儲引擎主要的工做就是對數據的磁盤文件進行操做,上層的sql解析等操做主要放在服務器層,服務器層根據sql進行優化和解析生成執行計劃,最後存儲引擎根據執行計劃來調用數據,有機會必定要讀一下這個源碼,體會一下這個過程。數據庫

tips:一個sql查詢每每有不少種方式,能夠弄成子查詢,也能夠用錶鏈接,這個地方的選擇主要就是由優化器來負責。緩存

在oracle中,優化器主要有:bash

rbo:基於規則的優化器 
cbo:基於代價的優化器
服務器

rbo已經被拋棄不使用了,目前主流使用基於代價的cbo。 
基於代價就要計算代價,初始狀況下,計算代價的最小單位就是隨機讀取一個4k頁面的成本,並選擇其中成本最小的執行計劃,後來引入了一些複雜的計算因子來計算某些操做的代價。機器畢竟是機器,不少狀況下,cbo優化器也會發生優化不許確的狀況,因此這也是爲何須要了解執行過程的緣由。mysql優化

 

不少狀況下都會致使優化器選擇了錯誤的執行計劃,並不能得到最好的性能:併發

統計信息不許確,mysql依賴存儲引擎提供的統計信息來評估成本,可是有的存儲引擎提供的信息是準確的,有的不許確
執行計劃中的成本估算不等同於實際執行的成本,由於MYSQL層面並不知道那些頁面在內存和磁盤上因此到底須要多少次物理IO未知。
MySQL的最優化和你的最優化並不同(目標不一樣,可能你但願最少時間,MySQL優化目標是最大吞吐量)
MYSQL不考慮併發查詢
MYSQL並非任什麼時候候都基於成本優化
MYSQL不會考慮不受其控制的操做的成本oracle

查看執行計劃

explain select xxx from xxx; 


上面這是最簡單的執行計劃實例,來分析一下上面的這幾個字段。

id: 主要是用來標識sql執行順序,若是沒有子查詢,通常來講id只有1個,執行順序也是從上到下。


select_type: 每一個select子句的類型,主要分紅下面幾種: 
1: SIMPLE: 查詢中不包含任何子查詢或者union 
2: PRIMARY: 查詢中包含了任何複雜的子部分,最外層的就會變成PRIMARY
3: SUBQUERY: 在SELECT或者WHERE列表中包含了子查詢 
4: DERIVED: 在FROM中包含了子查詢 
5: UNION: 若是第二個SELECT出如今UNION以後,則被標記爲UNION,
                     若是UNION包含在FROM子句的子查詢中,外層SELECT會被標記爲:DERIVED 
6: UNION RESULT: 從UNION表獲取結果的select

 

table: 查詢的表


type: 是指MySQL在表中找到所需行的方式,也就是訪問行的「類型」,從1開始,效率逐漸上升: 
1: all: 全表掃描,效率最低 
2: index: index會根據索引樹遍歷 
3: range: 索引範圍掃描,返回匹配值域的行。 
4: ref: 非惟一性索引掃描,返回匹配某個單獨值的全部行。通常是指多列的惟一索引中的某一列。 
5: eq_ref: 惟一性索引掃描,表中只有一條記錄與之匹配。 
6: const, system: 主要針對查詢中有常量的狀況,如將主鍵置於where列表中,MySQL就能將該查詢轉換爲一個常量。若是結果只有一行會變成system 
7: NULL: 顯而易見,既不走表,也不走索引

possible_keys 
possible_keys列預估了mysql可以爲當前查詢選擇的索引,這個字段是徹底獨立於執行計劃中輸出的表的順序,意味着在實際查詢中可能用不到這些索引。 若是該字段爲空則意味着沒有可以使用的索引,這個時候你能夠考慮爲where後面的字段創建索引

key 
這個字段表示了mysql真實使用的索引。若是mysql優化過程當中沒有加索引,鍵是NULL。要想強制MySQL使用或忽視possible_keys列中的索引,在查詢中使用FORCE INDEX、USE INDEX或者IGNORE INDEX。

key_len 
索引長度字段顧名思義,表示了mysql使用的索引的長度。在不損失精確性的狀況下,長度越短越好。

ref 
這個字段通常是指一些常量用於選擇過濾

rows 
這個字段是MySQL估計的 爲了找到所需的行而要讀取的行數。這個數字是內嵌循環關聯計劃裏的循環數目,也就是說它不是MySQL認爲它最終要從表裏讀取出來的行數,而是MySQL爲了找到符合查詢的每一點上標準的那些行而必須讀取的行的平均數。

它提供了試圖分析全部存在於累計結果集中的行數目的MySQL優化器估計值。QEP 很容易描述這個很困難的統計量。

查詢中總的讀操做數量是基於合併以前行的每一行的rows 值的連續積累而得出的。這是一種嵌套行算法

filtered (5.7版本以後默認會顯示的,在此以前要使用explain extended 纔會顯示)

它顯示的是 返回結果的行數 佔 rows字段數值 的 百分比 所作的一個 悲觀估算值。比值越大,代表執行計劃越準確。

Extra

關於MYSQL如何解析查詢的額外信息。將在表4.3中討論,但這裏能夠看到的壞的例子是Using temporary和Using filesort,意思MYSQL根本不能使用索引,結果是檢索會很慢。如下是一些具體的例子: 

1. Distinct :一旦mysql找到了與行相聯合匹配的行,就再也不搜索了。

2. Not exists :mysql優化了LEFT JOIN,一旦它找到了匹配LEFT JOIN標準的行,就再也不搜索了。

3. Range checked for each Record(index map:#) :沒有找到理想的索引,所以對從前面表中來的每個行組合,mysql檢查使用哪一個索引,並用它來從表中返回行。這是使用索引的最慢的鏈接之一。

4. Using filesort :MySQL中沒法利用索引完成的排序操做稱爲「文件排序」。看到這個的時候,查詢就須要優化了。mysql須要進行額外的步驟來發現如何對返回的行排序。它根據鏈接類型以及存儲排序鍵值和匹配條件的所有行的行指針來排序所有行。

5. Using index :列數據是從僅僅使用了索引中的信息而沒有讀取實際的行動的表返回的,這發生在對錶的所有的請求列都是同一個索引的部分的時候。

6. Using temporary :看到這個的時候,查詢須要優化了。這裏,mysql須要建立一個臨時表來存儲結果,這一般發生在對不一樣的列集進行ORDER BY上,而不是GROUP BY上。(常見於排序和分組查詢?)

7. Where used :使用了WHERE從句來限制哪些行將與下一張表匹配或者是返回給用戶。若是不想返回表中的所有行,而且鏈接類型ALL或index,這就會發生,或者是查詢有問題。

 

總結:
• EXPLAIN不會告訴你關於觸發器、存儲過程的信息或用戶自定義函數對查詢的影響狀況
• EXPLAIN不考慮各類Cache
• EXPLAIN不能顯示MySQL在執行查詢時所做的優化工做
• 部分統計信息是估算的,並不是精確值
• EXPALIN只能解釋SELECT操做,其餘操做要重寫爲SELECT後查看執行計劃。(錯誤X,EXPLAIN對增刪改查操做均可以查看執行計劃)

 

實踐測試

這裏取數據庫中最經典的employees庫做爲例子,兩張表,一張employees僱員表,一張salaries薪水錶:

`explain select e.birth_date,s.salary from employees e right join salaries s on e.emp_no = s.emp_no;`

這是一條 兩錶鏈接 的查詢。

執行計劃以下:

 
一個一個來,首先在id相同的狀況下,從上往下執行。這是一個右鏈接查詢,以右邊爲基準,右邊的每一行去匹配左邊的表,因此首先右邊進行s表全表掃描, type爲ALL,沒有走索引。同時兩次查詢都是簡單查詢,沒有子查詢,因此select_type都是simple。

接着是對e表進行查詢,type爲eq_ref,由於emp_no是主鍵,惟一性掃描,possible_keys和key都是primary,每次都針對前面的記錄掃出對應的一行。

相關文章
相關標籤/搜索