MySQL的執(zhí)行計劃詳解(Explain)

1、MySQL執(zhí)行計劃的定義
在 MySQL 中可以通過 explain 關(guān)鍵字模擬優(yōu)化器執(zhí)行 SQL語句,從而知道 MySQL 是如何處理 SQL 語句的。

2、MySQL整個查詢的過程
? 客戶端向 MySQL 服務(wù)器發(fā)送一條查詢請求
? 服務(wù)器首先檢查查詢緩存,如果命中緩存,則立刻返回存儲在緩存中的結(jié)果。否則進(jìn)入下一階段
? 服務(wù)器進(jìn)行 SQL 解析、預(yù)處理、再由優(yōu)化器生成對應(yīng)的執(zhí)行計劃
? MySQL 根據(jù)執(zhí)行計劃,調(diào)用存儲引擎的 API 來執(zhí)行查詢
? 將結(jié)果返回給客戶端,同時緩存查詢結(jié)果
注意:只有在8.0之前才有查詢緩存,8.0之后查詢緩存被去掉了

3、如何啟動執(zhí)行計劃
explain select 投影列 FROM 表名 WHERE 條件 ;

4、explain中的列

4.1、id
查詢執(zhí)行順序:
id 值相同時表示從上向下執(zhí)行
id 值相同被視為一組
如果是子查詢,id 值會遞增,id 值越高,優(yōu)先級越高
id為NULL最后執(zhí)行。

4.2、select_type
● simple:表示查詢中不包含子查詢或者 union
EXPLAIN select * from actor where id=1;

● primary:當(dāng)查詢中包含任何復(fù)雜的子部分,最外層的查詢被標(biāo)記成 primary
● derived:在 from 的列表中包含的子查詢被標(biāo)記成 derived
● subquery:在 select 或 where 列表中包含了子查詢,則子查詢被標(biāo)記成 subquery
用個例子來了解primary、subquery和derived
set session optimizer_switch=‘derived_merge=off’;#關(guān)閉mysql5.7新特性對衍生表的合并優(yōu)化
explain select (select 1 from actor where id = 1) from (select * from film where id = 1) der;

set session optimizer_switch=‘derived_merge=on’; #還原默認(rèn)配置
● union:兩個 select 查詢時前一個標(biāo)記為 PRIMARY,后一個標(biāo)記為 UNION。union 出現(xiàn)在 from 從句子查詢中,外層 select 標(biāo)記為 PIRMARY,union 中第一個查詢?yōu)?DERIVED,第二個子查詢標(biāo)記為 UNION
explain select 1 union all select 1;

● unionresult:從 union 表獲取結(jié)果的 select 被標(biāo)記成 union result 。

4.3、table
顯示這一行的數(shù)據(jù)是關(guān)于哪張表的。
當(dāng) from 子句中有子查詢時,table列是 格式,表示當(dāng)前查詢依賴 id=N 的查詢,于是先執(zhí)行 id=N 的查詢。
當(dāng)有 union 時,UNION RESULT 的 table 列的值為<union1,2>,1和2表示參與 union 的 select 行id。
1
2
3
4.4、type
這是重要的列,顯示連接使用了何種類型。
SQL 性能優(yōu)化的目標(biāo):至少要達(dá)到 range 級別,要求是 ref 級別,如果可以是 consts 最好。
說明:
1) consts 單表中最多只有一個匹配行(主鍵或者唯一索引),在優(yōu)化階段即可讀取到數(shù)據(jù)。
2) ref 指的是使用普通的索引(normal index)。
3) range 對索引進(jìn)行范圍檢索。
反例:explain 表的結(jié)果,type=index,索引物理文件全掃描,速度非常慢,這個 index 級別比較 range
還低,與全表掃描是小巫見大巫。

從最好到最差的連接類型為 system > const > eq_reg > ref > range > index > ALL。
一般來說,得保證查詢達(dá)到range級別,最好達(dá)到ref
● NULL:mysql能夠在優(yōu)化階段分解查詢語句,在執(zhí)行階段用不著再訪問表或索引。例如:在索引列中選取最小值,可以單獨查找索引來完成,不需要在執(zhí)行時訪問表
explain select min(id) from film;

● system:表中只有一行數(shù)據(jù)。屬于 const 的特例。如果物理表中就一行數(shù)據(jù)為 ALL
● const :查詢結(jié)果最多有一個匹配行。因為只有一行,所以可以被視為常量。const 查詢速度非???,因為只讀一次。一般情況下把主鍵或唯一索引作為唯一條件的查詢都是 const
explain select * from (select * from film where id = 1) tmp;

● eq_ref:唯一性索引掃描,對于每個索引鍵,表中只有一條記錄與之匹配。常見于主鍵或唯一索引掃描。
explain select * from film_actor left join film on film_actor.film_id = film.id

● ref:非唯一性索引掃描,返回匹配某個單獨值得所有行。索引要和某個值相比較,可能會找到多個符合條件的行。

1.簡單 select 查詢,name是普通索引(非唯一索引)
explain select * from film where name = ‘film1’;

2.關(guān)聯(lián)表查詢,idx_film_actor_id是film_id和actor_id的聯(lián)合索引,這里使用到了film_actor的左邊前綴film_id部分
explain select film_id from film left join film_actor on film.id = film_actor.film_id;

