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