MySQL Query Optimizer 通過執(zhí)行 EXPLAIN 命令來告訴我們它將使用一個(gè)怎樣的執(zhí)行計(jì)劃來優(yōu)化Query。所以,可以說 Explain 是在優(yōu)化 Query 時(shí)最直接有效地驗(yàn)證我們想法的工具。
在對(duì)某個(gè) Query 優(yōu)化過程中,須要不斷地使用 Explain 來驗(yàn)證各種調(diào)整是否有效。就像前面很多示例都會(huì)通過 Explain 來驗(yàn)證和展示結(jié)果一樣,所有的 Query 優(yōu)化都應(yīng)該充分利用它。
下面看一下在 MySQL Explain功能中展示各種信息的解釋。
ID:MySQL Query Optimizer選定的執(zhí)行計(jì)劃中查詢的序列號(hào)。
Select_type:所使用的查詢類型,主要有以下這幾種查詢類型。
DEPENDENT SUBQUERY:子查詢內(nèi)層的第一個(gè)SELECT,依賴于外部查詢的結(jié)果集。
DEPENDENT UNION:子查詢中的UNION,且為UNION中從第二個(gè)SELECT開始的后面所有SELECT,同樣依賴于外部查詢的結(jié)果集。
PRIMARY:子查詢中的最外層查詢,注意并不是主鍵查詢。
SIMPLE:除子查詢或UNION之外的其他查詢。
SUBQUERY:子查詢內(nèi)層查詢的第一個(gè)SELECT,結(jié)果不依賴于外部查詢結(jié)果集。
UNCACHEABLE SUBQUERY:結(jié)果集無法緩存的子查詢。
UNION:UNION語句中第二個(gè)SELECT開始后面的所有SELECT,第一個(gè)SELECT為PRIMARY。
UNION RESULT:UNION 中的合并結(jié)果。
Table:顯示這一步所訪問的數(shù)據(jù)庫中的表的名稱。
Type:告訴我們對(duì)表使用的訪問方式,主要包含如下集中類型。
all:全表掃描。
const:讀常量,最多只會(huì)有一條記錄匹配,由于是常量,實(shí)際上只須要讀一次。
eq_ref:最多只會(huì)有一條匹配結(jié)果,一般是通過主鍵或唯一鍵索引來訪問。
fulltext:進(jìn)行全文索引檢索。
index:全索引掃描。
index_merge:查詢中同時(shí)使用兩個(gè)(或更多)索引,然后對(duì)索引結(jié)果進(jìn)行合并(merge),再讀取表數(shù)據(jù)。
index_subquery:子查詢中的返回結(jié)果字段組合是一個(gè)索引(或索引組合),但不是一個(gè)主鍵或唯一索引。
rang:索引范圍掃描。
ref:Join語句中被驅(qū)動(dòng)表索引引用的查詢。
ref_or_null:與ref的唯一區(qū)別就是在使用索引引用的查詢之外再增加一個(gè)空值的查詢。
system:系統(tǒng)表,表中只有一行數(shù)據(jù);
unique_subquery:子查詢中的返回結(jié)果字段組合是主鍵或唯一約束。
Possible_keys:該查詢可以利用的索引。如果沒有任何索引可以使用,就會(huì)顯示成null,這項(xiàng)內(nèi)容對(duì)優(yōu)化索引時(shí)的調(diào)整非常重要。
Key:MySQL Query Optimizer 從 possible_keys 中所選擇使用的索引。
Key_len:被選中使用索引的索引鍵長(zhǎng)度。
Ref:列出是通過常量(const),還是某個(gè)表的某個(gè)字段(如果是join)來過濾(通過key)的。
Rows:MySQL Query Optimizer 通過系統(tǒng)收集的統(tǒng)計(jì)信息估算出來的結(jié)果集記錄條數(shù)。
Extra:查詢中每一步實(shí)現(xiàn)的額外細(xì)節(jié)信息,主要會(huì)是以下內(nèi)容。
Distinct:查找distinct 值,當(dāng)mysql找到了第一條匹配的結(jié)果時(shí),將停止該值的查詢,轉(zhuǎn)為后面其他值查詢。
Full scan on NULL key:子查詢中的一種優(yōu)化方式,主要在遇到無法通過索引訪問null值的使用。
Impossible WHERE noticed after reading const tables:MySQL Query Optimizer 通過收集到的統(tǒng)計(jì)信息判斷出不可能存在結(jié)果。
No tables:Query 語句中使用 FROM DUAL或不包含任何 FROM子句。
Not exists:在某些左連接中,MySQL Query Optimizer通過改變?cè)?Query 的組成而使用的優(yōu)化方法,可以部分減少數(shù)據(jù)訪問次數(shù)。
Range checked for each record (index map: N):通過 MySQL 官方手冊(cè)的描述,當(dāng) MySQL Query Optimizer 沒有發(fā)現(xiàn)好的可以使用的索引時(shí),如果發(fā)現(xiàn)前面表的列值已知,部分索引可以使用。對(duì)前面表的每個(gè)行組合,MySQL檢查是否可以使用range或 index_merge訪問方法來索取行。
SELECT tables optimized away:當(dāng)我們使用某些聚合函數(shù)來訪問存在索引的某個(gè)字段時(shí),MySQL Query Optimizer 會(huì)通過索引直接一次定位到所需的數(shù)據(jù)行完成整個(gè)查詢。當(dāng)然,前提是在 Query 中不能有 GROUP BY 操作。如使用MIN()或MAX()的時(shí)候。
Using filesort:當(dāng)Query 中包含 ORDER BY 操作,而且無法利用索引完成排序操作的時(shí)候,MySQL Query Optimizer 不得不選擇相應(yīng)的排序算法來實(shí)現(xiàn)。
Using index:所需數(shù)據(jù)只需在 Index 即可全部獲得,不須要再到表中取數(shù)據(jù)。
Using index for group-by:數(shù)據(jù)訪問和 Using index 一樣,所需數(shù)據(jù)只須要讀取索引,當(dāng)Query 中使用GROUP BY或DISTINCT 子句時(shí),如果分組字段也在索引中,Extra中的信息就會(huì)是 Using index for group-by。
Using temporary:當(dāng) MySQL 在某些操作中必須使用臨時(shí)表時(shí),在 Extra 信息中就會(huì)出現(xiàn)Using temporary 。主要常見于 GROUP BY 和 ORDER BY 等操作中。
Using where:如果不讀取表的所有數(shù)據(jù),或不是僅僅通過索引就可以獲取所有需要的數(shù)據(jù),則會(huì)出現(xiàn) Using where 信息。
Using where with pushed condition:這是一個(gè)僅僅在 NDBCluster存儲(chǔ)引擎中才會(huì)出現(xiàn)的信息,而且還須要通過打開 Condition Pushdown 優(yōu)化功能才可能被使用??刂茀?shù)為 engine_condition_pushdown 。
這里通過分析示例來看一下不同的 Query 語句通過 Explain 所顯示的不同信息。
先看一個(gè)簡(jiǎn)單的單表 Query,如示例代碼8-4所示:
代碼8-4
- sky@localhost : example 11:33:18> EXPLAIN SELECT COUNT(*),MAX(id),MIN(id)
- -> FROM user\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: SELECT tables optimized away
對(duì)user表的單表查詢,查詢類型為SIMPLE,因?yàn)榧葲]有 UNION 也不是子查詢。聚合函數(shù) MAX、MIN及COUNT 三者需要的數(shù)據(jù)都可以通過索引直接定位得到,所以整個(gè)實(shí)現(xiàn)的 Extra 信息為 SELECT tables optimized away。
再來看一個(gè)稍微復(fù)雜一點(diǎn)的 Query,一個(gè)子查詢,如示例代碼8-5所示:
代碼8-5
- sky@localhost : example 11:38:48> EXPLAIN SELECT name FROM groups
- -> WHERE id IN ( SELECT group_id FROM user_group WHERE user_id = 1)\G
- *************************** 1. row ***************************
- id: 1
- select_type: PRIMARY
- table: groups
- type: ALL
- possible_keys: NULL
- key: NULL
- key_len: NULL
- ref: NULL
- rows: 50000
- Extra: Using where
- *************************** 2. row ***************************
- id: 2
- select_type: DEPENDENT SUBQUERY
- table: user_group
- type: ref
- possible_keys: user_group_gid_ind,user_group_uid_ind
- key: user_group_uid_ind
- key_len: 4
- ref: const
- rows: 1
- Extra: Using where
通過 id 信息可以得知 MySQL Query Optimizer 給出的執(zhí)行計(jì)劃,首先是對(duì) groups 進(jìn)行全表掃描,第二步才訪問 user_group 表,所使用的查詢方式是DEPENDENT SUBQUERY,對(duì)所需數(shù)據(jù)的訪問方式是索引掃描,由于過濾條件是一個(gè)整數(shù),所以索引掃描的類型為 ref,過濾條件是 const。可以使用的索引有兩個(gè),一個(gè)是基于 user_id,另一個(gè)則是基于 group_id 的。為什么基于 group_id 的索引 user_group_gid_ind 也被列為可選索引了呢?是因?yàn)榕c子查詢的外層查詢所關(guān)聯(lián)的條件是基于 group_id 的。當(dāng)然,最后 MySQL Query Optimizer 還是選擇了使用基于 user_id 的索引 user_group_uid_ind。
聯(lián)系客服