MySQL EXPLAIN的輸出信息mysql
表結構 drop table article; drop table user; -- 最終的兩個表的定義,authorId上沒有任何索引(不是外鍵,也沒有索引) create table article( id int auto_increment primary key, title varchar(255) not null, shortName varchar(255) not null, authorId int not null, createTime datetime not null, state int not null, totalView int default null, unique index idx_short_name_title (title,shortName) ); create table user ( id int auto_increment primary key, name varchar(255) not null, sex bit default 0, email varchar(50) not null, address varchar(255) default null, unique index idx_email (email), index idx_name (name) ); insert into article (title,shortName,authorId,createTime,state,totalView) values ('hello world','hello-world-0',1,'2015-10-11 08:08:08',1,10), ('hello world','hello-world-1',1,'2015-10-11 08:08:08',2,10), ('hello world','hello-world-2',2,'2015-10-11 08:08:08',3,10), ('hello world','hello-world-3',3,'2015-10-11 08:08:08',4,10), ('hello world','hello-world-4',3,'2015-10-11 08:08:08',5,10); insert into user (name,sex,email,address) values('lyx',0,'000@gmail.com','bj'), ('lyx',0,'111@gmail.com','bj'), ('lyx-0',0,'222@gmail.com','bj'), ('lyx-1',0,'333@gmail.com','bj'); ==== alter table article add constraint fk_author_id foreign key (authorId) references user(id); alter table article drop foreign key fk_author_id; alter table article drop index fk_author_id; alter table article add index idx_author_id (authorId); alter table article drop index idx_author_id; show create table article; ====
explain article;sql
explain select * from article a where a.author_id in (select author_id from user);服務器
第一個至關於desc表結構。性能
第二個表示select查詢語句的查詢計劃優化
> explain select (select 1 from article limit 1) from user ******************** 1. row ********************* id: 1 select_type: PRIMARY table: user type: index possible_keys: key: idx_email key_len: 52 ref: rows: 4 Extra: Using index ******************** 2. row ********************* id: 2 select_type: SUBQUERY table: article type: index possible_keys: key: idx_short_name_title key_len: 514 ref: rows: 5 Extra: Using index 2 rows in set
包含在select列表中的子查詢的select(換句話說,不在form子句中)標記爲SUBQUERY。this
> explain select * from article a where exists (select * from user u where a.authorId = u.id) ******************** 1. row ********************* id: 1 select_type: PRIMARY table: a type: ALL possible_keys: key: key_len: ref: rows: 5 Extra: Using where ******************** 2. row ********************* id: 2 select_type: DEPENDENT SUBQUERY table: u type: eq_ref possible_keys: PRIMARY key: PRIMARY key_len: 4 ref: test.a.authorId rows: 1 Extra: Using index 2 rows in set
> explain select max(id) from (select * from article) a ******************** 1. row ********************* id: 1 select_type: PRIMARY table: <derived2> type: ALL possible_keys: key: key_len: ref: rows: 5 Extra: ******************** 2. row ********************* id: 2 select_type: DERIVED table: article type: ALL possible_keys: key: key_len: ref: rows: 5 Extra: 2 rows in set
DERIVED值用來表示包含在from子句的子查詢中的select,mysql會遞歸併將結果防盜一個臨時表中。服務器內部稱其爲「派生表」,由於該表是從子查詢中派生出來的。spa
> explain select id , title from article a where a.id = 1 union select id ,title from article b where b.id = 2 ******************** 1. row ********************* id: 1 select_type: PRIMARY table: a type: const possible_keys: PRIMARY key: PRIMARY key_len: 4 ref: const rows: 1 Extra: ******************** 2. row ********************* id: 2 select_type: UNION table: b type: const possible_keys: PRIMARY key: PRIMARY key_len: 4 ref: const rows: 1 Extra: ******************** 3. row ********************* id: select_type: UNION RESULT table: <union1,2> type: ALL possible_keys: key: key_len: ref: rows: Extra: Using temporary 3 rows in set
mysql用戶手冊上說這一列顯示了「關聯類型」,但咱們認爲更準確的說法是訪問類型——換言之就是mysql決定如何查找表中的行。下面是最重的訪問數據行的方法,依次從最差到最優。code
這就是人們所稱的全表掃描,一般意味着mysql必須掃描整張表,從頭至尾,去找到須要的行。(這裏也有個例外,例如在查詢裏使用了LIMIT,或者在Extra列中顯示using distinct/not exists)。orm
好比,排序
> explain select * from article ******************** 1. row ********************* id: 1 select_type: SIMPLE table: article type: ALL possible_keys: key: key_len: ref: rows: 5 Extra: 1 rows in set
這個跟全表掃描同樣,只是mysql掃描表的時候按照索引次序進行而不是行。它的主要優勢是避免了排序;最大的缺點時要承擔按索引次序讀取整個表的開銷。這一般意味着如是按照隨機次序訪問行,開銷將會很是大。
若是在Extra列中看到了using index,說明mysql正在使用覆蓋索引,他只是掃描索引的數據,而不是按索引次序的每一行。他比按照索引次序全表掃描的開銷要少不少。
> explain select id , title from article ******************** 1. row ********************* id: 1 select_type: SIMPLE table: article type: index possible_keys: key: idx_short_name_title key_len: 514 ref: rows: 5 Extra: Using index 1 rows in set
mysql5.6手冊解釋:
Only rows that are in a given range are retrieved, using an index to select the rows. The key column in the output row indicates which index is used. The key_len contains the longest key part that was used. The ref column isNULL for this type.
range can be used when a key column is compared to a constant using any of the =, <>, >, >=, <, <=, IS NULL,<=>, BETWEEN, or IN() operators.
範圍掃描就是一個有限制的索引掃描,它開始於索引裏的某一點,返回匹配這個值域的行。這比全索引掃描好一些,由於它用不着遍歷所有索引。顯而易見的範圍掃描是帶有between或在where子句裏帶有>的查詢。
當mysql使用索引去查找一系列值時,例如 IN() 或 OR列表,也會顯示爲範圍掃描。然而這二者實際上是至關不一樣的訪問類型,在性能上有重要的差別。
> explain select * from article where id > 2 ******************** 1. row ********************* id: 1 select_type: SIMPLE table: article type: range possible_keys: PRIMARY key: PRIMARY key_len: 4 ref: rows: 3 Extra: Using where 1 rows in set
要注意並非全部的範圍查詢當執行查詢計劃時出現type=range,以下面的sql
> explain select * from article a where a.totalView >2 ******************** 1. row ********************* id: 1 select_type: SIMPLE table: a type: ALL possible_keys: key: key_len: ref: rows: 5 Extra: Using where 1 rows in set
這是由於totalView列上沒有創建索引。
這是一種索引訪問(有時也叫作索引查找),它返回全部匹配某個單個值的行。然而,它可能會找到多個符合條件的行,所以,它是查找和掃描的混合體。此類索引訪問只有當使用非惟一性索引或者惟一性索引的非惟一性前綴時纔會發生。把它叫作ref是由於要跟某個參考值相比較。這個參考值或者是一個常數,或者是來自多表查詢前一個表裏的結果值。
ref_or_null是ref之上的一個遍體,它意味着mysql必須在初次查找的結果裏進行第二次查找以找出NULL條目。
首先咱們給authorId列建上索引,
alter table article add index idx_author_id (authorId);
> explain select * from article where authorId = 1 ******************** 1. row ********************* id: 1 select_type: SIMPLE table: article type: ref possible_keys: idx_author_id key: idx_author_id key_len: 4 ref: const rows: 2 Extra: 1 rows in set
再看一個示例,
> explain select * from article a , user u where a.authorId = u.id and a.authorId = 1 ******************** 1. row ********************* id: 1 select_type: SIMPLE table: u type: const possible_keys: PRIMARY key: PRIMARY key_len: 4 ref: const rows: 1 Extra: ******************** 2. row ********************* id: 1 select_type: SIMPLE table: a type: ref possible_keys: idx_author_id key: idx_author_id key_len: 4 ref: const rows: 2 Extra: 2 rows in set
下面着三個sql的訪問數據的方式type都是ref,
explain select * from article a , user u where a.authorId = u.id and a.authorId = 1; explain select * from article a inner join user u on a.authorId = u.id and a.authorId = 1; explain select * from article a inner join user u on a.authorId = u.id where a.authorId = 1;
使用這種索引查找,mysql知道最多隻返回一條符合條件的記錄。這種訪問方式能夠在mysql使用主鍵活着惟一索引查找時看到,它會將他們與某個參考值作比較。mysql對於這類訪問類型的優化作的很是好,由於它知道無須估計匹配行的範圍或在找到匹配行後再繼續查找。
基於上面的表結構中,authorId列上沒有索引,咱們要給他創建一個惟一索引
alter table article add unique index idx_author_id (authorId);
> explain select * from article a left join user u on a.authorId = u.id ******************** 1. row ********************* id: 1 select_type: SIMPLE table: a type: ALL possible_keys: key: key_len: ref: rows: 1 Extra: ******************** 2. row ********************* id: 1 select_type: SIMPLE table: u type: eq_ref possible_keys: PRIMARY key: PRIMARY key_len: 4 ref: test.a.authorId rows: 1 Extra: 2 rows in set
再看一個使用exists子查詢的示例,先把上面創建的索引刪掉
alter table article drop index idx_author_id;
> explain select * from article a where exists (select * from user u where a.authorId = u.id) ******************** 1. row ********************* id: 1 select_type: PRIMARY table: a type: ALL possible_keys: key: key_len: ref: rows: 5 Extra: Using where ******************** 2. row ********************* id: 2 select_type: DEPENDENT SUBQUERY table: u type: eq_ref possible_keys: PRIMARY key: PRIMARY key_len: 4 ref: test.a.authorId rows: 1 Extra: Using index 2 rows in set
訪問數據行的類型也是eq_ref。exists子查詢的做用就是判斷前面查詢出來的數據行在子查詢中存在不存在,若是存在就返回true。因此只要肯定有一條記錄存在就行。
當mysql能對查詢的某部分進行優化並將其轉換爲一個常量時,他就會使用這些訪問類型。舉例來講,若是你經過將某一行的主鍵放入where子句中裏的方式來選取此行的主鍵,mysql就能將這個查詢轉換爲一個常量。
> explain select * from (select * from article a where a.id=1) temp_a inner join user u on temp_a.authorId = u.id ******************** 1. row ********************* id: 1 select_type: PRIMARY table: <derived2> type: system possible_keys: key: key_len: ref: rows: 1 Extra: ******************** 2. row ********************* id: 1 select_type: PRIMARY table: u type: const possible_keys: PRIMARY key: PRIMARY key_len: 4 ref: const rows: 1 Extra: ******************** 3. row ********************* id: 2 select_type: DERIVED table: a type: const possible_keys: PRIMARY key: PRIMARY key_len: 4 ref: const rows: 1 Extra: 3 rows in set
放到下一篇文章來講。
============END============