轉(zhuǎn)自:https://www.imooc.com/article/308225
EXPLAIN作為MySQL的性能分析神器懦胞,讀懂其結(jié)果是很有必要的惦积,然而我在各種搜索引擎上竟然找不到特別完整的解讀障癌。都是只有重點(diǎn)幸海,沒(méi)有細(xì)節(jié)(例如type的取值不全、Extra缺乏完整的介紹等)菩帝。
所以刊棕,我肝了將近一個(gè)星期,整理了一下伙菜。這應(yīng)該是全網(wǎng)最全面轩缤、最細(xì)致的EXPLAIN解讀文章了命迈,下面是全文。
文章比較長(zhǎng)火的,建議收藏壶愤。
TIPS
本文基于MySQL 8.0編寫,理論支持MySQL 5.0及更高版本馏鹤。
EXPLAIN使用
explain可用來(lái)分析SQL的執(zhí)行計(jì)劃征椒。格式如下:
{EXPLAIN | DESCRIBE | DESC}
tbl_name [col_name | wild]
{EXPLAIN | DESCRIBE | DESC}
[explain_type]
{explainable_stmt | FOR CONNECTION connection_id}
{EXPLAIN | DESCRIBE | DESC} ANALYZE select_statement
explain_type: {
FORMAT = format_name
}
format_name: {
TRADITIONAL
| JSON
| TREE
}
explainable_stmt: {
SELECT statement
| TABLE statement
| DELETE statement
| INSERT statement
| REPLACE statement
| UPDATE statement
}
示例:
EXPLAIN format = TRADITIONAL json SELECT tt.TicketNumber, tt.TimeIn,
tt.ProjectReference, tt.EstimatedShipDate,
tt.ActualShipDate, tt.ClientID,
tt.ServiceCodes, tt.RepetitiveID,
tt.CurrentProcess, tt.CurrentDPPerson,
tt.RecordVolume, tt.DPPrinted, et.COUNTRY,
et_1.COUNTRY, do.CUSTNAME
FROM tt, et, et AS et_1, do
WHERE tt.SubmitTime IS NULL
AND tt.ActualPC = et.EMPLOYID
AND tt.AssignedPC = et_1.EMPLOYID
AND tt.ClientID = do.CUSTNMBR;
結(jié)果輸出展示:
字段 | format=json時(shí)的名稱 | 含義 |
---|---|---|
id | select_id | 該語(yǔ)句的唯一標(biāo)識(shí) |
select_type | 無(wú) | 查詢類型 |
table | table_name | 表名 |
partitions | partitions | 匹配的分區(qū) |
type | access_type | 聯(lián)接類型 |
possible_keys | possible_keys | 可能的索引選擇 |
key | key | 實(shí)際選擇的索引 |
key_len | key_length | 索引的長(zhǎng)度 |
ref | ref | 索引的哪一列被引用了 |
rows | rows | 估計(jì)要掃描的行 |
filtered | filtered | 表示符合查詢條件的數(shù)據(jù)百分比 |
Extra | 沒(méi)有 | 附加信息 |
結(jié)果解讀
id
該語(yǔ)句的唯一標(biāo)識(shí)。如果explain的結(jié)果包括多個(gè)id值湃累,則數(shù)字越大越先執(zhí)行勃救;而對(duì)于相同id的行,則表示從上往下依次執(zhí)行治力。
select_type
查詢類型蒙秒,有如下幾種取值:
查詢類型 | 作用 |
---|---|
SIMPLE | 簡(jiǎn)單查詢(未使用UNION或子查詢) |
PRIMARY | 最外層的查詢 |
UNION | 在UNION中的第二個(gè)和隨后的SELECT被標(biāo)記為UNION。如果UNION被FROM子句中的子查詢包含宵统,那么它的第一個(gè)SELECT會(huì)被標(biāo)記為DERIVED晕讲。 |
DEPENDENT UNION | UNION中的第二個(gè)或后面的查詢,依賴了外面的查詢 |
UNION RESULT | UNION的結(jié)果 |
SUBQUERY | 子查詢中的第一個(gè) SELECT |
DEPENDENT SUBQUERY | 子查詢中的第一個(gè) SELECT马澈,依賴了外面的查詢 |
DERIVED | 用來(lái)表示包含在FROM子句的子查詢中的SELECT瓢省,MySQL會(huì)遞歸執(zhí)行并將結(jié)果放到一個(gè)臨時(shí)表中。MySQL內(nèi)部將其稱為是Derived table(派生表)痊班,因?yàn)樵撆R時(shí)表是從子查詢派生出來(lái)的 |
DEPENDENT DERIVED | 派生表勤婚,依賴了其他的表 |
MATERIALIZED | 物化子查詢 |
UNCACHEABLE SUBQUERY | 子查詢,結(jié)果無(wú)法緩存涤伐,必須針對(duì)外部查詢的每一行重新評(píng)估 |
UNCACHEABLE UNION | UNION屬于UNCACHEABLE SUBQUERY的第二個(gè)或后面的查詢 |
table
表示當(dāng)前這一行正在訪問(wèn)哪張表馒胆,如果SQL定義了別名荆永,則展示表的別名
partitions
當(dāng)前查詢匹配記錄的分區(qū)。對(duì)于未分區(qū)的表国章,返回null
type
連接類型具钥,有如下幾種取值,性能從好到壞排序 如下:
system:該表只有一行(相當(dāng)于系統(tǒng)表)液兽,system是const類型的特例
const:針對(duì)主鍵或唯一索引的等值查詢掃描, 最多只返回一行數(shù)據(jù). const 查詢速度非陈钌荆快, 因?yàn)樗鼉H僅讀取一次即可
-
eq_ref:當(dāng)使用了索引的全部組成部分,并且索引是PRIMARY KEY或UNIQUE NOT NULL 才會(huì)使用該類型四啰,性能僅次于system及const宁玫。
-- 多表關(guān)聯(lián)查詢,單行匹配 SELECT * FROM ref_table,other_table WHERE ref_table.key_column=other_table.column; -- 多表關(guān)聯(lián)查詢柑晒,聯(lián)合索引欧瘪,多行匹配 SELECT * FROM ref_table,other_table WHERE ref_table.key_column_part1=other_table.column AND ref_table.key_column_part2=1;
-
ref:當(dāng)滿足索引的最左前綴規(guī)則,或者索引不是主鍵也不是唯一索引時(shí)才會(huì)發(fā)生匙赞。如果使用的索引只會(huì)匹配到少量的行佛掖,性能也是不錯(cuò)的。
-- 根據(jù)索引(非主鍵涌庭,非唯一索引)芥被,匹配到多行 SELECT * FROM ref_table WHERE key_column=expr; -- 多表關(guān)聯(lián)查詢,單個(gè)索引坐榆,多行匹配 SELECT * FROM ref_table,other_table WHERE ref_table.key_column=other_table.column; -- 多表關(guān)聯(lián)查詢拴魄,聯(lián)合索引,多行匹配 SELECT * FROM ref_table,other_table WHERE ref_table.key_column_part1=other_table.column AND ref_table.key_column_part2=1;
TIPS
最左前綴原則席镀,指的是索引按照最左優(yōu)先的方式匹配索引匹中。比如創(chuàng)建了一個(gè)組合索引(column1, column2, column3),那么豪诲,如果查詢條件是:
- WHERE column1 = 1顶捷、WHERE column1= 1 AND column2 = 2、WHERE column1= 1 AND column2 = 2 AND column3 = 3 都可以使用該索引跛溉;
- WHERE column1 = 2焊切、WHERE column1 = 1 AND column3 = 3就無(wú)法匹配該索引。
fulltext:全文索引
-
ref_or_null:該類型類似于ref芳室,但是MySQL會(huì)額外搜索哪些行包含了NULL专肪。這種類型常見于解析子查詢
SELECT * FROM ref_table WHERE key_column=expr OR key_column IS NULL;
index_merge:此類型表示使用了索引合并優(yōu)化,表示一個(gè)查詢里面用到了多個(gè)索引
-
unique_subquery:該類型和eq_ref類似堪侯,但是使用了IN查詢嚎尤,且子查詢是主鍵或者唯一索引。例如:
value IN (SELECT primary_key FROM single_table WHERE some_expr)
-
index_subquery:和unique_subquery類似伍宦,只是子查詢使用的是非唯一索引
value IN (SELECT key_column FROM single_table WHERE some_expr)
-
range:范圍掃描芽死,表示檢索了指定范圍的行乏梁,主要用于有限制的索引掃描。比較常見的范圍掃描是帶有BETWEEN子句或WHERE子句里有>关贵、>=遇骑、<、<=揖曾、IS NULL落萎、<=>、BETWEEN炭剪、LIKE练链、IN()等操作符。
SELECT * FROM tbl_name WHERE key_column BETWEEN 10 and 20; SELECT * FROM tbl_name WHERE key_column IN (10,20,30);
-
index:全索引掃描奴拦,和ALL類似媒鼓,只不過(guò)index是全盤掃描了索引的數(shù)據(jù)。當(dāng)查詢僅使用索引中的一部分列時(shí)错妖,可使用此類型绿鸣。有兩種場(chǎng)景會(huì)觸發(fā):
- 如果索引是查詢的覆蓋索引,并且索引查詢的數(shù)據(jù)就可以滿足查詢中所需的所有數(shù)據(jù)站玄,則只掃描索引樹枚驻。此時(shí)濒旦,explain的Extra 列的結(jié)果是Using index株旷。index通常比ALL快,因?yàn)樗饕拇笮⊥ǔP∮诒頂?shù)據(jù)尔邓。
- 按索引的順序來(lái)查找數(shù)據(jù)行晾剖,執(zhí)行了全表掃描。此時(shí)梯嗽,explain的Extra列的結(jié)果不會(huì)出現(xiàn)Uses index齿尽。
ALL:全表掃描,性能最差灯节。
possible_keys
展示當(dāng)前查詢可以使用哪些索引循头,這一列的數(shù)據(jù)是在優(yōu)化過(guò)程的早期創(chuàng)建的,因此有些索引可能對(duì)于后續(xù)優(yōu)化過(guò)程是沒(méi)用的炎疆。
key
表示MySQL實(shí)際選擇的索引
key_len
索引使用的字節(jié)數(shù)卡骂。由于存儲(chǔ)格式,當(dāng)字段允許為NULL時(shí)形入,key_len比不允許為空時(shí)大1字節(jié)全跨。
key_len計(jì)算公式: https://www.cnblogs.com/gomysql/p/4004244.html
ref
表示將哪個(gè)字段或常量和key列所使用的字段進(jìn)行比較。
如果ref是一個(gè)函數(shù)亿遂,則使用的值是函數(shù)的結(jié)果浓若。要想查看是哪個(gè)函數(shù)渺杉,可在EXPLAIN語(yǔ)句之后緊跟一個(gè)SHOW WARNING語(yǔ)句。
rows
MySQL估算會(huì)掃描的行數(shù)挪钓,數(shù)值越小越好是越。
filtered
表示符合查詢條件的數(shù)據(jù)百分比,最大100碌上。用rows × filtered可獲得和下一張表連接的行數(shù)英妓。例如rows = 1000,filtered = 50%绍赛,則和下一張表連接的行數(shù)是500蔓纠。
TIPS
在MySQL 5.7之前,想要顯示此字段需使用explain extended命令吗蚌;
MySQL.5.7及更高版本腿倚,explain默認(rèn)就會(huì)展示filtered
Extra
展示有關(guān)本次查詢的附加信息,取值如下:
-
Child of ‘table’ pushed join@1
此值只會(huì)在NDB Cluster下出現(xiàn)蚯妇。
-
const row not found
例如查詢語(yǔ)句SELECT … FROM tbl_name敷燎,而表是空的
-
Deleting all rows
對(duì)于DELETE語(yǔ)句,某些引擎(例如MyISAM)支持以一種簡(jiǎn)單而快速的方式刪除所有的數(shù)據(jù)箩言,如果使用了這種優(yōu)化硬贯,則顯示此值
-
Distinct
查找distinct值,當(dāng)找到第一個(gè)匹配的行后陨收,將停止為當(dāng)前行組合搜索更多行
-
FirstMatch(tbl_name)
當(dāng)前使用了半連接FirstMatch策略饭豹,詳見 https://mariadb.com/kb/en/firstmatch-strategy/ ,翻譯 https://www.cnblogs.com/abclife/p/10895624.html
-
Full scan on NULL key
子查詢中的一種優(yōu)化方式务漩,在無(wú)法通過(guò)索引訪問(wèn)null值的時(shí)候使用
-
Impossible HAVING
HAVING子句始終為false拄衰,不會(huì)命中任何行
-
Impossible WHERE
WHERE子句始終為false,不會(huì)命中任何行
-
Impossible WHERE noticed after reading const tables
MySQL已經(jīng)讀取了所有const(或system)表饵骨,并發(fā)現(xiàn)WHERE子句始終為false
-
LooseScan(m…n)
當(dāng)前使用了半連接LooseScan策略翘悉,詳見 https://mariadb.com/kb/en/loosescan-strategy/ ,翻譯 http://www.javacoder.cn/?p=39
-
No matching min/max row
沒(méi)有任何能滿足例如 SELECT MIN(…) FROM … WHERE condition 中的condition的行
-
no matching row in const table
對(duì)于關(guān)聯(lián)查詢居触,存在一個(gè)空表妖混,或者沒(méi)有行能夠滿足唯一索引條件
-
No matching rows after partition pruning
對(duì)于DELETE或UPDATE語(yǔ)句,優(yōu)化器在partition pruning(分區(qū)修剪)之后轮洋,找不到要delete或update的內(nèi)容
-
No tables used
當(dāng)此查詢沒(méi)有FROM子句或擁有FROM DUAL子句時(shí)出現(xiàn)制市。例如:explain select 1
-
Not exists
MySQL能對(duì)LEFT JOIN優(yōu)化,在找到符合LEFT JOIN的行后砖瞧,不會(huì)為上一行組合中檢查此表中的更多行息堂。例如:
SELECT * FROM t1 LEFT JOIN t2 ON t1.id=t2.id WHERE t2.id IS NULL;
假設(shè)t2.id定義成了
NOT NULL
,此時(shí),MySQL會(huì)掃描t1荣堰,并使用t1.id的值查找t2中的行床未。 如果MySQL在t2中找到一個(gè)匹配的行,它會(huì)知道t2.id永遠(yuǎn)不會(huì)為NULL振坚,并且不會(huì)掃描t2中具有相同id值的其余行薇搁。也就是說(shuō),對(duì)于t1中的每一行渡八,MySQL只需要在t2中只執(zhí)行一次查找啃洋,而不考慮在t2中實(shí)際匹配的行數(shù)。在MySQL 8.0.17及更高版本中屎鳍,如果出現(xiàn)此提示宏娄,還可表示形如 NOT IN (subquery) 或 NOT EXISTS (subquery) 的WHERE條件已經(jīng)在內(nèi)部轉(zhuǎn)換為反連接。這將刪除子查詢并將其表放入最頂層的查詢計(jì)劃中逮壁,從而改進(jìn)查詢的開銷孵坚。通過(guò)合并半連接和反聯(lián)接,優(yōu)化器可以更加自由地對(duì)執(zhí)行計(jì)劃中的表重新排序窥淆,在某些情況下卖宠,可讓查詢提速。你可以通過(guò)在EXPLAIN語(yǔ)句后緊跟一個(gè)SHOW WARNING語(yǔ)句忧饭,并分析結(jié)果中的Message列扛伍,從而查看何時(shí)對(duì)該查詢執(zhí)行了反聯(lián)接轉(zhuǎn)換。
Note
兩表關(guān)聯(lián)只返回主表的數(shù)據(jù)词裤,并且只返回主表與子表沒(méi)關(guān)聯(lián)上的數(shù)據(jù)刺洒,這種連接就叫反連接
-
Plan isn’t ready yet
使用了EXPLAIN FOR CONNECTION,當(dāng)優(yōu)化器尚未完成為在指定連接中為執(zhí)行的語(yǔ)句創(chuàng)建執(zhí)行計(jì)劃時(shí)亚斋, 就會(huì)出現(xiàn)此值作媚。
-
Range checked for each record (index map: N)
MySQL沒(méi)有找到合適的索引去使用,但是去檢查是否可以使用range或index_merge來(lái)檢索行時(shí)帅刊,會(huì)出現(xiàn)此提示。index map N索引的編號(hào)從1開始漂问,按照與表的SHOW INDEX所示相同的順序赖瞒。 索引映射值N是指示哪些索引是候選的位掩碼值。 例如0x19(二進(jìn)制11001)的值意味著將考慮索引1蚤假、4和5栏饮。
示例:下面例子中,name是varchar類型磷仰,但是條件給出整數(shù)型袍嬉,涉及到隱式轉(zhuǎn)換。
圖中t2也沒(méi)有用到索引,是因?yàn)椴樵冎拔覍2中name字段排序規(guī)則改為utf8_bin導(dǎo)致的鏈接字段排序規(guī)則不匹配伺通。explain select a.* from t1 a left join t2 b on t1.name = t2.name where t2.name = 2;
結(jié)果:
id select_type table partitions type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t2 NULL ALL idx_name NULL NULL NULL 9 11.11 Using where 1 SIMPLE t1 NULL ALL idx_name NULL NULL NULL 5 11.11 Range checked for each record (index map: 0x8) -
Recursive
出現(xiàn)了遞歸查詢箍土。詳見 “WITH (Common Table Expressions)”
-
Rematerialize
用得很少,使用類似如下SQL時(shí)罐监,會(huì)展示Rematerialize
SELECT ... FROM t, LATERAL (derived table that refers to t) AS dt ...
-
Scanned N databases
表示在處理INFORMATION_SCHEMA表的查詢時(shí)吴藻,掃描了幾個(gè)目錄,N的取值可以是0弓柱,1或者all沟堡。詳見 “Optimizing INFORMATION_SCHEMA Queries”
-
Select tables optimized away
優(yōu)化器確定:①最多返回1行;②要產(chǎn)生該行的數(shù)據(jù)矢空,要讀取一組確定的行航罗,時(shí)會(huì)出現(xiàn)此提示。一般在用某些聚合函數(shù)訪問(wèn)存在索引的某個(gè)字段時(shí)屁药,優(yōu)化器會(huì)通過(guò)索引直接一次定位到所需要的數(shù)據(jù)行完成整個(gè)查詢時(shí)展示伤哺,例如下面這條SQL。
explain select min(id) from t1;
-
Skip_open_table者祖, Open_frm_only立莉, Open_full_table
這些值表示適用于INFORMATION_SCHEMA表查詢的文件打開優(yōu)化;
- Skip_open_table:無(wú)需打開表文件七问,信息已經(jīng)通過(guò)掃描數(shù)據(jù)字典獲得
- Open_frm_only:僅需要讀取數(shù)據(jù)字典以獲取表信息
- Open_full_table:未優(yōu)化的信息查找蜓耻。表信息必須從數(shù)據(jù)字典以及表文件中讀取
-
Start temporary, End temporary
表示臨時(shí)表使用Duplicate Weedout策略,詳見 https://mariadb.com/kb/en/duplicateweedout-strategy/ 械巡,翻譯 https://www.cnblogs.com/abclife/p/10895531.html
-
unique row not found
對(duì)于形如 SELECT … FROM tbl_name 的查詢刹淌,但沒(méi)有行能夠滿足唯一索引或主鍵查詢的條件
-
Using filesort
當(dāng)Query 中包含 ORDER BY 操作,而且無(wú)法利用索引完成排序操作的時(shí)候讥耗,MySQL Query Optimizer 不得不選擇相應(yīng)的排序算法來(lái)實(shí)現(xiàn)有勾。數(shù)據(jù)較少時(shí)從內(nèi)存排序,否則從磁盤排序古程。Explain不會(huì)顯示的告訴客戶端用哪種排序蔼卡。官方解釋:“MySQL需要額外的一次傳遞,以找出如何按排序順序檢索行挣磨。通過(guò)根據(jù)聯(lián)接類型瀏覽所有行并為所有匹配WHERE子句的行保存排序關(guān)鍵字和行的指針來(lái)完成排序雇逞。然后關(guān)鍵字被排序,并按排序順序檢索行”
-
Using index
僅使用索引樹中的信息從表中檢索列信息茁裙,而不必進(jìn)行其他查找以讀取實(shí)際行塘砸。當(dāng)查詢僅使用屬于單個(gè)索引的列時(shí),可以使用此策略晤锥。例如:
explain SELECT id FROM t
-
Using index condition
表示先按條件過(guò)濾索引掉蔬,過(guò)濾完索引后找到所有符合索引條件的數(shù)據(jù)行廊宪,隨后用 WHERE 子句中的其他條件去過(guò)濾這些數(shù)據(jù)行。通過(guò)這種方式女轿,除非有必要箭启,否則索引信息將可以延遲“下推”讀取整個(gè)行的數(shù)據(jù)。詳見 “Index Condition Pushdown Optimization” 谈喳。例如:
TIPS
- MySQL分成了Server層和引擎層册烈,下推指的是將請(qǐng)求交給引擎層處理。
- 理解這個(gè)功能婿禽,可創(chuàng)建所以INDEX (zipcode, lastname, firstname)赏僧,并分別用如下指令,
``` SET optimizer_switch = 'index_condition_pushdown=off'; SET optimizer_switch = 'index_condition_pushdown=on'; ``` 開或者關(guān)閉索引條件下推扭倾,并對(duì)比: ``` explain SELECT * FROM people WHERE zipcode='95054' AND lastname LIKE '%etrunia%' AND address LIKE '%Main Street%'; ``` 的執(zhí)行結(jié)果淀零。
- index condition pushdown從MySQL 5.6開始支持,是MySQL針對(duì)特定場(chǎng)景的優(yōu)化機(jī)制膛壹,感興趣的可以看下 https://blog.51cto.com/lee90/2060449
-
Using index for group-by
數(shù)據(jù)訪問(wèn)和 Using index 一樣驾中,所需數(shù)據(jù)只須要讀取索引,當(dāng)Query 中使用GROUP BY或DISTINCT 子句時(shí)模聋,如果分組字段也在索引中肩民,Extra中的信息就會(huì)是 Using index for group-by。詳見 “GROUP BY Optimization”
-- name字段有索引 explain SELECT name FROM t1 group by name
-
Using index for skip scan
表示使用了Skip Scan链方。詳見 Skip Scan Range Access Method
-
Using join buffer (Block Nested Loop), Using join buffer (Batched Key Access)
使用Block Nested Loop或Batched Key Access算法提高join的性能持痰。詳見 https://www.cnblogs.com/chenpingzhao/p/6720531.html
-
Using MRR
使用了Multi-Range Read優(yōu)化策略。詳見 “Multi-Range Read Optimization”
-
Using sort_union(…), Using union(…), Using intersect(…)
這些指示索引掃描如何合并為index_merge連接類型祟蚀。詳見 “Index Merge Optimization” 工窍。
-
Using temporary
為了解決該查詢,MySQL需要?jiǎng)?chuàng)建一個(gè)臨時(shí)表來(lái)保存結(jié)果前酿。如果查詢包含不同列的GROUP BY和 ORDER BY子句患雏,通常會(huì)發(fā)生這種情況。
-- name無(wú)索引 explain SELECT name FROM t1 group by name
-
Using where
如果我們不是讀取表的所有數(shù)據(jù)罢维,或者不是僅僅通過(guò)索引就可以獲取所有需要的數(shù)據(jù)淹仑,則會(huì)出現(xiàn)using where信息
explain SELECT * FROM t1 where id > 5
-
Using where with pushed condition
僅用于NDB
-
Zero limit
該查詢有一個(gè)limit 0子句,不能選擇任何行
explain SELECT name FROM resource_template limit 0
擴(kuò)展的EXPLAIN
EXPLAIN可產(chǎn)生額外的擴(kuò)展信息言津,可通過(guò)在EXPLAIN語(yǔ)句后緊跟一條SHOW WARNING語(yǔ)句查看擴(kuò)展信息攻人。
TIPS
- 在MySQL 8.0.12及更高版本,擴(kuò)展信息可用于SELECT悬槽、DELETE、INSERT瞬浓、REPLACE初婆、UPDATE語(yǔ)句;在MySQL 8.0.12之前,擴(kuò)展信息僅適用于SELECT語(yǔ)句磅叛;
- 在MySQL 5.6及更低版本屑咳,需使用EXPLAIN EXTENDED xxx語(yǔ)句;而從MySQL 5.7開始弊琴,無(wú)需添加EXTENDED關(guān)鍵詞兆龙。
使用示例:
mysql> EXPLAIN
SELECT t1.a, t1.a IN (SELECT t2.a FROM t2) FROM t1\G
*************************** 1. row ***************************
id: 1
select_type: PRIMARY
table: t1
type: index
possible_keys: NULL
key: PRIMARY
key_len: 4
ref: NULL
rows: 4
filtered: 100.00
Extra: Using index
*************************** 2. row ***************************
id: 2
select_type: SUBQUERY
table: t2
type: index
possible_keys: a
key: a
key_len: 5
ref: NULL
rows: 3
filtered: 100.00
Extra: Using index
2 rows in set, 1 warning (0.00 sec)
mysql> SHOW WARNINGS\G
*************************** 1. row ***************************
Level: Note
Code: 1003
Message: /* select#1 */ select `test`.`t1`.`a` AS `a`,
<in_optimizer>(`test`.`t1`.`a`,`test`.`t1`.`a` in
( <materialize> (/* select#2 */ select `test`.`t2`.`a`
from `test`.`t2` where 1 having 1 ),
<primary_index_lookup>(`test`.`t1`.`a` in
<temporary table> on <auto_key>
where ((`test`.`t1`.`a` = `materialized-subquery`.`a`))))) AS `t1.a
IN (SELECT t2.a FROM t2)` from `test`.`t1`
1 row in set (0.00 sec)
由于SHOW WARNING的結(jié)果并不一定是一個(gè)有效SQL,也不一定能夠執(zhí)行(因?yàn)槔锩姘撕芏嗵厥鈽?biāo)記)敲董。特殊標(biāo)記取值如下:
-
<auto_key>
自動(dòng)生成的臨時(shí)表key
-
<cache>(expr)
表達(dá)式(例如標(biāo)量子查詢)執(zhí)行了一次紫皇,并且將值保存在了內(nèi)存中以備以后使用。對(duì)于包括多個(gè)值的結(jié)果腋寨,可能會(huì)創(chuàng)建臨時(shí)表聪铺,你將會(huì)看到
<temporary table>
的字樣 -
<exists>(query fragment)
子查詢被轉(zhuǎn)換為
EXISTS
-
<in_optimizer>(query fragment)
這是一個(gè)內(nèi)部?jī)?yōu)化器對(duì)象,對(duì)用戶沒(méi)有任何意義
-
<index_lookup>(query fragment)
使用索引查找來(lái)處理查詢片段萄窜,從而找到合格的行
-
<if>(condition, expr1, expr2)
如果條件是true铃剔,則取expr1,否則取expr2
-
<is_not_null_test>(expr)
驗(yàn)證表達(dá)式不為NULL的測(cè)試
-
<materialize>(query fragment)
使用子查詢實(shí)現(xiàn)
-
materialized-subquery.col_name
在內(nèi)部物化臨時(shí)表中對(duì)col_name的引用查刻,以保存子查詢的結(jié)果
-
<primary_index_lookup>(query fragment)
使用主鍵來(lái)處理查詢片段键兜,從而找到合格的行
-
<ref_null_helper>(expr)
這是一個(gè)內(nèi)部?jī)?yōu)化器對(duì)象,對(duì)用戶沒(méi)有任何意義
-
/* select#N */ select_stmt
SELECT與非擴(kuò)展的EXPLAIN輸出中id=N的那行關(guān)聯(lián)
-
outer_tables semi join (inner_tables)
半連接操作穗泵。inner_tables展示未拉出的表普气。詳見 “Optimizing Subqueries, Derived Tables, and View References with Semijoin Transformations”
-
<temporary table>
表示創(chuàng)建了內(nèi)部臨時(shí)表而緩存中間結(jié)果
當(dāng)某些表是const或system類型時(shí),這些表中的列所涉及的表達(dá)式將由優(yōu)化器盡早評(píng)估火欧,并且不屬于所顯示語(yǔ)句的一部分棋电。但是,當(dāng)使用FORMAT=JSON時(shí)苇侵,某些const表的訪問(wèn)將顯示為ref赶盔。
估計(jì)查詢性能
多數(shù)情況下,你可以通過(guò)計(jì)算磁盤的搜索次數(shù)來(lái)估算查詢性能榆浓。對(duì)于比較小的表于未,通常可以在一次磁盤搜索中找到行(因?yàn)樗饕赡芤呀?jīng)被緩存了)陡鹃,而對(duì)于更大的表烘浦,你可以使用B-tree索引進(jìn)行估算:你需要進(jìn)行多少次查找才能找到行:log(row_count) / log(index_block_length / 3 * 2 / (index_length + data_pointer_length)) + 1
在MySQL中,index_block_length通常是1024字節(jié)萍鲸,數(shù)據(jù)指針一般是4字節(jié)闷叉。比方說(shuō),有一個(gè)500,000的表脊阴,key是3字節(jié)握侧,那么根據(jù)計(jì)算公式 log(500,000)/log(1024/3*2/(3+4)) + 1 = 4
次搜索蚯瞧。
該索引將需要500,000 * 7 * 3/2 = 5.2MB的存儲(chǔ)空間(假設(shè)典型的索引緩存的填充率是2/3),因此你可以在內(nèi)存中存放更多索引品擎,可能只要一到兩個(gè)調(diào)用就可以找到想要的行了埋合。
但是,對(duì)于寫操作萄传,你需要四個(gè)搜索請(qǐng)求來(lái)查找在何處放置新的索引值甚颂,然后通常需要2次搜索來(lái)更新索引并寫入行。
前面的討論并不意味著你的應(yīng)用性能會(huì)因?yàn)閘og N而緩慢下降秀菱。只要內(nèi)容被OS或MySQL服務(wù)器緩存振诬,隨著表的變大,只會(huì)稍微變慢答朋。在數(shù)據(jù)量變得太大而無(wú)法緩存后贷揽,將會(huì)變慢很多,直到你的應(yīng)用程序受到磁盤搜索約束(按照l(shuí)og N增長(zhǎng))梦碗。為了避免這種情況禽绪,可以根據(jù)數(shù)據(jù)的增長(zhǎng)而增加key的。對(duì)于MyISAM表洪规,key的緩存大小由名為key_buffer_size的系統(tǒng)變量控制印屁,詳見 Section 5.1.1, “Configuring the Server”
參考文檔
- EXPLAIN Output Format
- EXPLAIN Statement
- Extended EXPLAIN Output Format
- Estimating Query Performance
- MySQL中explain執(zhí)行計(jì)劃中額外信息字段(Extra)詳解
- explain參數(shù)詳解
- 最官方的 mysql explain type 字段解讀
- What does eq_ref and ref types mean in MySQL explain
- 面試官:不會(huì)看 Explain執(zhí)行計(jì)劃,簡(jiǎn)歷敢寫 SQL 優(yōu)化斩例?
點(diǎn)擊查看更多內(nèi)容
作者:大目
鏈接:https://www.imooc.com/article/308225
來(lái)源:慕課網(wǎng)
本文原創(chuàng)發(fā)布于慕課網(wǎng) 雄人,轉(zhuǎn)載請(qǐng)注明出處,謝謝合作