mysql explain執行計劃 type 和 extra 字段說明

參考文章:如何利用工具,迅猛定位低效SQL? | 1分鐘系列

同一個SQL語句,爲啥性能差異咋就這麼大呢?(1分鐘系列

1.type字段

explain結果中的type字段代表什麼意思?

MySQL的官網解釋非常簡潔,只用了3個單詞:連接類型(the join type)。它描述了找到所需數據使用的掃描方式。

 

最爲常見的掃描方式有:

  • system:系統表,少量數據,往往不需要進行磁盤IO;

  • const:常量連接;

  • eq_ref:主鍵索引(primary key)或者非空唯一索引(unique not null)等值掃描;

  • ref:非主鍵非唯一索引等值掃描;

  • range:範圍掃描;

  • index:索引樹掃描;

  • ALL:全表掃描(full table scan);

畫外音:這些是最常見的,大家去explain自己工作中的SQL語句,95%都是上面這些類型。

 

上面各類掃描方式由快到慢

system > const > eq_ref > ref > range > index > ALL

下面一一舉例說明。

 

一、system

explain select * from mysql.time_zone;

上例中,從系統庫mysql的系統表time_zone裏查詢數據,掃碼類型爲system,這些數據已經加載到內存裏,不需要進行磁盤IO。

 

這類掃描是速度最快的。

 

explain select * from (select * from user where id=1) tmp;

再舉一個例子,內層嵌套(const)返回了一個臨時表,外層嵌套從臨時表查詢,其掃描類型也是system,也不需要走磁盤IO,速度超快。

 

二、const

數據準備:

create table user (

id int primary key,

name varchar(20)

)engine=innodb;

 

insert into user values(1,'shenjian');

insert into user values(2,'zhangsan');

insert into user values(3,'lisi');

const掃描的條件爲:

(1)命中主鍵(primary key)或者唯一(unique)索引;

(2)被連接的部分是一個常量(const)值;

 

explain select * from user where id=1;

如上例,id是PK,連接部分是常量1。

畫外音:別搞什麼類型轉換的幺蛾子。

 

這類掃描效率極高,返回數據量少,速度非常快。

 

三、eq_ref

數據準備:

create table user (

id int primary key,

name varchar(20)

)engine=innodb;

 

insert into user values(1,'shenjian');

insert into user values(2,'zhangsan');

insert into user values(3,'lisi');

 

create table user_ex (

id int primary key,

age int

)engine=innodb;

 

insert into user_ex values(1,18);

insert into user_ex values(2,20);

insert into user_ex values(3,30);

insert into user_ex values(4,40);

insert into user_ex values(5,50);

eq_ref掃描的條件爲,對於前表的每一行(row),後表只有一行被掃描。

 

再細化一點:

(1)join查詢;

(2)命中主鍵(primary key)或者非空唯一(unique not null)索引;

(3)等值連接;

 

explain select * from user,user_ex where user.id=user_ex.id;

如上例,id是主鍵,該join查詢爲eq_ref掃描。

 

 

 

這類掃描的速度也異常之快。

 

四、ref

數據準備:

create table user (

id int,

name varchar(20) ,

index(id)

)engine=innodb;

 

insert into user values(1,'shenjian');

insert into user values(2,'zhangsan');

insert into user values(3,'lisi');

 

create table user_ex (

id int,

age int,

index(id)

)engine=innodb;

 

insert into user_ex values(1,18);

insert into user_ex values(2,20);

insert into user_ex values(3,30);

insert into user_ex values(4,40);

insert into user_ex values(5,50);

如果把上例eq_ref案例中的主鍵索引,改爲普通非唯一(non unique)索引。

 

explain select * from user,user_ex where user.id=user_ex.id;

就由eq_ref降級爲了ref,此時對於前表的每一行(row),後表可能有多於一行的數據被掃描。

 

explain select * from user where id=1;

當id改爲普通非唯一索引後,常量的連接查詢,也由const降級爲了ref,因爲也可能有多於一行的數據被掃描。

 

ref掃描,可能出現在join裏,也可能出現在單表普通索引裏,每一次匹配可能有多行數據返回,雖然它比eq_ref要慢,但它仍然是一個很快的join類型。

 

五、range

數據準備:

create table user (

id int primary key,

name varchar(20)

)engine=innodb;

 

insert into user values(1,'shenjian');

insert into user values(2,'zhangsan');

insert into user values(3,'lisi');

insert into user values(4,'wangwu');

insert into user values(5,'zhaoliu');

range掃描就比較好理解了,它是索引上的範圍查詢,它會在索引上掃碼特定範圍內的值。

 

explain select * from user where id between 1 and 4;

explain select * from user where idin(1,2,3);

explain select * from user where id>3;

像上例中的between,in,>都是典型的範圍(range)查詢。

畫外音:必須是索引,否則不能批量"跳過"。

 

六、index

index類型,需要掃描索引上的全部數據。

 

explain count (*) from user;

如上例,id是主鍵,該count查詢需要通過掃描索引上的全部數據來計數。

畫外音:此表爲InnoDB引擎。

 

它僅比全表掃描快一點。

 

七、ALL

數據準備:

create table user (

id int,

name varchar(20)

)engine=innodb;

 

insert into user values(1,'shenjian');

insert into user values(2,'zhangsan');

insert into user values(3,'lisi');

 

create table user_ex (

id int,

age int

)engine=innodb;

 

insert into user_ex values(1,18);

insert into user_ex values(2,20);

insert into user_ex values(3,30);

insert into user_ex values(4,40);

insert into user_ex values(5,50);

explain select * from user,user_ex where user.id=user_ex.id;

如果id上不建索引,對於前表的每一行(row),後表都要被全表掃描。

 

今天這篇文章中,這個相同的join語句出現了三次:

(1)掃描類型爲eq_ref,此時id爲主鍵;

(2)掃描類型爲ref,此時id爲非唯一普通索引;

(3)掃描類型爲ALL,全表掃描,此時id上無索引;

2.Extra字段

 

explain結果中還有一個Extra字段,對分析與優化SQL有很大的幫助,今天花1分鐘簡單和大家聊一聊。

 

數據準備

create table user (

id int primary key,

name varchar(20),

sex varchar(5),

index(name)

)engine=innodb;

 

insert into user values(1, 'shenjian','no');

insert into user values(2, 'zhangsan','no');

insert into user values(3, 'lisi', 'yes');

insert into user values(4, 'lisi', 'no');

 

數據說明

用戶表:id主鍵索引,name普通索引(非唯一),sex無索引

四行記錄:其中name普通索引存在重複記錄lisi;

 

實驗目的

通過構造各類SQL語句,對explain的Extra字段進行說明,啓發式定位待優化低性能SQL語句。

 

一、【Using where】

實驗語句

explain select * from user where sex='no';

 

結果說明

Extra爲Using where說明,SQL使用了where條件過濾數據。

 

需要注意的是:

(1)返回所有記錄的SQL,不使用where條件過濾數據,大概率不符合預期,對於這類SQL往往需要進行優化;

(2)使用了where條件的SQL,並不代表不需要優化,往往需要配合explain結果中的type(連接類型)來綜合判斷;

畫外音:join type在《同一個SQL語句,爲啥性能差異咋就這麼大呢?》一文中有詳細敘述,本文不再展開。

 

本例雖然Extra字段說明使用了where條件過濾,但type屬性是ALL,表示需要掃描全部數據,仍有優化空間。

 

常見的優化方法爲,在where過濾屬性上添加索引。

畫外音:本例中,sex字段區分度不高,添加索引對性能提升有限。

 

二、【Using index】

實驗語句

explain select id,name from user where name='shenjian';

 

結果說明

Extra爲Using index說明,SQL所需要返回的所有列數據均在一棵索引樹上,而無需訪問實際的行記錄。

畫外音:The column information is retrieved from the table using only information in the index tree without having to do an additional seek to read the actual row.

 

這類SQL語句往往性能較好。

 

問題來了,什麼樣的列數據,會包含在索引樹上呢?

 

三、【Using index condition】

實驗語句:

explain select id,name,sex from user 

where name='shenjian';

畫外音:該SQL語句與上一個SQL語句不同的地方在於,被查詢的列,多了一個sex字段。

 

結果說明:

Extra爲Using index condition說明,確實命中了索引,但不是所有的列數據都在索引樹上,還需要訪問實際的行記錄。

畫外音:聚集索引,普通索引的底層實現差異,詳見《1分鐘瞭解MyISAM與InnoDB的索引差異》。

 

這類SQL語句性能也較高,但不如Using index。

 

問題來了,如何優化爲Using index呢?

 

四、【Using filesort】

實驗語句:

explain select * from user order by sex;

 

結果說明:

Extra爲Using filesort說明,得到所需結果集,需要對所有記錄進行文件排序。

也可以是用 order by null 告訴mysql不需要排序

這類SQL語句性能極差,需要進行優化。

 

典型的,在一個沒有建立索引的列上進行了order by,就會觸發filesort,常見的優化方案是,在order by的列上添加索引,避免每次查詢都全量排序。

 

五、【Using temporary】

實驗語句:

explain select * from user group by name order by sex;

 

結果說明:

Extra爲Using temporary說明,需要建立臨時表(temporary table)來暫存中間結果。

 

這類SQL語句性能較低,往往也需要進行優化。

 

典型的,group by和order by同時存在,且作用於不同的字段時,就會建立臨時表,以便計算出最終的結果集。

 

六、【Using join buffer (Block Nested Loop)】

實驗語句:

explain select * from user where id in(select id from user where sex='no');

 

結果說明:

Extra爲Using join buffer (Block Nested Loop)說明,需要進行嵌套循環計算。

畫外音:內層和外層的type均爲ALL,rows均爲4,需要循環進行4*4次計算。

 

這類SQL語句性能往往也較低,需要進行優化。

 

典型的,兩個關聯表join,關聯字段均未建立索引,就會出現這種情況。常見的優化方案是,在關聯字段上添加索引,避免每次嵌套循環計算。

 

結尾

explain是SQL優化中最常用的工具,搞定type和Extra,explain也就基本搞定了

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章