EXPLAIN 執(zhí)行計(jì)劃
有了慢查詢語(yǔ)句后,就要對(duì)語(yǔ)句進(jìn)行分析。一條查詢語(yǔ)句在經(jīng)過(guò) MySQL 查詢優(yōu)化器的各種基于成本和規(guī)則的優(yōu)化會(huì)后生成一個(gè)所謂的執(zhí)行計(jì)劃,這個(gè)執(zhí)行計(jì)劃展示了接下來(lái)具體執(zhí)行查詢的方式,比如多表連接的順序是什么,對(duì)于每個(gè)表采用什么訪問(wèn)方法來(lái)具體執(zhí)行查詢等等。EXPLAIN 語(yǔ)句來(lái)能夠查看某個(gè)查詢語(yǔ)句的具體執(zhí)行計(jì)劃,要搞懂 EPLATN 的各個(gè)輸出項(xiàng)都有什么作用,從而可以有針對(duì)性的提升查詢語(yǔ)句的性能。
通過(guò)使用 EXPLAIN 關(guān)鍵字可以模擬優(yōu)化器執(zhí)行 SQL 查詢語(yǔ)句,從而知道 MySQL 是如何處理 SQL 語(yǔ)句的。分析查詢語(yǔ)句或是表結(jié)構(gòu)的性能瓶頸。
EXPLAIN 可以得到以下結(jié)果:
- 表的讀取順序;
- 數(shù)據(jù)讀取操作的操作類(lèi)型;
- 哪些索引可以使用;
- 哪些索引被實(shí)際使用;
- 表之間的引用;
- 每張表有多少行被優(yōu)化器查詢;
EXPLAIN 基本語(yǔ)法
執(zhí)行計(jì)劃的語(yǔ)法非常簡(jiǎn)單:在 SQL 查詢的前面加上 EXPLAIN 關(guān)鍵字。比如:EXPLAIN select * from tableName;
除了以 SELECT 開(kāi)頭的查詢語(yǔ)句,其余的 DELETE、INSERT、UPOATE 語(yǔ)句前邊都可以加上 EXPLAIN,用來(lái)查看這些語(yǔ)句的執(zhí)行計(jì)劃,只不過(guò)大多數(shù)情況下都會(huì)對(duì) SELECT 語(yǔ)句更感興趣。
EXPLAIN 詳解
執(zhí)行 sql explain SELECT * FROM order_test;
輸出結(jié)果如圖:
每列的含義
id
查詢語(yǔ)句一般都以 SELECT 關(guān)鍵字開(kāi)頭,比較簡(jiǎn)單的查詢語(yǔ)句里只有一個(gè) SELECT 關(guān)鍵字,稍微復(fù)雜一點(diǎn)的連接查詢中也只有一個(gè) SELECT 關(guān)鍵字,比如:
explain SELECT * FROM test1 t1 INNER JOIN test2 t2 ON t1.id=t2.id ;
但是下邊兩種情況下在一條查詢語(yǔ)句中會(huì)出現(xiàn)多個(gè) SELECT 關(guān)鍵字:
-
查詢中包含子查詢的情況:explain SELECT * FROM test1 WHERE id IN(SELECT id FROM test2);
-
查詢中包含 UNION / UNION ALL 語(yǔ)句的情況:EXPLAIN SELECT * FROM test1 UNION ALL SELECT * FROM test2;
查詢語(yǔ)句中每出現(xiàn)一個(gè) SELECT 關(guān)鍵字,MySQL 就會(huì)為它分配一個(gè)唯一的 id 值。這個(gè) id 值就是 EXPLAIN 語(yǔ)句的第一列的值,并且 id 的順序是按 SELECT 出現(xiàn)的順序增長(zhǎng)的,id 列越大執(zhí)行優(yōu)先級(jí)越高,id 相同則從上往下執(zhí)行,id 為 NULL 最后執(zhí)行。
單 SELECT 關(guān)鍵字
比如下邊這個(gè)查詢中只有一個(gè) SELECT 關(guān)鍵字,所以 EXPLAIN 的結(jié)果中也就只有一條 id 列為 1 的記錄。
連接查詢 對(duì)于連接查詢來(lái)說(shuō),一個(gè) SELECT 關(guān)鍵字后邊的 FROM 子句中可以跟隨多個(gè)表,所以在連接查詢的執(zhí)行計(jì)劃中,每個(gè)表都會(huì)對(duì)應(yīng)一條記錄,但是這些記錄的 id 值都是相同的。
可以看到,上述連接查詢中參與連接的 test1 和 test2 表分別對(duì)應(yīng)一條記錄,但是這兩條記錄對(duì)應(yīng)的 id 值都是 1,在連接查詢的執(zhí)行計(jì)劃中,每個(gè)表都會(huì)對(duì)應(yīng)一條記錄,這些記錄的 id 列的值是相同的。
包含子查詢
對(duì)于包含子查詢的查詢語(yǔ)句來(lái)說(shuō),就可能涉及多個(gè) SELECT 關(guān)鍵字,所以在包含子查詢的查詢語(yǔ)句的執(zhí)行計(jì)劃中,每個(gè) SELECT 關(guān)鍵字都會(huì)對(duì)應(yīng)一個(gè)唯一的 id 值。
但是這里需要特別注意,查詢優(yōu)化器可能對(duì)涉及子查詢的查詢語(yǔ)句進(jìn)行重寫(xiě),從而轉(zhuǎn)換為連接查詢。所以如果我們想知道查詢優(yōu)化器對(duì)某個(gè)包含子查詢的語(yǔ)句是否進(jìn)行了重寫(xiě),直接查看執(zhí)行計(jì)劃。
可以看到,雖然查詢語(yǔ)句是一個(gè)子查詢,但是執(zhí)行計(jì)劃中 test1 和 test2 表對(duì)應(yīng)的記錄的 id 值全部是 1,這就表明了查詢優(yōu)化器將子查詢轉(zhuǎn)換為了連接查詢。
包含 UNION \ UNION ALL 子句
對(duì)于包含 UNION 子句的查詢語(yǔ)句來(lái)說(shuō),每個(gè) SELECT 關(guān)鍵字對(duì)應(yīng)一個(gè) id 值也是沒(méi)錯(cuò)的,不過(guò)還是有點(diǎn)兒特別。
這個(gè)語(yǔ)句的執(zhí)行計(jì)劃的第三條記錄是因?yàn)?UNION 子句會(huì)把多個(gè)查詢的結(jié)果集合并起來(lái)并對(duì)結(jié)果集中的記錄進(jìn)行去重,MySQL 使用的是內(nèi)部的臨時(shí)表。UNION 子句是為了把 id 為 1 的查詢和 id 為 2 的查詢的結(jié)果集合并起來(lái)并去重,所以在內(nèi)部創(chuàng)建了一個(gè)名為 <union1,2> 的臨時(shí)表,就是執(zhí)行計(jì)劃第三條記錄的 table 列的名稱,id 為 NULL 表明這個(gè)臨時(shí)表是為了合并兩個(gè)查詢的結(jié)果集而創(chuàng)建的。
跟 UNION 對(duì)比起來(lái) UNION ALL 就不需要為最終的結(jié)果集進(jìn)行去重,它只是單純的把多個(gè)查詢的結(jié)果集中的記錄合并成一個(gè)并返回給用戶,所以也就不需要使用臨時(shí)表。所以在包含 UNION ALL 子句的查詢的執(zhí)行計(jì)劃中,就沒(méi)有那個(gè) id 為 NULL 的記錄。
select_type
一條大的查詢語(yǔ)句里邊可以包含若干個(gè) SELECT 關(guān)鍵字,每個(gè) SELECT 關(guān)鍵字代表著一個(gè)小的查詢語(yǔ)句,而每個(gè) SELECT 關(guān)鍵字的 FROM 子句中都可以包含若干張表,每一張表都對(duì)應(yīng)著執(zhí)行計(jì)劃輸出中的一條記錄,對(duì)于在同一個(gè) SELECT 關(guān)鍵字中的表來(lái)說(shuō),它們的 id 值是相同的。
MySQL 為每一個(gè) SELECT 關(guān)鍵字代表的小查詢都定義了一個(gè)稱之為:select_type 的屬性,意思是我們只要知道了某個(gè)小查詢的 select_type 屬性,就知道了這個(gè)小查詢?cè)谡麄€(gè)大查詢中扮演了一個(gè)什么角色,select_type 取值如下:
- SIMPLE:簡(jiǎn)單的 SELECT 查詢,不使用 union 及子查詢;
- PRIMARY:最外層的 SELECT 查詢;
- UNION:UNION 中的第二個(gè)或隨后的 SELECT 查詢,不依賴于外部查詢的結(jié)果集;
- UNION RESULT:UNION 結(jié)果集;
- SUBQUERY:子查詢中的第一個(gè) SELECT 查詢,不依賴于外部查詢的結(jié)果集;
- DERIVED: 用于 FROM 子句里有子查詢的情況,MySQL 會(huì)遞歸執(zhí)行這些子查詢,把結(jié)果放在臨時(shí)表里;
SIMPLE
簡(jiǎn)單的 select 查詢,查詢中不包含子查詢或者 UNION。
連接查詢也算是 SIMPLE 類(lèi)型。
PRIMARY
對(duì)于包含 UNION、UNION ALL 或者子查詢的大查詢來(lái)說(shuō),它是由幾個(gè)小查詢組成的,其中最左邊的那個(gè)查詢的 select_type 值就是 PRIMARY。
從結(jié)果中可以看到,最左邊的小查詢 SELECT * FROMN test1
對(duì)應(yīng)的是執(zhí)行計(jì)劃中的第一條記錄,它的 select_type 值就是 PRIMARY。
UNION
對(duì)于包含 UNION 或者 UNION ALL 的大查詢來(lái)說(shuō),它是由幾個(gè)小查詢組成的,其中除了最左邊的那個(gè)小查詢以外,其余的查詢的 select_type 值就是 UNION。
UNION RESULT
MySQL 選擇使用臨時(shí)表來(lái)完成 UNION 查詢的去重工作,針對(duì)該臨時(shí)表的查詢的 select_type 就是 UNION RESULT,如上圖。
SUBQUERY
包含在 SELECT 中的子查詢,不在 FROM 子句中。
DERIVED
包含在 FROM 子句中的子查詢。MySQL 會(huì)將結(jié)果存放在一個(gè)臨時(shí)表中,也稱為派生表。
從執(zhí)行計(jì)劃中可以看出, id 為 2 的記錄就代表子查詢的執(zhí)行方式,它的 select_type 是 DERIVED ,說(shuō)明該子查詢是以派生表的方式執(zhí)行的。id 為 1 的記錄代表外層查詢,注意看它的 table 列顯示的是 <derived2>
,表示該查詢是針對(duì)將派生表之后的表進(jìn)行查詢的。
table
不論我們的查詢語(yǔ)句有多復(fù)雜,里邊包含了多少個(gè)表,到最后也是需要對(duì)每個(gè)表進(jìn)行單表訪問(wèn)的,MySQL 規(guī)定 EXPLAIN 語(yǔ)句輸出的每條記錄都對(duì)應(yīng)著某個(gè)單表的訪問(wèn)方法,該條記錄的 table 列代表著該表的表名。
只涉及對(duì) test1 表的單表查詢,所以 EXPLAIN 輸出中只有一條記錄,其中的 table 列的值是 test1;
連接查詢的執(zhí)行計(jì)劃中有兩條記錄,這兩條記錄的 table 列分別是 test1和 test2。
partitions
和分區(qū)表有關(guān),一般情況下查詢語(yǔ)句的執(zhí)行計(jì)劃的 partitions 列的值都是NULL。
type
執(zhí)行計(jì)劃的一條記錄就代表著 MySQL 對(duì)某個(gè)表的執(zhí)行查詢時(shí)的訪問(wèn)方法或訪問(wèn)類(lèi)型,其中的 type 列就表明了這個(gè)訪問(wèn)方法或訪問(wèn)類(lèi)型是較為重要的一個(gè)指標(biāo),結(jié)果值從最好到最壞依次是:system > const > eq_ref > ref > range > index > ALL
。
一般來(lái)說(shuō),得保證查詢至少達(dá)到 range 級(jí)別,最好能達(dá)到 ref。
system
當(dāng)表中只有一條記錄并且該表使用的存儲(chǔ)引擎的統(tǒng)計(jì)數(shù)據(jù)是精確的,比如:MyISAM、Memory,那么對(duì)該表的訪問(wèn)方法就是 system。
如果改成使用 InnoDB 存儲(chǔ)引擎,type 的值就是 all。
const
根據(jù)主鍵或者唯一二級(jí)索引列與常數(shù)進(jìn)行等值匹配時(shí),對(duì)單表的訪問(wèn)方法就是 const。因?yàn)橹黄ヅ湟恍袛?shù)據(jù),所以很快。
B+ Tree 葉子節(jié)點(diǎn)中的記錄是按照索引列排序的,對(duì)于的聚簇索引來(lái)說(shuō),它對(duì)應(yīng)的B+樹(shù)葉子節(jié)點(diǎn)中的記錄就是按照id列排序的。B+樹(shù)矮胖,所以這樣根據(jù)主鍵值定位一條記錄的速度很快。類(lèi)似的,我們根據(jù)唯一二級(jí)索引列來(lái)定位一條記錄的速度也很快的,比如下邊這個(gè)查詢:
eq_ref
在連接查詢時(shí),如果被驅(qū)動(dòng)表是通過(guò)主鍵或者唯一二級(jí)索引列等值匹配的方式進(jìn)行訪問(wèn)的,如果該主鍵或者唯一二級(jí)索引是聯(lián)合索引的話,所有的索引列都必須進(jìn)行等值比較,則對(duì)該被驅(qū)動(dòng)表的訪問(wèn)方法就是:eq_ref。
從執(zhí)行計(jì)劃的結(jié)果中可以看出,MySQL 打算將 test2 作為驅(qū)動(dòng)表,test1 作為被驅(qū)動(dòng)表,重點(diǎn)關(guān)注 test1 的訪問(wèn)方法是 eq_ref,表明在訪問(wèn) test1 表的時(shí)候可以通過(guò)主鍵的等值匹配來(lái)進(jìn)行訪問(wèn)。
驅(qū)動(dòng)表與被驅(qū)動(dòng)表:A 表和 B 表 join 連接查詢,如果通過(guò) A 表的結(jié)果集作為循環(huán)基礎(chǔ)數(shù)據(jù),然后一條一條地通過(guò)該結(jié)果集中的數(shù)據(jù)作為過(guò)濾條件到 B 表中查詢數(shù)據(jù),然后合并結(jié)果。那么我們稱 A 表為驅(qū)動(dòng)表,B 表為被驅(qū)動(dòng)表。
ref
當(dāng)通過(guò)普通的二級(jí)索引列與常量進(jìn)行等值匹配時(shí)來(lái)查詢某個(gè)表,那么對(duì)該表的訪問(wèn)方法就可能是 ref。
本質(zhì)上也是一種索引訪問(wèn),它返回所有匹配某個(gè)單獨(dú)值的行,它可能會(huì)找到多個(gè)符合條件的行,所以他屬于查找和掃描的混合體。
對(duì)于這個(gè)查詢可以選擇全表掃描來(lái)逐一對(duì)比搜索條件是否滿足要求,也可以先使用二級(jí)索引找到對(duì)應(yīng)記錄的 id 值,然后再回表到聚簇索引中查找完整的用戶記錄。
由于普通二級(jí)索引并不限制索引列值的唯一性,所以可能找到多條對(duì)應(yīng)的記錄,也就是說(shuō)使用二級(jí)索引來(lái)執(zhí)行查詢的代價(jià)取決于等值匹配到的二級(jí)索引記錄條數(shù)。
如果匹配的記錄較少,則回表的代價(jià)還是比較低的,所以 MySQL 可能選擇使用索引而不是全表掃描的方式來(lái)執(zhí)行查詢。這種搜索條件為二級(jí)索引列與常數(shù)等值比較,采用二級(jí)索引來(lái)執(zhí)行查詢的訪問(wèn)方法稱為:ref。
對(duì)于普通的二級(jí)索引來(lái)說(shuō),通過(guò)索引列進(jìn)行等值比較后可能匹配到多條連續(xù)的記錄,而不是像主鍵或者唯一二級(jí)索引那樣最多只能匹配 1 條記錄,所以這種 ref 訪問(wèn)方法比 const 要差些,但是在二級(jí)索引等值比較時(shí)匹配的記錄數(shù)較少時(shí)的效率還是很高的,如果匹配的二級(jí)索引記錄太多那么回表的成本就太大了。
對(duì)于某個(gè)包含多個(gè)索引列的二級(jí)索引來(lái)說(shuō),只要是最左邊的連續(xù)索引列是與常數(shù)的等值比較就可能采用 ref 的訪問(wèn)方法。
range
如果使用索引獲取某些范圍區(qū)間的記錄,那么就可能使用到 range 訪問(wèn)方法,一般就是在 where 語(yǔ)句中出現(xiàn)了 between
、<
、>
、in
等的查詢。
這種范圍掃描索引比全表掃描要好,因?yàn)樗恍枰_(kāi)始于索引的某一點(diǎn),而結(jié)束語(yǔ)另一點(diǎn),不用掃描全部索引。
這種利用索引(聚簇索引、二級(jí)索引)進(jìn)行范圍匹配的訪問(wèn)方法稱之為:range。
index
可以使用索引覆蓋,但需要掃描全部的索引記錄時(shí),該表的訪問(wèn)方法就是:index。
ALL
最熟悉的全表掃描,將遍歷全表以找到匹配的行。
possible_keys 和 key
在 EXPLAIN 語(yǔ)句輸出的執(zhí)行計(jì)劃中,possible_keys 列表示在某個(gè)查詢語(yǔ)句中,對(duì)某個(gè)表執(zhí)行單表查詢時(shí)可能用到的索引有哪些。key 列表示實(shí)際用到的索引有哪些,如果為 NULL,則沒(méi)有使用索引。
上述執(zhí)行計(jì)劃的 possible_keys 列的值表示該查詢可能使用到 u_idx_day_status、idx_insert_time 兩個(gè)索引,然后 key 列的值是 u_idx_day_status,表示經(jīng)過(guò)查詢優(yōu)化器計(jì)算使用不同索引的成本后,最后決定使用 u_idx_day_status 來(lái)執(zhí)行查詢比較劃算。
key_len
key_len 列,表示當(dāng)優(yōu)化器決定使用某個(gè)索引執(zhí)行查詢時(shí),該索引記錄的最大長(zhǎng)度,計(jì)算方式如下:
- 對(duì)于使用固定長(zhǎng)度類(lèi)型的索引列來(lái)說(shuō),它實(shí)際占用的存儲(chǔ)空間的最大長(zhǎng)度就是該固定值,對(duì)于指定字符集的變長(zhǎng)類(lèi)型的索引列來(lái)說(shuō),如:某個(gè)索引列的類(lèi)型是VARCHAR(100),使用的字符集是 utf8,那么該列實(shí)際占用的最大存儲(chǔ)空間就是100x3=300個(gè)字節(jié);
- 如果該索引列可以存儲(chǔ) NULL 值,則 key_len 比不可以存儲(chǔ) NULL 值時(shí)多 1 個(gè)字節(jié);
- 對(duì)于變長(zhǎng)字段來(lái)說(shuō),都會(huì)有 2 個(gè)字節(jié)的空間來(lái)存儲(chǔ)該變長(zhǎng)列的實(shí)際長(zhǎng)度;
由于 id 列的類(lèi)型是 bigint,并且不可以存儲(chǔ) NULL 值,所以在使用該列的索引時(shí) key_len 大小就是 8。
對(duì)于可變長(zhǎng)度的索引列來(lái)說(shuō)。
由于 order_no 列的類(lèi)型是 VARCHAR(50),所以該列實(shí)際最多占用的存儲(chǔ)空間就是 50*3 字節(jié),又因?yàn)樵摿惺强勺冮L(zhǎng)度列,所以 key_len 需要加2,所以最后ken_len 的值就是 152。
執(zhí)行計(jì)劃的生成是在 MySQL server 層中的功能,并不是針對(duì)具體某個(gè)存儲(chǔ)引擎的功能,MySQL 在執(zhí)行計(jì)劃中輸出 key_len 列主要是為了區(qū)分某個(gè)使用聯(lián)合索引的查詢具體用了幾個(gè)索引列,而不是為了準(zhǔn)確的說(shuō)明針對(duì)某個(gè)具體存儲(chǔ)引擎存儲(chǔ)變長(zhǎng)字段的實(shí)際長(zhǎng)度占用的空間到底是占用幾個(gè)字節(jié)。
key_len 表示索引中使用的字節(jié)數(shù),可通過(guò)該列計(jì)算查詢中使用的索引的長(zhǎng)度。在不損失精確性的情況下,長(zhǎng)度越短越好。
key_len 顯示的值為索引字段的最大可能長(zhǎng)度,并非實(shí)際使用長(zhǎng)度,即 key_len 是根據(jù)表定義計(jì)算而得,不是通過(guò)表內(nèi)檢索出的。
注:char 和 varchar 跟字符編碼也有密切的聯(lián)系,比如 latin1 占用 1 個(gè)字節(jié),gbk 占用 2 個(gè)字節(jié),utf8 占用 3 個(gè)字節(jié)。
ref
當(dāng)使用索引列等值查詢時(shí),與索引列進(jìn)行等值匹配的對(duì)象信息;
可以看到 ref 列的值是 const,表明在使用 idx_order_no 索引執(zhí)行查詢時(shí),與 order_no 列作等值匹配的對(duì)象是一個(gè)常數(shù)。
復(fù)雜的情況。
可以看到對(duì)被驅(qū)動(dòng)表 t1 的訪問(wèn)方法是 eg_ref,而對(duì)應(yīng)的 ref 列的值是 test.t2.id,這說(shuō)明在對(duì)被驅(qū)動(dòng)表進(jìn)行訪問(wèn)時(shí)會(huì)用到 PRIMARY 索引,也就是聚簇索引與一個(gè)列進(jìn)行等值匹配的條件,與 t2 表的 id 作等值匹配的對(duì)象就是 test.t2.id列,格式:數(shù)據(jù)庫(kù)名稱.表名.字段。
rows
如果查詢優(yōu)化器決定使用全表掃描的方式對(duì)某個(gè)表執(zhí)行查詢時(shí),執(zhí)行計(jì)劃的 rows 列就代表預(yù)計(jì)需要掃描的行數(shù),如果使用索引來(lái)執(zhí)行查詢時(shí),執(zhí)行計(jì)劃的 rows 列就代表預(yù)計(jì)掃描的索引記錄行數(shù)。
filtered
某個(gè)表經(jīng)過(guò)搜索條件過(guò)濾后剩余記錄條數(shù)的百分比;
從執(zhí)行計(jì)劃的 key 列中可以看出來(lái),該查詢使用 PRIMARY 索引來(lái)執(zhí)行查詢,從 rows 列可以看出滿足 id>5890
的記錄有 5177 條。執(zhí)行計(jì)劃的 filtered 列就代表查詢優(yōu)化器預(yù)測(cè)在這 5177 條記錄中,有多少條記錄滿足其余的搜索條件,也就是 order_note='a' 這個(gè)條件的百分比。此處 filtered 列的值是 10.0,說(shuō)明查詢優(yōu)化器預(yù)測(cè)在 5177 條記錄中有 10.00% 的記錄滿足 order_note='a' 這個(gè)條件。
Extra
Extra 列是用來(lái)說(shuō)明一些額外信息的,可以通過(guò)這些額外信息來(lái)更準(zhǔn)確的理解 MySQL 到底將如何執(zhí)行給定的查詢語(yǔ)句。MySQL 提供的額外信息很多,常見(jiàn)的重要值如下:
Using index:當(dāng)我們的查詢列表以及搜索條件中只包含屬于某個(gè)索引的列,也就是在可以使用索引覆蓋的情況下,在 Extra 列將會(huì)提示該額外信息。
這個(gè)查詢中只需要用到 u_idx_day_status 而不需要回表操作。
Using where:當(dāng)我們使用全表掃描來(lái)執(zhí)行對(duì)某個(gè)表的查詢,并且該語(yǔ)句的 WHERE 子句中有針對(duì)該表的搜索條件時(shí),在 Extra 列中會(huì)提示。
Using where 只是表示 MySQL 使用 where 子句中的條件對(duì)記錄進(jìn)行了過(guò)濾。
Using index condition:有些搜索條件中雖然出現(xiàn)了索引列,但卻不能使用到索引。
其中的 order_no >'z'
可以使用到索引,但是 order_no LIKE '%a'
卻無(wú)法使用到索引,在以前版本的 MySQL 中,是按照下邊步驟來(lái)執(zhí)行這個(gè)查詢的:
- 先根據(jù) order_no>'z' 這個(gè)條件,從二級(jí)索引 idx_order_no 中獲取到對(duì)應(yīng)的二級(jí)索引記錄;
- 根據(jù)上一步驟得到的二級(jí)索引記錄中的主鍵值進(jìn)行回表,找到完整的用戶記錄再檢測(cè)該記錄是否符合 order_no LIKE '%a' 這個(gè)條件,將符合條件的記錄加入到最后的結(jié)果集;
雖然 order_no LIKE '%a'
不能組成范圍區(qū)間參與 range 訪問(wèn)方法的執(zhí)行,但這個(gè)條件畢竟只涉及到了 order_no 列,MySQL 把上邊的步驟改進(jìn)了一下。
索引條件下推
- 先根據(jù) order_no>'z' 這個(gè)條件,定位到二級(jí)索引 idx_order_no 中對(duì)應(yīng)的二級(jí)索引記錄;
- 對(duì)于指定的二級(jí)索引記錄,先不著急回表,而是先檢測(cè)一下該記錄是否滿足 order_no LIKE '%a' 這個(gè)條件,如果這個(gè)條件不滿足,則該二級(jí)索引記錄壓根兒就沒(méi)必要回表;
- 對(duì)于滿足 order_no LIKE '%a' 這個(gè)條件的二級(jí)索引記錄執(zhí)行回表操作,回表操作其實(shí)是一個(gè)隨機(jī) IO 比較耗時(shí);
所以上述修改可以省去很多回表操作的成本,這個(gè)改進(jìn)稱之為索引條件下推。
如果在查詢語(yǔ)句的執(zhí)行過(guò)程中將要使用索引條件下推這個(gè)特性,在 Extra 列中將會(huì)顯示 Using index condition。
Using temporary:在許多查詢的執(zhí)行過(guò)程中,MySQL 可能會(huì)借助臨時(shí)表來(lái)完成一些功能,比如去重、排序之類(lèi)的,比如在執(zhí)行許多包含 DISTINCT、GROUPBY、UNION 等子句的查詢過(guò)程中,如果不能有效利用索引來(lái)完成查詢,MySQL 很有可能尋求通過(guò)建立內(nèi)部的臨時(shí)表來(lái)執(zhí)行查詢。如果查詢中使用到了內(nèi)部的臨時(shí)表,在執(zhí)行計(jì)劃的 Extra 列將會(huì)顯示 Using temporary。
上邊的 GROUP BY 的執(zhí)行計(jì)劃的 Extra 列不僅僅包含 Using temporary 提示,還包含 Using filesort 提示,可是查詢語(yǔ)句中明明沒(méi)有寫(xiě) ORDER BY 子句,這是因?yàn)?MySQL 會(huì)在包含 GROUP BY 子句的查詢中默認(rèn)添加上 ORDER BY 子句。
如果不想為包含 GROUP BY 子句的查詢進(jìn)行排序,需要顯式的寫(xiě)上 ORDER BY NULL。
Using filesort:有一些情況下對(duì)結(jié)果集中的記錄進(jìn)行排序是可以使用到索引的。
這個(gè)查詢語(yǔ)句可以利用 idx_order_no 索引直接取出 order_no 列的 10 條記錄,然后再進(jìn)行回表操作。但是很多情況下排序操作無(wú)法使用到索引,只能在內(nèi)存中或者磁盤(pán)中進(jìn)行排序,MySQL 把這種在內(nèi)存中或者磁盤(pán)上進(jìn)行排序的方式統(tǒng)稱為文件排序。如果某個(gè)查詢需要使用文件排序的方式執(zhí)行查詢,就會(huì)在執(zhí)行計(jì)劃的Extra 列中顯示 Using filesort。
Select tables optimized away:使用某些聚合函數(shù),比如:max、min 來(lái)訪問(wèn)存在索引的某個(gè)字段是。
作者:京東物流?張士欣文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-748217.html
來(lái)源:京東云開(kāi)發(fā)者社區(qū) 自猿其說(shuō)Tech 轉(zhuǎn)載請(qǐng)注明來(lái)源文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-748217.html
到了這里,關(guān)于MYSQL EXPLAIN 執(zhí)行計(jì)劃的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!