● range:把這個列當(dāng)作條件只檢索其中一個范圍。常見 where 從句中出現(xiàn) between、<、>、>=、in 等。主要應(yīng)用在具有索引的列中
explain select * from actor where id > 1;

● index:full index scan全索引掃描,index與all的區(qū)別為:index類型只遍歷索引樹,這通常比all快,因為索引文件通常比數(shù)據(jù)文件小。也就是說雖然index和all都是讀全表,但index是從索引中讀的,而all是從硬盤中讀的。
explain select * from film;

● ALL:即全表掃描,掃描你的聚簇索引的所有葉子節(jié)點。通常情況下這需要增加索引來進(jìn)行優(yōu)化了。
explain select * from actor;

4.5、possible_keys (可能用到的索引)
查詢條件字段涉及到的索引,可能沒有使用。
explain 時可能出現(xiàn) possible_keys 有列,而 key 顯示 NULL 的情況,這種情況是因為表中數(shù)據(jù)不多,mysql認(rèn)為索引對此查詢幫助不大,選擇了全表查詢。
如果該列是NULL,則沒有相關(guān)的索引。在這種情況下,可以通過檢查 where 子句看是否可以創(chuàng)造一個適當(dāng)?shù)乃饕齺硖岣卟樵冃阅?,然后?explain 查看效果
4.6、key (實際使用的索引)
實際使用的索引。如果為 NULL,則沒有使用索引。
如果想強(qiáng)制mysql使用或忽視possible_keys列中的索引,在查詢中使用 forceindex、ignore index。

4.7、key_len (索引的長度)
表示索引中使用的字節(jié)數(shù),查詢中使用的索引的長度(最大可能長度),并非實際使用長度,理論上長度越短越好。key_len 是根據(jù)表定義計算而得的,不是通過表內(nèi)檢索出的。
例子:
film_actor的聯(lián)合索引 idx_film_actor_id 由 film_id 和 actor_id 兩個int列組成,并且每個int是4字節(jié)。通過結(jié)果中的key_len=4可推斷出查詢使用了第一個列:film_id列來執(zhí)行索引查找。
explain select * from film_actor where film_id = 2;

4.8、ref
顯示索引的哪一列被使用了,如果可能的話,是一個常量 const。

4.9、rows
根據(jù)表統(tǒng)計信息及索引選用情況,大致估算出找到所需的記錄所需要讀取的行數(shù)。注意這個不是結(jié)果集里的行數(shù), 數(shù)值越低越好。

4.10、fitered
顯示了通過條件過濾出的行數(shù)的百分比估計值。

4.11、Extra (額外的)
MYSQL 如何解析查詢的額外信息。

Distinct: MySQL 發(fā)現(xiàn)第 1 個匹配行后,停止為當(dāng)前的行組合搜索更多的行。

Not exists:MySQL 能夠?qū)Σ樵冞M(jìn)行 LEFT JOIN 優(yōu)化,發(fā)現(xiàn) 1 個匹配 LEFT JOIN 標(biāo)準(zhǔn)的行后,不再為前面的的行組合在該表內(nèi)檢查更多的行。

range checked for each record (index map: #):MySQL 沒有發(fā)現(xiàn)好的可以使用的索引,但發(fā)現(xiàn)如果來自前面的表的列值已知,可能部分索引可以使用。

Using filesort: 說明mysql會對數(shù)據(jù)使用一個外部的索引排序,而不是按照表內(nèi)的索引順序進(jìn)行讀取。Mysql中無法利用索引完成的排序操作稱為“文件排序”這種情況下一般也是要考慮使用索引來優(yōu)化的。
4.1. actor.name未創(chuàng)建索引,會瀏覽actor整個表,保存排序關(guān)鍵字name和對應(yīng)的id,然后排序name并檢索行記錄
explain select * from actor order by name;

4.2. film.name建立了idx_name索引,此時查詢時extra是using index
explain select * from film order by name;

Using index: 表示相應(yīng)的select操作中使用了覆蓋索引,避免訪問了表的數(shù)據(jù)行,效率不錯!
如果同時出現(xiàn)了using where,表明索引被用來執(zhí)行索引鍵值的查找;
如果沒有同時出現(xiàn)using where,表明索引用來讀取數(shù)據(jù)而非執(zhí)行查找動作
explain select film_id from film_actor where film_id = 1;

Using temporary:為了解決查詢,MySQL 需要創(chuàng)建一個臨時表來容納結(jié)果。使用了臨時表保存中間結(jié)果。常見于排序order by和分組查詢group by。
Using filesort和Using temporary都是不太好的結(jié)果,會影響性能。
6.1. actor.name沒有索引,此時創(chuàng)建了張臨時表來distinct
explain select distinct name from actor;

6.2. film.name建立了idx_name索引,此時查詢時extra是using index,沒有用臨時表
explain select distinct name from film;

Using where: 表示使用了where過濾。
explain select * from actor where name = ‘a(chǎn)’;

Using sort_union(…), Using union(…), Using intersect(…): 這 些 函 數(shù) 說 明 如 何 為index_merge 聯(lián)接類型合并索引掃描。

Using index for group-by:類似于訪問表的 Using index 方式,Using index for group-by 表示MySQL發(fā)現(xiàn)了一個索引,可以用來查 詢GROUP BY或DISTINCT查詢的所有列,而不要額外搜索硬盤訪問實際的表。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容