概念
在日常工作中,我們會(huì)有時(shí)會(huì)開慢查詢?nèi)ビ涗浺恍﹫?zhí)行時(shí)間比較久的SQL語(yǔ)句,找出這些SQL語(yǔ)句并不意味著完事了,些時(shí)我們常常用到explain這個(gè)命令來(lái)查看一個(gè)這些SQL語(yǔ)句的執(zhí)行計(jì)劃,查看該SQL語(yǔ)句有沒(méi)有使用上了索引,有沒(méi)有做全表掃描,這都可以通過(guò)
explain命令來(lái)查看。所以我們深入了解MySQL的基于開銷的優(yōu)化器,還可以獲得很多可能被優(yōu)化器考慮到的訪問(wèn)策略的細(xì)節(jié),以及當(dāng)運(yùn)行SQL語(yǔ)句時(shí)哪種策略預(yù)計(jì)會(huì)被優(yōu)化器采用。(QEP:sql生成一個(gè)執(zhí)行計(jì)劃query Execution plan)expain出來(lái)的信息有10列,分別是
id、select_type、table、type、possible_keys、key、key_len、ref、rows、Extra,下面對(duì)這些字段出現(xiàn)的可能進(jìn)行解釋
參數(shù)詳解
id
- SQL執(zhí)行的順序的標(biāo)識(shí),SQL從大到小的執(zhí)行
- id相同時(shí),執(zhí)行順序由上至下
- 如果是子查詢,id的序號(hào)會(huì)遞增,id值越大優(yōu)先級(jí)越高,越先被執(zhí)行
- id如果相同,可以認(rèn)為是一組,從上往下順序執(zhí)行;在所有組中,id值越大,優(yōu)先級(jí)越高,越先執(zhí)行
select_type
- 表示查詢中每個(gè)select子句的類型
| 類型 | 解釋 |
|---|---|
| SIMPLE | 簡(jiǎn)單SELECT,不使用UNION或子查詢等 |
| PRIMARY | 查詢中若包含任何復(fù)雜的子部分,最外層的select被標(biāo)記為PRIMARY |
| UNION | UNION中的第二個(gè)或后面的SELECT語(yǔ)句 |
| DEPENDENT UNION | UNION中的第二個(gè)或后面的SELECT語(yǔ)句,取決于外面的查詢 |
| UNION RESULT | UNION的結(jié)果 |
| SUBQUERY | 子查詢中的第一個(gè)SELECT |
| DEPENDENT SUBQUERY | 子查詢中的第一個(gè)SELECT,取決于外面的查詢 |
| DERIVED | 派生表的SELECT, FROM子句的子查詢 |
| UNCACHEABLE SUBQUERY | 一個(gè)子查詢的結(jié)果不能被緩存,必須重新評(píng)估外鏈接的第一行 |
table
- 顯示這一行的數(shù)據(jù)是關(guān)于哪張表的,有時(shí)不是真實(shí)的表名字,看到的是derivedx(x是個(gè)數(shù)字,我的理解是第幾步執(zhí)行的結(jié)果)
type (重要)
表示MySQL在表中找到所需行的方式,又稱“訪問(wèn)類型”。
常用的類型有:** ALL, index, range, ref, eq_ref, const, system, NULL(從左到右,性能從差到好)**
| 類型 | 說(shuō)明 |
|---|---|
| system | 該表只有一行(相當(dāng)于系統(tǒng)表),system是const類型的特例 |
| const | 針對(duì)主鍵或唯一索引的等值查詢掃描,最多只返回一行數(shù)據(jù),速度非常快,因?yàn)樗恍枰x取一次 |
| eq_ref | 當(dāng)使用了索引的全部組成部分,并且索引是PRIMARY KEY或UNIQUE NOT NULL |
| ref | 當(dāng)滿足索引的最左前綴規(guī)則,或者索引不是主鍵也不是唯一索引時(shí)才會(huì)發(fā)送。 |
| fulltext | 全文索引 |
| ref_or_null | 類似于ref,但mysql會(huì)額外搜索哪些行包含了NULL。常見(jiàn)于解析子查詢 |
| index_merge | 使用了索引合并優(yōu)化,表示一個(gè)查詢里用到了多個(gè)索引 |
| unique_subquery | 和eq_ref類似,但用了in,且子查詢是主鍵或唯一索引 |
| index_subquery | 和unique_subquery類似,只是子查詢用的不是唯一索引 |
| range | 范圍掃描。表示檢索了指定范圍的行,主要用于有限制的索引掃描。常見(jiàn)于between或where里有>、<、IS NULL、<=>、LIKE、IN等 |
| index | 全索引掃描,與ALL類似,只不過(guò)index是全盤掃描了索引的數(shù)據(jù)。當(dāng)查詢僅使用索引中的一部分列時(shí)(且不符合最左前綴),可使用此類型。有兩個(gè)場(chǎng)景會(huì)觸發(fā):1. 如果索引是查詢的覆蓋索引,并且索引查詢的數(shù)據(jù)就可以滿足查詢中所需的所有數(shù)據(jù),則只掃描索引樹。此時(shí)extra中會(huì)有using index。2. 按照索引的順序來(lái)查找,執(zhí)行了全表掃描。次數(shù)extra不會(huì)出現(xiàn)using index |
| all | 全表掃描,性能最差 |
possible_keys
- 指出MySQL能使用哪個(gè)索引在表中找到記錄,查詢涉及到的字段上若存在索引,則該索引將被列出,但不一定被查詢使用**
- 該列完全獨(dú)立于EXPLAIN輸出所示的表的次序。這意味著在possible_keys中的某些鍵實(shí)際上不能按生成的表次序使用。
如果該列是NULL,則沒(méi)有相關(guān)的索引。在這種情況下,可以通過(guò)檢查WHERE子句看是否它引用某些列或適合索引的列來(lái)提高你的查詢性能。如果是這樣,創(chuàng)造一個(gè)適當(dāng)?shù)乃饕⑶以俅斡肊XPLAIN檢查查詢
Key
key列顯示MySQL實(shí)際決定使用的鍵(索引)
如果沒(méi)有選擇索引,鍵是NULL。要想強(qiáng)制MySQL使用或忽視possible_keys列中的索引,在查詢中使用FORCE INDEX、USE INDEX或者IGNORE INDEX。
key_len
表示索引中使用的字節(jié)數(shù),可通過(guò)該列計(jì)算查詢中使用的索引的長(zhǎng)度(key_len顯示的值為索引字段的最大可能長(zhǎng)度,并非實(shí)際使用長(zhǎng)度,即key_len是根據(jù)表定義計(jì)算而得,不是通過(guò)表內(nèi)檢索出的)
不損失精確性的情況下,長(zhǎng)度越短越好
key_len的長(zhǎng)度計(jì)算公式:
varchr(10)變長(zhǎng)字段且允許NULL = 10 * ( character set:utf8=3,gbk=2,latin1=1)+1(NULL)+2(變長(zhǎng)字段)
varchr(10)變長(zhǎng)字段且不允許NULL = 10 *( character set:utf8=3,gbk=2,latin1=1)+2(變長(zhǎng)字段)
char(10)固定字段且允許NULL = 10 * ( character set:utf8=3,gbk=2,latin1=1)+1(NULL)
char(10)固定字段且不允許NULL = 10 * ( character set:utf8=3,gbk=2,latin1=1)
ref
- 表示上述表的連接匹配條件,即哪些列或常量被用于查找索引列上的值
rows
- 要掃描的行數(shù)
- 表示MySQL根據(jù)表統(tǒng)計(jì)信息及索引選用情況,估算的找到所需的記錄所需要讀取的行數(shù)
需要降低掃描的行數(shù)
filtered
符合查詢條件的數(shù)據(jù)百分比,最大100.用rows* filtered可獲得和下一張表連接的行數(shù)。例如rows = 1000,filtered = 50%,則和下一章表連接的行數(shù)是500
Extra
該列包含MySQL解決查詢的詳細(xì)信息,有以下幾種情況:從上到下變慢
Using index:不需要回表,使用索引匹配,且不需要索引范圍掃描
Using index & using where:不需要回表,使用索引匹配,但需要索引范圍掃描
Using index condition: 在5.6引入的新特性,先條件過(guò)濾索引,過(guò)濾完索引后找到所有符合索引條件的數(shù)據(jù)行,隨后用where中其他條件去過(guò)濾
Using where: 意味著通過(guò)索引或者表掃描的方式進(jìn)行where條件的過(guò)濾,反過(guò)來(lái)說(shuō),也就是沒(méi)有可用的索引查找,當(dāng)然這里也要考慮索引掃描+回表與表掃描的代價(jià)。
Using temporary:表示MySQL需要使用臨時(shí)表來(lái)存儲(chǔ)結(jié)果集,常見(jiàn)于排序和分組查詢,需要著重優(yōu)化
query里包含不同列的group by和order by子句,通常會(huì)用到臨時(shí)表
-- name無(wú)索引
explain SELECT name FROM t1 GROUP BY name
Using filesort:MySQL中無(wú)法利用索引完成的排序操作稱為“文件排序”
當(dāng)query中包含order by,且無(wú)法利用索引進(jìn)行排序時(shí),就會(huì)出現(xiàn)using filesort, 需要著重優(yōu)化Using join buffer:改值強(qiáng)調(diào)了在獲取連接條件時(shí)沒(méi)有使用索引,并且需要連接緩沖區(qū)來(lái)存儲(chǔ)中間結(jié)果。如果出現(xiàn)了這個(gè)值,那應(yīng)該注意,根據(jù)查詢的具體情況可能需要添加索引來(lái)改進(jìn)能。
Impossible where:這個(gè)值強(qiáng)調(diào)了where語(yǔ)句會(huì)導(dǎo)致沒(méi)有符合條件的行。
Select tables optimized away:這個(gè)值意味著僅通過(guò)使用索引,優(yōu)化器可能僅從聚合函數(shù)結(jié)果中返回一行
總結(jié): EXPLAIN不會(huì)告訴你關(guān)于觸發(fā)器、存儲(chǔ)過(guò)程的信息或用戶自定義函數(shù)對(duì)查詢的影響情況
- EXPLAIN不考慮各種Cache
- EXPLAIN不能顯示MySQL在執(zhí)行查詢時(shí)所作的優(yōu)化工作
- 部分統(tǒng)計(jì)信息是估算的,并非精確值
- EXPALIN只能解釋SELECT操作,其他操作要重寫為SELECT后查看執(zhí)行計(jì)劃。**