
目錄
一、執(zhí)行計劃分析
二、SQL執(zhí)行計劃中的參數(shù)講解——explain中的id
1、id值相同,從上往下順序執(zhí)行。
2、ID值不同,id值越大越優(yōu)先查詢
3、id值有相同,又有不同。id值越大越優(yōu)先;id值相同,從上往下順序執(zhí)行
二、SQL執(zhí)行計劃中的參數(shù)講解——explain中的select_type
三、SQL執(zhí)行計劃中的參數(shù)講解——explain中的table
四、SQL執(zhí)行計劃中的參數(shù)講解——explain中的type
1、type字段中的——system
2、type字段中的——const
3、type字段中的——eq_ref
4、type字段中的——ref
5、type字段中的——range
6、type字段中的——index
7、type字段中的——all
四、SQL執(zhí)行計劃中的參數(shù)講解——explain中的possible_keys
五、SQL執(zhí)行計劃中的參數(shù)講解——explain中的key
六、SQL執(zhí)行計劃中的參數(shù)講解——explain中的key_len
七、SQL執(zhí)行計劃中的參數(shù)講解——explain中的ref
八、SQL執(zhí)行計劃中的參數(shù)講解——explain中的rows
九、SQL執(zhí)行計劃中的參數(shù)講解——explain中的Extra
1、Extra字段中——using filesort
2、Extra字段中——using temporary
3、Extra字段中——using index
4、Extra字段中——using where
十、優(yōu)化案例,單表優(yōu)化、兩表優(yōu)化、三表優(yōu)化
十一、避免索引失效的一些原則
前不久看了SQL優(yōu)化的視頻,學的時候醍醐灌頂,學完后發(fā)現(xiàn)就是在圍繞著explain的各個字段講解,先把學習的知識梳理一下,當然自己還是有很多不會的地方,后期不斷完善。
sql優(yōu)化是面試必問的面試點,而SQL優(yōu)化主要就是在優(yōu)化索引。在學習索引優(yōu)化之前,需要知道SQL的執(zhí)行順序,這里只給結論,深入學習可參考文章:步步深入:MySQL架構總覽->查詢執(zhí)行流程->SQL解析順序
SQL執(zhí)行順序
先執(zhí)行SQL語句中的from,在執(zhí)行on.. join ..where ..group by ....having ...,最后執(zhí)行select dinstinct ..order by limit ...
from .. on.. join ..where ..group by ....having ...
select dinstinct ..order by limit ...
一、執(zhí)行計劃分析
通過explain可以知道m(xù)ysql是如何處理語句的,并分析出查詢或是表結構的性能瓶頸,其實就是在干查詢優(yōu)化器的事,通過expalin可以得到查詢執(zhí)行計劃。
#語法:
explain +SQL語句
explain select * from tb

- 各字段含義
id : 編號
select_type :查詢類型
table :表
type :類型
possible_keys :預測用到的索引
key :實際使用的索引
key_len :實際使用索引的長度
ref :表之間的引用
rows :通過索引查詢到的數(shù)據(jù)量
Extra :額外的信息
下面講解explain中各字段的具體含義,我們只關注三種,分別是type,key,rows
二、SQL執(zhí)行計劃中的參數(shù)講解——explain中的id
explain中id是SQL執(zhí)行順序的標識,id的返回結果是數(shù)字,id 值越大優(yōu)先級越高,越先被執(zhí)行;id 相同,從上往下順序執(zhí)行。返回結果有三種情況。
目錄
1、id值相同,從上往下順序執(zhí)行。
2、ID值不同,id值越大越優(yōu)先查詢
3、id值有相同,又有不同。id值越大越優(yōu)先;id值相同,從上往下順序執(zhí)行
1、id值相同,從上往下順序執(zhí)行。
案例如下
explain select t.* from teacher t,course c,teacherCard tc where t.tid=c.tid
and t.tcid = tc.tcid
and (c.cid=2 or tc.tcid=3)

在上圖SQL邏輯中,id值相同都為1,從上往下順序執(zhí)行。先執(zhí)行teacher表(t表),在執(zhí)行teacherCard 表(tc表),最后執(zhí)行course 表(c表)。
建表:
create table course
(
cid int(3),
cname varchar(20),
tid int(3)
);
create table teacher
(
tid int(3),
tname varchar(20),
tcid int(3)
);
create table teacherCard
(
tcid int(3),
tcdesc varchar(200)
);
插入數(shù)據(jù)
insert into course values(1,'java',1);
insert into course values(2,'html',1);
insert into course values(3,'sql',2);
insert into course values(4,'web',3);
insert into teacher values(1,'tz',1);
insert into teacher values(2,'tw',2);
insert into teacher values(3,'tl',3);
insert into teacherCard values(1,'tzdesc') ;
insert into teacherCard values(2,'twdesc') ;
insert into teacherCard values(3,'tldesc') ;
- 改變表中數(shù)據(jù)量會影響表的執(zhí)行順序
圖1表中數(shù)據(jù)量:
teacher表3條
teacherCard 表3條
course 表4條
改變teacher表數(shù)據(jù)條數(shù),增加3條數(shù)據(jù)
insert into teacher values(4,'ta',4);
insert into teacher values(5,'tb',5);
insert into teacher values(6,'tc',6);
查看表的執(zhí)行順序

我們可以看見修改了表數(shù)據(jù)量之后,表的執(zhí)行也改變了。先執(zhí)行teacherCard 表(tc表),在執(zhí)行course 表(c表),最后執(zhí)行teacher表(t表)。
-
表的執(zhí)行順序因數(shù)量的個數(shù)改變而改變的原因
笛卡兒積。數(shù)據(jù)量小對程序占用內(nèi)存小,優(yōu)先計算(數(shù)據(jù)小的表,優(yōu)先查詢)
2、ID值不同,id值越大越優(yōu)先查詢
案例如下
mysql> explain select tc.tcdesc from teacherCard tc where tc.tcid =
-> (select t.tcid from teacher t where t.tid =
-> (select c.tid from course c where c.cname = 'sql')
-> );
+----+-------------+-------+------+---------------+-------------+---------+-------+------+-----------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+-------------+---------+-------+------+-----------------------+
| 1 | PRIMARY | tc | ALL | NULL | NULL | NULL | NULL | 3 | Using where |
| 2 | SUBQUERY | t | ALL | NULL | NULL | NULL | NULL | 6 | Using where |
| 3 | SUBQUERY | c | ref | cname_index | cname_index | 23 | const | 1 | Using index condition |
+----+-------------+-------+------+---------------+-------------+---------+-------+------+-----------------------+
在上面邏輯中,id值不相同,id值越大越優(yōu)先查詢 (本質(zhì):在嵌套子查詢時,先查內(nèi)層 再查外層)
從id字段和table字段可以知道程序在底層先執(zhí)行course 表(c表),在執(zhí)行teacher表(t表),最后執(zhí)行teacherCard 表(tc表)
3、id值有相同,又有不同。id值越大越優(yōu)先;id值相同,從上往下順序執(zhí)行
案例如下
mysql> explain select t.tname ,tc.tcdesc from teacher t,teacherCard tc where t.tcid= tc.tcid
-> and t.tid = (select c.tid from course c where cname = 'sql') ;
+----+-------------+-------+------+---------------+-------------+---------+-------+------+----------------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+-------------+---------+-------+------+----------------------------------------------------+
| 1 | PRIMARY | tc | ALL | NULL | NULL | NULL | NULL | 3 | NULL |
| 1 | PRIMARY | t | ALL | NULL | NULL | NULL | NULL | 6 | Using where; Using join buffer (Block Nested Loop) |
| 2 | SUBQUERY | c | ref | cname_index | cname_index | 23 | const | 1 | Using index condition |
+----+-------------+-------+------+---------------+-------------+---------+-------+------+----------------------------------------------------+
在上面案例中,id值有相同,又有不同。先執(zhí)行course 表(c表),在執(zhí)行teacherCard 表(tc表),最后執(zhí)行teacher表(t表)。
二、SQL執(zhí)行計劃中的參數(shù)講解——explain中的select_type
select_type是查詢類型,常見的查詢類型如下。
1、: 簡單查詢,查詢SQL中不包含子查詢或者UNION
2、PRIMARY: 查詢中若包含復雜的子查詢,最外層的查詢則標記為
3、SUBQUERY : 包含子查詢SQL中的 子查詢 (非最外層)
4、DERIVED : 衍生查詢(使用到了臨時表)。在from列表中包含子查詢被標記為DRIVED衍生,MYSQL會遞歸執(zhí)行這些子查詢,把結果放到臨時表中
5、UNION: 若第二個SELECT出現(xiàn)在union之后,則被標記為UNION, 若union包含在from子句的子查詢中,外層select被標記為:derived
6、UNION RESULT: 從union表獲取結果的select
查詢類型這里只總結了結果,進一步了解建議大家看參考文章。
參考文章1:MySQL的Explain信息中select_type字段解釋
參考文章2:explain之select_type
三、SQL執(zhí)行計劃中的參數(shù)講解——explain中的table
顯示這一行的數(shù)據(jù)是關于哪張表的,有時不是真實的表名字,看到的是derivedx(x是個數(shù)字,我的理解是第幾步執(zhí)行的結果)
mysql> explain select cr.cname from
-> ( select * from course where tid = 1 union select * from course where tid = 2 ) cr;
+----+--------------+------------+------+---------------+------+---------+------+------+-----------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+--------------+------------+------+---------------+------+---------+------+------+-----------------+
| 1 | PRIMARY | <derived2> | ALL | NULL | NULL | NULL | NULL | 8 | NULL |
| 2 | DERIVED | course | ALL | NULL | NULL | NULL | NULL | 4 | Using where |
| 3 | UNION | course | ALL | NULL | NULL | NULL | NULL | 4 | Using where |
| NULL | UNION RESULT | <union2,3> | ALL | NULL | NULL | NULL | NULL | NULL | Using temporary |
+----+--------------+------------+------+---------------+------+---------+------+------+-----------------+
四、SQL執(zhí)行計劃中的參數(shù)講解——explain中的type
表示MySQL在表中找到所需行的方式,又稱“訪問類型”,sql查詢優(yōu)化中一個很重要的指標,結果值從好到壞依次是:
system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL
一般來說,好的sql查詢至少達到range級別,最好能達到ref
目錄:
1、type字段中的——system
2、type字段中的——const
3、type字段中的——eq_ref
4、type字段中的——ref
5、type字段中的——range
6、type字段中的——index
7、type字段中的——all
1、type字段中的——system
實現(xiàn)場景:當表里面只有一行數(shù)據(jù)的時候就會這樣,而且不管有沒有索引都一樣,這是const連接類型的特殊情況。
實現(xiàn)只有一條數(shù)據(jù)的系統(tǒng)表 ;或衍生表只有一條數(shù)據(jù)的主查詢
2、type字段中的——const
實現(xiàn)場景:當查詢只有唯一的一條記錄被匹配,并且使用作為查詢條件時, MySQL 會視查詢出來的值為常數(shù)(可以為字符串或者數(shù)字),這種類型非常快。和system不同的地方是system是表里面只有一行數(shù)據(jù),而const有多行數(shù)據(jù),const是只有一行數(shù)據(jù)被匹配。
案例如下
create table test01
(
tid int(3),
tname varchar(20)
);
插入數(shù)據(jù)
insert into test01 values(1,'a') ;
insert into test01 values(2,'b') ;
增加索引
alter table test01 add constraint tid_pk primary key(tid) ;
#查詢一個字段
mysql> explain select tid from test01 where tid =1 ;
+----+-------------+--------+-------+---------------+---------+---------+-------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+-------+---------------+---------+---------+-------+------+-------------+
| 1 | SIMPLE | test01 | const | PRIMARY | PRIMARY | 4 | const | 1 | Using index |
+----+-------------+--------+-------+---------------+---------+---------+-------+------+-------------+
#查詢表中所有字段
mysql> explain select * from test01 where tid =1 ;
+----+-------------+--------+-------+---------------+---------+---------+-------+------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+-------+---------------+---------+---------+-------+------+-------+
| 1 | SIMPLE | test01 | const | PRIMARY | PRIMARY | 4 | const | 1 | NULL |
+----+-------------+--------+-------+---------------+---------+---------+-------+------+-------+
3、type字段中的——eq_ref
唯一性索引:對于每個索引鍵的查詢,返回匹配唯一行數(shù)據(jù)(有且只有1個,不能多 、不能0),常見于唯一索引 和主鍵索引。
必須滿足查詢結果和表中數(shù)據(jù)是一致的。
案例如下
create table teacher
(
tid int(3),
tname varchar(20),
tcid int(3)
);
create table teacherCard
(
tcid int(3),
tcdesc varchar(200)
);
insert into teacher values(1,'tz',1);
insert into teacher values(2,'tw',2);
insert into teacher values(3,'tl',3);
insert into teacher values(4,'ta',4);
insert into teacher values(5,'tb',5);
insert into teacher values(6,'tc',6);
insert into teacherCard values(1,'tzdesc') ;
insert into teacherCard values(2,'twdesc') ;
insert into teacherCard values(3,'tldesc') ;
建索引
alter table teacherCard add constraint pk_tcid primary key(tcid);
alter table teacher add constraint uk_tcid unique index(tcid) ;
#1、teacher表和teacherCard 都有唯一索引tcid,查詢teacher表的tcid,type類型是ref
mysql> explain select t.tcid from teacher t,teacherCard tc where t.tcid = tc.tcid ;
+----+-------------+-------+-------+---------------+---------+---------+--------------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+---------+---------+--------------+------+-------------+
| 1 | SIMPLE | tc | index | PRIMARY | PRIMARY | 4 | NULL | 3 | Using index |
| 1 | SIMPLE | t | ref | uk_tcid | uk_tcid | 5 | test.tc.tcid | 1 | Using index |
+----+-------------+-------+-------+---------------+---------+---------+--------------+------+-------------+
#2、teacher表和teacherCard 都有唯一索引tcid,查詢teacherCard 表的tcid,type類型是ref
mysql> explain select tc.tcid from teacher t,teacherCard tc where t.tcid = tc.tcid ;
+----+-------------+-------+-------+---------------+---------+---------+--------------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+---------+---------+--------------+------+-------------+
| 1 | SIMPLE | tc | index | PRIMARY | PRIMARY | 4 | NULL | 3 | Using index |
| 1 | SIMPLE | t | ref | uk_tcid | uk_tcid | 5 | test.tc.tcid | 1 | Using index |
+----+-------------+-------+-------+---------------+---------+---------+--------------+------+-------------+
為什么上面type類型不是eq_ref
原因:teacher有6條數(shù)據(jù),索引查到的數(shù)據(jù)只有3條,還有3條數(shù)據(jù)沒查到,沒查到的數(shù)據(jù)結果就是0。
上面查詢?nèi)绾芜_到eq_ref
#刪除teacher表中的三條數(shù)據(jù)
delete from teacher where tid>3;
刪除數(shù)據(jù)后查看type類型
mysql> explain select t.tcid from teacher t,teacherCard tc where t.tcid = tc.tcid ;
+----+-------------+-------+--------+---------------+---------+---------+-------------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+--------+---------------+---------+---------+-------------+------+--------------------------+
| 1 | SIMPLE | t | index | uk_tcid | uk_tcid | 5 | NULL | 3 | Using where; Using index |
| 1 | SIMPLE | tc | eq_ref | PRIMARY | PRIMARY | 4 | test.t.tcid | 1 | Using index |
+----+-------------+-------+--------+---------------+---------+---------+-------------+------+--------------------------+
以上SQL,用到的索引是 t.tcid,即teacher表中的tcid字段;如果teacher表的數(shù)據(jù)個數(shù) 和 連接查詢teacherCard 表的數(shù)據(jù)個數(shù)一致(都是3條數(shù)據(jù)),則有可能滿足eq_ref級別;否則無法滿足。
4、type字段中的——ref
非唯一性索引,對于每個索引鍵的查詢,返回匹配的所有行(0行,多行或者一行)
出現(xiàn)該連接類型的條件是: 查找條件列使用了索引而且不為主鍵和unique。其實,意思就是雖然使用了索引,但該索引列的值并不唯一,有重復。這樣即使使用索引快速查找到了第一條數(shù)據(jù),仍然不能停止,要進行目標值附近的小范圍掃描。但它的好處是它并不需要掃全表,因為索引是有序的,即便有重復值,也是在一個非常小的范圍內(nèi)掃描。
準備數(shù)據(jù):
insert into teacher values(4,'tz',4) ;
insert into teacherCard values(4,'tz222');
添加索引:
alter table teacher add index index_name (tname) ;
mysql> explain select * from teacher where tname='tz';
+----+-------------+---------+------+---------------+------------+---------+-------+------+-----------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------+------+---------------+------------+---------+-------+------+-----------------------+
| 1 | SIMPLE | teacher | ref | index_name | index_name | 23 | const | 2 | Using index condition |
+----+-------------+---------+------+---------------+------------+---------+-------+------+-----------------------+
mysql> explain select * from teacher where tname='tw';
+----+-------------+---------+------+---------------+------------+---------+-------+------+-----------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------+------+---------------+------------+---------+-------+------+-----------------------+
| 1 | SIMPLE | teacher | ref | index_name | index_name | 23 | const | 1 | Using index condition |
+----+-------------+---------+------+---------------+------------+---------+-------+------+-----------------------+
5、type字段中的——range
range指的是有范圍的索引掃描,where后面是一個范圍查詢(between ,> < >=, **特殊:in有時候會失效 **,從而轉(zhuǎn)為 無索引all)
#添加索引
alter table teacher add index tid_index (tid) ;
#1、in范圍查詢時索引失效
mysql> explain select t.* from teacher t where t.tid in (1,2) ;
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| 1 | SIMPLE | t | ALL | tid_index | NULL | NULL | NULL | 4 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
#2、<范圍查詢時type 類型是range
mysql> explain select t.* from teacher t where t.tid <3 ;
+----+-------------+-------+-------+---------------+-----------+---------+------+------+-----------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+-----------+---------+------+------+-----------------------+
| 1 | SIMPLE | t | range | tid_index | tid_index | 5 | NULL | 1 | Using index condition |
+----+-------------+-------+-------+---------------+-----------+---------+------+------+-----------------------+
6、type字段中的——index
查詢?nèi)克饕袛?shù)據(jù)
mysql> explain select tid from teacher;--tid 是索引, 只需要掃描索引表,不需要所有表中的所有數(shù)據(jù)
+----+-------------+---------+-------+---------------+-----------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------+-------+---------------+-----------+---------+------+------+-------------+
| 1 | SIMPLE | teacher | index | NULL | tid_index | 5 | NULL | 4 | Using index |
+----+-------------+---------+-------+---------------+-----------+---------+------+------+-------------+
7、type字段中的——all
查詢?nèi)勘碇械臄?shù)據(jù)。這便是所謂的“全表掃描”,如果是展示一個數(shù)據(jù)表中的全部數(shù)據(jù)項,倒是覺得也沒什么,如果是在一個查找數(shù)據(jù)項的sql中出現(xiàn)了all類型,那通常意味著你的sql語句處于一種最原生的狀態(tài),有很大的優(yōu)化空間。
mysql> explain select cid from course;--cid不是索引,需要全表所有,即需要所有表中的所有數(shù)據(jù)
+----+-------------+--------+------+---------------+------+---------+------+------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+------+---------------+------+---------+------+------+-------+
| 1 | SIMPLE | course | ALL | NULL | NULL | NULL | NULL | 4 | NULL |
+----+-------------+--------+------+---------------+------+---------+------+------+-------+
參考文章1:mysql中explain的type的解釋
參考文章2:MySQL執(zhí)行計劃分析(EXPLAIN)之type字段詳細介紹說明
四、SQL執(zhí)行計劃中的參數(shù)講解——explain中的possible_keys
possible_keys可能用到的索引,是一種預測。查詢涉及到的字段上若存在索引,則該索引將被列出,但不一定被查詢使用,如果為空,說明沒有可用的索引。
mysql> explain select cid from course;
+----+-------------+--------+------+---------------+------+---------+------+------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+------+---------------+------+---------+------+------+-------+
| 1 | SIMPLE | course | ALL | NULL | NULL | NULL | NULL | 4 | NULL |
+----+-------------+--------+------+---------------+------+---------+------+------+-------+
該查詢中possible_keys 為null,說明沒有索引。
五、SQL執(zhí)行計劃中的參數(shù)講解——explain中的key
實際使用到的索引
六、SQL執(zhí)行計劃中的參數(shù)講解——explain中的key_len
key_len 索引的長度 ,用于判斷復合索引是否被完全使用
案例如下
#創(chuàng)建一張表
create table test_kl
(
name char(20)
);
創(chuàng)建索引
alter table test_kl add index index_name(name) ;
查看執(zhí)行計劃
mysql> explain select * from test_k2 where name ='' ;
+----+-------------+---------+------+---------------+------------+---------+-------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------+------+---------------+------------+---------+-------+------+--------------------------+
| 1 | SIMPLE | test_k2 | ref | index_name | index_name | 23 | const | 1 | Using where; Using index |
+----+-------------+---------+------+---------------+------------+---------+-------+------+--------------------------+
在utf8:1個字符站3個字節(jié)
如果索引字段可以為Null,則會使用1個字節(jié)用于標識。
20*1=20 + 1(null) +2(用2個字節(jié) 標識可變長度,字段類型是可變長度) =23
utf8:1個字符3個字節(jié)
gbk:1個字符2個字節(jié)
latin:1個字符1個字節(jié)
復合索引案例如下
alter table test_k2 add column name1 varchar(20) ; --name1可以為null
drop index index_name on test_k2 ;
增加一個復合索引
alter table test_k2 add index name_name1_index (name,name1) ;
#復合索引,要用name1 ,得先用name
mysql> explain select * from test_k2 where name1 = '' ;
+----+-------------+---------+-------+---------------+------------------+---------+------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------+-------+---------------+------------------+---------+------+------+--------------------------+
| 1 | SIMPLE | test_k2 | index | NULL | name_name1_index | 46 | NULL | 1 | Using where; Using index |
+----+-------------+---------+-------+---------------+------------------+---------+------+------+--------------------------+
#2、雖然是復合索引,但是只用到了name
mysql> explain select * from test_k2 where name = '' ;
+----+-------------+---------+------+------------------+------------------+---------+-------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------+------+------------------+------------------+---------+-------+------+--------------------------+
| 1 | SIMPLE | test_k2 | ref | name_name1_index | name_name1_index | 23 | const | 1 | Using where; Using index |
+----+-------------+---------+------+------------------+------------------+---------+-------+------+--------------------------+
七、SQL執(zhí)行計劃中的參數(shù)講解——explain中的ref
注意與type中的ref值區(qū)分,顯示索引的哪一列被使用了 。返回兩種結果,const(是常量則該字段結果是const),或者使用了那個字段。
alter table course add index tid_index (tid) ;
mysql> explain select * from course c,teacher t where c.tid = t.tid and t.tname ='tw' ;
+----+-------------+-------+------+----------------------+------------+---------+------------+------+------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+----------------------+------------+---------+------------+------+------------------------------------+
| 1 | SIMPLE | t | ref | index_name,tid_index | index_name | 23 | const | 1 | Using index condition; Using where |
| 1 | SIMPLE | c | ref | tid_index | tid_index | 5 | test.t.tid | 1 | NULL |
+----+-------------+-------+------+----------------------+------------+---------+------------+------+------------------------------------+
八、SQL執(zhí)行計劃中的參數(shù)講解——explain中的rows
被索引優(yōu)化查詢的數(shù)據(jù)個數(shù) (實際通過索引而查詢到的 數(shù)據(jù)個數(shù))
explain select * from course c,teacher t where c.tid = t.tid
and t.tname = 'tz' ;
九、SQL執(zhí)行計劃中的參數(shù)講解——explain中的Extra
目錄:
1、Extra字段中——using filesort
2、Extra字段中——using temporary
3、Extra字段中——using index
4、Extra字段中——using where
Extra其他信息,出現(xiàn)Using filesort、Using temporary 意味著不能使用索引,效率會受到重大影響。應盡可能對此進行優(yōu)化
1、Extra字段中——using filesort
性能消耗大;需要“額外”的一次排序(或者是額外得一次查詢),常見于 order by 語句中。
排序:排序得前提是先查詢,在排序。比如需要對10個人根據(jù)年齡排序,所以排序之前需要先排序
- 案例如下
create table test02
(
a1 char(3),
a2 char(3),
a3 char(3),
index idx_a1(a1),
index idx_a2(a2),
index idx_a3(a3)
);
mysql> explain select * from test02 where a1 ='' order by a2 ;
+----+-------------+--------+------+---------------+--------+---------+-------+------+----------------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+------+---------------+--------+---------+-------+------+----------------------------------------------------+
| 1 | SIMPLE | test02 | ref | idx_a1 | idx_a1 | 4 | const | 1 | Using index condition; Using where; Using filesort |
+----+-------------+--------+------+---------------+--------+---------+-------+------+----------------------------------------------------+
Extra字段中出現(xiàn)了Using filesort。
小結:對于單索引, 如果排序和查找是同一個字段,則不會出現(xiàn)using filesort;如果排序和查找不是同一個字段,則會出現(xiàn)using filesort;
如何避免: where哪些字段,就order by那些字段2
-
復合索引——分析Extra字段
復合索引,不能跨列(最佳左前綴)
drop index idx_a1 on test02;
drop index idx_a2 on test02;
drop index idx_a3 on test02;
alter table test02 add index idx_a1_a2_a3 (a1,a2,a3) ;
test02 有復合索引idx_a1_a2_a3 (a1,a2,a3) ,where條件是a1,排序是a3??缌辛?,所以出現(xiàn)了Using filesort。
mysql> explain select *from test02 where a1='' order by a3 ;
+----+-------------+--------+------+---------------+--------------+---------+-------+------+------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+------+---------------+--------------+---------+-------+------+------------------------------------------+
| 1 | SIMPLE | test02 | ref | idx_a1_a2_a3 | idx_a1_a2_a3 | 4 | const | 1 | Using where; Using index; Using filesort |
+----+-------------+--------+------+---------------+--------------+---------+-------+------+------------------------------------------+
Extra字段存在Using filesort
mysql> explain select *from test02 where a2='' order by a3 ;
+----+-------------+--------+-------+---------------+--------------+---------+------+------+------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+-------+---------------+--------------+---------+------+------+------------------------------------------+
| 1 | SIMPLE | test02 | index | NULL | idx_a1_a2_a3 | 12 | NULL | 1 | Using where; Using index; Using filesort |
+----+-------------+--------+-------+---------------+--------------+---------+------+------+------------------------------------------+
Extra字段無Using filesort
mysql> explain select *from test02 where a1='' order by a2 ;
+----+-------------+--------+------+---------------+--------------+---------+-------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+------+---------------+--------------+---------+-------+------+--------------------------+
| 1 | SIMPLE | test02 | ref | idx_a1_a2_a3 | idx_a1_a2_a3 | 4 | const | 1 | Using where; Using index |
+----+-------------+--------+------+---------------+--------------+---------+-------+------+--------------------------+
小結:避免where和order by 按照復合索引的順序使用,不要跨列或無序使用。
2、Extra字段中——using temporary
using temporary性能損耗大 ,用到了臨時表。一般出現(xiàn)在group by 語句中,已經(jīng)有表了,但不適用,必須在來一張表。了解表概念,需要知道sql的執(zhí)行順序,SQL的執(zhí)行順序會產(chǎn)生虛擬表。
參考文章:步步深入:MySQL架構總覽->查詢執(zhí)行流程->SQL解析順序
mysql> explain select a1 from test02 where a1 in ('1','2','3') group by a2;
+----+-------------+--------+-------+---------------+--------------+---------+------+------+-----------------------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+-------+---------------+--------------+---------+------+------+-----------------------------------------------------------+
| 1 | SIMPLE | test02 | index | idx_a1_a2_a3 | idx_a1_a2_a3 | 12 | NULL | 1 | Using where; Using index; Using temporary; Using filesort |
+----+-------------+--------+-------+---------------+--------------+---------+------+------+-----------------------------------------------------------+
出現(xiàn)了Using temporary,根據(jù)a2分組,但是沒查詢a2.
3、Extra字段中——using index
出現(xiàn)using index性能會提升,即索引覆蓋(覆蓋索引)。
索引覆蓋該SQL不讀取原文件,只從索引文件中獲取數(shù)據(jù) (不需要回表查詢),只要使用到的列 全部都在索引中,就是索引覆蓋using index。
案例如下
#例如:test02表中有一個復合索引(a1,a2,a3)
mysql> explain select a1,a2 from test02 where a1='' or a2= '' ;
+----+-------------+--------+-------+---------------+--------------+---------+------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+-------+---------------+--------------+---------+------+------+--------------------------+
| 1 | SIMPLE | test02 | index | idx_a1_a2_a3 | idx_a1_a2_a3 | 12 | NULL | 1 | Using where; Using index |
+----+-------------+--------+-------+---------------+--------------+---------+------+------+--------------------------+
4、Extra字段中——using where
using where 回表查詢,需要在索引中查,有需要在原表中查就會出現(xiàn)using where
mysql> explain select a1,a3 from test02 where a3 = '' ;
+----+-------------+--------+-------+---------------+--------------+---------+------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+-------+---------------+--------------+---------+------+------+--------------------------+
| 1 | SIMPLE | test02 | index | NULL | idx_a1_a2_a3 | 12 | NULL | 1 | Using where; Using index |
+----+-------------+--------+-------+---------------+--------------+---------+------+------+--------------------------+
5、Extra字段中——impossible where
where子句永遠為false,會出現(xiàn)impossible where
案例如下:
mysql> explain select * from test02 where a1='x' and a1='y' ;
+----+-------------+-------+------+---------------+------+---------+------+------+------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+------------------+
| 1 | SIMPLE | NULL | NULL | NULL | NULL | NULL | NULL | NULL | Impossible WHERE |
+----+-------------+-------+------+---------------+------+---------+------+------+------------------+
a1不會出現(xiàn)即等于x,又等于y
十、優(yōu)化案例,單表優(yōu)化、兩表優(yōu)化、三表優(yōu)化
1、單表優(yōu)化
小結:a.最佳做前綴,保持索引的定義和使用的順序一致性
b.索引需要逐步優(yōu)化
c.將含In的范圍查詢 放到where條件的最后,防止失效。
2、兩表優(yōu)化
1、索引往哪張表加?
小表驅(qū)動大表
2、索引建立經(jīng)常使用的字段上
一般情況對于左外連接,給左表加索引;右外連接,給右表加索引
3、join時,為什么需要用小表驅(qū)動大表?
原因是join時是雙層循環(huán),一般建議將數(shù)據(jù)小的循環(huán)放外層;數(shù)據(jù)大的循環(huán)放內(nèi)存。
在編程語言中,外層越小,內(nèi)存越大,性能越高(小表在左,外層循環(huán)少,依賴的原則是程序優(yōu)化)
3、三張表優(yōu)化A B C
a.小表驅(qū)動大表
b.索引建立在經(jīng)常查詢的字段上--where 字段加索引,和常用字段加索引
十一、避免索引失效的一些原則
使用數(shù)據(jù)如下
create table book
(
bid int(4) primary key,
name varchar(20) not null,
authorid int(4) not null,
publicid int(4) not null,
typeid int(4) not null
);
insert into book values(1,'tjava',1,1,2) ;
insert into book values(2,'tc',2,1,2) ;
insert into book values(3,'wx',3,2,1) ;
insert into book values(4,'math',4,2,3) ;
alter table book add index idx_bta (bid,typeid,authorid);
1、復合索引
a.復合索引,不要跨列或無序使用(最佳左前綴)
(a,b,c)
b.復合索引,盡量使用全索引匹配
(a,b,c)
2、不要在索引上進行任何操作(計算、函數(shù)、類型轉(zhuǎn)換),否則索引失效
mysql> explain select * from book where authorid = 1 and typeid = 2 ;
---用到了at 2個索引
+----+-------------+-------+------+---------------+---------+---------+-------------+------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+---------+---------+-------------+------+-------+
| 1 | SIMPLE | book | ref | idx_atb | idx_atb | 8 | const,const | 1 | NULL |
+----+-------------+-------+------+---------------+---------+---------+-------------+------+-------+
1 row in set (0.00 sec)
mysql> explain select * from book where authorid = 1 and typeid*2 = 2 ;
--用到了a1個索引
+----+-------------+-------+------+---------------+---------+---------+-------+------+-----------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+---------+---------+-------+------+-----------------------+
| 1 | SIMPLE | book | ref | idx_atb | idx_atb | 4 | const | 1 | Using index condition |
+----+-------------+-------+------+---------------+---------+---------+-------+------+-----------------------+
1 row in set (0.00 sec)
mysql> explain select * from book where authorid*2 = 1 and typeid*2 = 2 ;
----用到了0個索引
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| 1 | SIMPLE | book | ALL | NULL | NULL | NULL | NULL | 4 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)
mysql> explain select * from book where authorid*2 = 1 and typeid = 2 ;
---用到了0個索引,
--原因:對于復合索引,如果左邊失效,右側全部失效。(a,b,c),例如如果 b失效,則b c同時失效。
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| 1 | SIMPLE | book | ALL | NULL | NULL | NULL | NULL | 4 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)
3、復合索引不能使用不等于(!= <>)或is null (is not null)
復合索引不能使用不等于(!= <>)或is null (is not null),否則自身以及右側所有全部失效。復合索引中如果有>,則自身和右側索引全部失效。
案例如下
mysql> explain select * from book where authorid != 1 and typeid =2 ;
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| 1 | SIMPLE | book | ALL | idx_atb | NULL | NULL | NULL | 4 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)
mysql> explain select * from book where authorid != 1 and typeid !=2 ;
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| 1 | SIMPLE | book | ALL | idx_atb | NULL | NULL | NULL | 4 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
SQL優(yōu)化,是一種概率層面的優(yōu)化。至于是否實際使用了我們的優(yōu)化,需要通過explain進行推測。
-
體驗概率情況(< > =)
原因是服務層中有SQL優(yōu)化器,可能會影響我們的優(yōu)化。
drop index idx_typeid on book;
drop index idx_authroid on book;
alter table book add index idx_book_at (authorid,typeid);
explain select * from book where authorid = 1 and typeid =2 ;--復合索引at全部使用
explain select * from book where authorid > 1 and typeid =2 ; --復合索引中如果有>,則自身和右側索引全部失效。
explain select * from book where authorid = 1 and typeid >2 ;--復合索引at全部使用
- 明顯的概率問題
explain select * from book where authorid < 1 and typeid =2 ;--復合索引at只用到了1個索引
explain select * from book where authorid < 4 and typeid =2 ;--復合索引全部失效
我們學習索引優(yōu)化 ,是一個大部分情況適用的結論,但由于SQL優(yōu)化器等原因 該結論不是100%正確。一般而言, 范圍查詢(> < in),之后的索引失效。
4、 like盡量以“常量”開頭,不要以'%'開頭,否則索引失效
如下
select * from xx where name like '%x%' ; --name索引失效
explain select * from teacher where tname like '%x%'; --tname索引失效
explain select * from teacher where tname like 'x%';
explain select tname from teacher where tname like '%x%';
如果必須使用like '%x%'進行模糊查詢,可以使用索引覆蓋挽救一部分。
5、盡量不要使用類型轉(zhuǎn)換(顯示、隱式),否則索引失效
explain select * from teacher where tname = 'abc' ;
explain select * from teacher where tname = 123 ;//程序底層將 123 -> '123',即進行了類型轉(zhuǎn)換,因此索引失效
6、盡量不要使用or,否則索引失效
explain select * from teacher where tname ='' or tcid >1 ; --將or左側的tname 失效。
7、一些其他的優(yōu)化方法
- (1)exist和in
select ..from table where exist (子查詢) ;
select ..from table where 字段 in (子查詢) ;
如果主查詢的數(shù)據(jù)集大,則使用In ,效率高。
如果子查詢的數(shù)據(jù)集大,則使用exist,效率高。
-
(2)order by 優(yōu)化
using filesort 有兩種算法:雙路排序、單路排序 (根據(jù)IO的次數(shù))
MySQL4.1之前 默認使用 雙路排序;雙路:掃描2次磁盤(1:從磁盤讀取排序字段 ,對排序字段進行排序(在buffer中進行的排序) 2:掃描其他字段 )
--IO較消耗性能
MySQL4.1之后 默認使用 單路排序 : 只讀取一次(全部字段),在buffer中進行排序。但種單路排序 會有一定的隱患 (不一定真的是“單路|1次IO”,有可能多次IO)。原因:如果數(shù)據(jù)量特別大,則無法 將所有字段的數(shù)據(jù) 一次性讀取完畢,因此 會進行“分片讀取、多次讀取”。
注意:單路排序 比雙路排序 會占用更多的buffer。單路排序在使用時,如果數(shù)據(jù)大,可以考慮調(diào)大buffer的容量大?。? set max_length_for_sort_data = 1024 單位byte
如果max_length_for_sort_data值太低,則mysql會自動從 單路->雙路 (太低:需要排序的列的總大小超過max_length_for_sort_data定義的字節(jié)數(shù))
提高order by查詢的策略
a.選擇使用單路、雙路 ;調(diào)整buffer的容量大小;
b.避免select * ...
c.復合索引 不要跨列使用 ,避免using filesort
d.保證全部的排序字段 排序的一致性(都是升序 或 降序)
后記
最后SQL優(yōu)化簡單記錄了,SQL優(yōu)化也暫時學習到這,后期學習中會繼續(xù)完善。文章寫得不好,但是總比自己不梳理好。
在等2天就上班了,過年好像什么都沒學到,新的一年也要加油呀。發(fā)現(xiàn)自己在目標規(guī)劃,以及拆解目標上面特別擅長,但是在落實到每天或每周上面執(zhí)行有點差,新的一年得改掉這個習慣,自己還有很多事想去做。除了工作,還想學習其它的。
另外下個春節(jié)不這么早回成都了,要在老家陪外婆,對爸媽也要像外婆一樣好好的