新聞中心
大家好,我是菜鳥哥!

公司主營(yíng)業(yè)務(wù):成都做網(wǎng)站、網(wǎng)站制作、成都外貿(mào)網(wǎng)站建設(shè)、移動(dòng)網(wǎng)站開發(fā)等業(yè)務(wù)。幫助企業(yè)客戶真正實(shí)現(xiàn)互聯(lián)網(wǎng)宣傳,提高企業(yè)的競(jìng)爭(zhēng)能力。成都創(chuàng)新互聯(lián)是一支青春激揚(yáng)、勤奮敬業(yè)、活力青春激揚(yáng)、勤奮敬業(yè)、活力澎湃、和諧高效的團(tuán)隊(duì)。公司秉承以“開放、自由、嚴(yán)謹(jǐn)、自律”為核心的企業(yè)文化,感謝他們對(duì)我們的高要求,感謝他們從不同領(lǐng)域給我們帶來(lái)的挑戰(zhàn),讓我們激情的團(tuán)隊(duì)有機(jī)會(huì)用頭腦與智慧不斷的給客戶帶來(lái)驚喜。成都創(chuàng)新互聯(lián)推出定陶免費(fèi)做網(wǎng)站回饋大家。
很多朋友在做數(shù)據(jù)分析時(shí),分析兩分鐘,跑數(shù)兩小時(shí)?
在使用SQL過程中不僅要關(guān)注數(shù)據(jù)結(jié)果,同樣要注意SQL語(yǔ)句的執(zhí)行效率。
本文涉及三部分:
- SQL介紹
- SQL優(yōu)化方法
- SQL優(yōu)化實(shí)例
一. MySQL的基本架構(gòu)
(1)MySQL的基礎(chǔ)架構(gòu)圖
左邊的client可以看成是客戶端,客戶端有很多,像我們經(jīng)常你使用的CMD黑窗口,像我們經(jīng)常用于學(xué)習(xí)的WorkBench,像企業(yè)經(jīng)常使用的Navicat工具,它們都是一個(gè)客戶端。右邊的這一大堆都可以看成是Server(MySQL的服務(wù)端),我們將Server在細(xì)分為sql層和存儲(chǔ)引擎層。
當(dāng)查詢出數(shù)據(jù)以后,會(huì)返回給執(zhí)行器。執(zhí)行器一方面將結(jié)果寫到查詢緩存里面,當(dāng)你下次再次查詢的時(shí)候,就可以直接從查詢緩存中獲取到數(shù)據(jù)了。另一方面,直接將結(jié)果響應(yīng)回客戶端。
(2)查詢數(shù)據(jù)庫(kù)的引擎
① show engines;
② show variables like “%storage_engine%”;
(3)指定數(shù)據(jù)庫(kù)對(duì)象的存儲(chǔ)引擎
create table tb(
id int(4) auto_increment,
name varchar(5),
dept varchar(5),
primary key(id)
) engine=myISAM auto_increment=1 default charset=utf8;
二. SQL優(yōu)化
(1)為什么需要進(jìn)行SQL優(yōu)化?
在進(jìn)行多表連接查詢、子查詢等操作的時(shí)候,由于你寫出的SQL語(yǔ)句欠佳,導(dǎo)致的服務(wù)器執(zhí)行時(shí)間太長(zhǎng),我們等待結(jié)果的時(shí)間太長(zhǎng)?;诖?,我們需要學(xué)習(xí)怎么優(yōu)化SQL。
(2)mysql的編寫過程和解析過程
① 編寫過程
select dinstinct ..from ..join ..on ..where ..group by ..having ..order by ..limit ..
② 解析過程
from .. on.. join ..where ..group by ..having ..select dinstinct ..order by ..limit ..
提供一個(gè)網(wǎng)站,詳細(xì)說明了mysql解析過程:
??https://www.cnblogs.com/annsshadow/p/5037667.html ??
(3)SQL優(yōu)化—主要就是優(yōu)化索引
優(yōu)化SQL,最重要的就是優(yōu)化SQL索引。
索引相當(dāng)于字典的目錄。利用字典目錄查找漢字的過程,就相當(dāng)于利用SQL索引查找某條記錄的過程。有了索引,就可以很方便快捷的定位某條記錄。
① 什么是索引?
索引就是幫助MySQL高效獲取數(shù)據(jù)的一種【數(shù)據(jù)結(jié)構(gòu)】。索引是一種樹結(jié)構(gòu),MySQL中一般用的是【B+樹】。
② 索引圖示說明(這里用二叉樹來(lái)幫助我們理解索引)
樹形結(jié)構(gòu)的特點(diǎn)是:子元素比父元素小的,放在左側(cè);子元素比父元素大的,放在右側(cè)。
這個(gè)圖示只是為了幫我們簡(jiǎn)單理解索引的,真實(shí)的關(guān)于【B+樹】的說明,我們會(huì)在下面進(jìn)行說明。
索引是怎么查找數(shù)據(jù)的呢??jī)蓚€(gè)字【指向】,上圖中我們給age列指定了一個(gè)索引,即類似于右側(cè)的這種樹形結(jié)構(gòu)。mysql表中的每一行記錄都有一個(gè)硬件地址,例如索引中的age=50,指向的就是源表中該行的標(biāo)識(shí)符(“硬件地址”)。
也就是說,樹形索引建立了與源表中每行記錄硬件地址的映射關(guān)系,當(dāng)你指定了某個(gè)索引,這種映射關(guān)系也就建成了,這就是為什么我們可以通過索引快速定位源表中記錄的原因。
以【select * from student where age=33】查詢語(yǔ)句為例。當(dāng)我們不加索引的時(shí)候,會(huì)從上到下掃描源表,當(dāng)掃描到第5行的時(shí)候,找到了我們想要找到了元素,一共是查詢了5次。
當(dāng)添加了索引以后,就直接在樹形結(jié)構(gòu)中進(jìn)行查找,33比50小,就從左側(cè)查詢到了23,33大于23,就又查詢到了右側(cè),這下找到了33,整個(gè)索引結(jié)束,一共進(jìn)行了3次查找。是不是很方便,假如我們此時(shí)需要查找age=62,你再想想“添加索引”前后,查找次數(shù)的變化情況。
(4)索引的弊端
1.當(dāng)數(shù)據(jù)量很大的時(shí)候,索引也會(huì)很大(當(dāng)然相比于源表來(lái)說,還是相當(dāng)小的),也需要存放在內(nèi)存/硬盤中(通常存放在硬盤中),占據(jù)一定的內(nèi)存空間/物理空間。
2.索引并不適用于所有情況:a.少量數(shù)據(jù);b.頻繁進(jìn)行改動(dòng)的字段,不適合做索引;c.很少使用的字段,不需要加索引;
3.索引會(huì)提高數(shù)據(jù)查詢效率,但是會(huì)降低“增、刪、改”的效率。當(dāng)不使用索引的時(shí)候,我們進(jìn)行數(shù)據(jù)的增刪改,只需要操作源表即可,但是當(dāng)我們添加索引后,不僅需要修改源表,也需要再次修改索引,很麻煩。盡管是這樣,添加索引還是很劃算的,因?yàn)槲覀兇蠖鄶?shù)使用的就是查詢,“查詢”對(duì)于程序的性能影響是很大的。
(5)索引的優(yōu)勢(shì)
1.提高查詢效率(降低了IO使用率)。當(dāng)創(chuàng)建了索引后,查詢次數(shù)減少了。
2.降低CPU使用率。比如說【…order by age desc】這樣一個(gè)操作,當(dāng)不加索引,會(huì)把源表加載到內(nèi)存中做一個(gè)排序操作,極大的消耗了資源。但是使用了索引以后,第一索引本身就小一些,第二索引本身就是排好序的,左邊數(shù)據(jù)最小,右邊數(shù)據(jù)最大。
(6)B+樹圖示說明
MySQL中索引使用的就是B+樹結(jié)構(gòu)。
關(guān)于B+樹的說明:
首先,Btree一般指的都是【B+樹】,數(shù)據(jù)全部存放在葉子節(jié)點(diǎn)中。對(duì)于上圖來(lái)說,最下面的第3層,屬于葉子節(jié)點(diǎn),真實(shí)數(shù)據(jù)部份都是存放在葉子節(jié)點(diǎn)當(dāng)中的。
那么對(duì)于第1、2層中的數(shù)據(jù)又是干嘛的呢?答:用于分割指針塊兒的,比如說小于26的找P1,介于26-30之間的找P2,大于30的找P3。
其次,三層【B+樹】可以存放上百萬(wàn)條數(shù)據(jù)。這么多數(shù)據(jù)怎么放的呢?增加“節(jié)點(diǎn)數(shù)”。圖中我們只有三個(gè)節(jié)點(diǎn)。
最后,【B+樹】中查詢?nèi)我鈹?shù)據(jù)的次數(shù),都是n次,n表示的是【B+樹】的高度。
三. 索引的分類與創(chuàng)建
(1)索引分類
單值索引
唯一索引
復(fù)合索引
① 單值索引
利用表中的某一個(gè)字段創(chuàng)建單值索引。一張表中往往有多個(gè)字段,也就是說每一列其實(shí)都可以創(chuàng)建一個(gè)索引,這個(gè)根據(jù)我們實(shí)際需求來(lái)進(jìn)行創(chuàng)建。還需要注意的一點(diǎn)就是,一張表可以創(chuàng)建多個(gè)“單值索引”。
假如某一張表既有age字段,又有name字段,我們可以分別對(duì)age、name創(chuàng)建一個(gè)單值索引,這樣一張表就有了兩個(gè)單值索引。
② 唯一索引
也是利用表中的某一個(gè)字段創(chuàng)建單值索引,與單值索引不同的是:創(chuàng)建唯一索引的字段中的數(shù)據(jù),不能有重復(fù)值。像age肯定有很多人的年齡相同,像name肯定有些人是重名的,因此都不適合創(chuàng)建“唯一索引”。像編號(hào)id、學(xué)號(hào)sid,對(duì)于每個(gè)人都不一樣,因此可以用于創(chuàng)建唯一索引。
③ 復(fù)合索引
多個(gè)列共同構(gòu)成的索引。比如說我們創(chuàng)建這樣一個(gè)“復(fù)合索引”(name,age),先利用name進(jìn)行索引查詢,當(dāng)name相同的時(shí)候,我們利用age再進(jìn)行一次篩選。注意:復(fù)合索引的字段并不是非要都用完,當(dāng)我們利用name字段索引出我們想要的結(jié)果以后,就不需要再使用age進(jìn)行再次篩選了。
(2)創(chuàng)建索引
① 語(yǔ)法
語(yǔ)法:create 索引類型 索引名 on 表(字段);
建表語(yǔ)句如下:
查詢表結(jié)構(gòu)如下:
② 創(chuàng)建索引的第一種方式
Ⅰ 創(chuàng)建單值索引
create index dept_index on tb(dept);
Ⅱ 創(chuàng)建唯一索引:這里我們假定name字段中的值都是唯一的
create unique index name_index on tb(name);
Ⅲ 創(chuàng)建復(fù)合索引
create index dept_name_index on tb(dept,name);
③ 創(chuàng)建索引的第二種方式
先刪除之前創(chuàng)建的索引以后,再進(jìn)行這種創(chuàng)建索引方式的測(cè)試;
語(yǔ)法:alter table 表名 add 索引類型 索引名(字段)
Ⅰ 創(chuàng)建單值索引
alter table tb add index dept_index(dept);
Ⅱ 創(chuàng)建唯一索引:這里我們假定name字段中的值都是唯一的
alter table tb add unique index name_index(name);
Ⅲ 創(chuàng)建復(fù)合索引
alter table tb add index dept_name_index(dept,name);
④ 補(bǔ)充說明
如果某個(gè)字段是primary key,那么該字段默認(rèn)就是主鍵索引。
主鍵索引和唯一索引非常相似。相同點(diǎn):該列中的數(shù)據(jù)都不能有相同值;不同點(diǎn):主鍵索引不能有null值,但是唯一索引可以有null值。
(3)索引刪除和索引查詢
① 索引刪除
語(yǔ)法:drop index 索引名 on 表名;
drop index name_index on tb;
② 索引查詢
語(yǔ)法:show index from 表名;
show index from tb;
結(jié)果如下:
四. SQL性能問題的探索
人為優(yōu)化: 需要我們使用explain分析SQL的執(zhí)行計(jì)劃。該執(zhí)行計(jì)劃可以模擬SQL優(yōu)化器執(zhí)行SQL語(yǔ)句,可以幫助我們了解到自己編寫SQL的好壞。
SQL優(yōu)化器自動(dòng)優(yōu)化: 最開始講述MySQL執(zhí)行原理的時(shí)候,我們已經(jīng)知道MySQL有一個(gè)優(yōu)化器,當(dāng)你寫了一個(gè)SQL語(yǔ)句的時(shí)候,SQL優(yōu)化器如果認(rèn)為你寫的SQL語(yǔ)句不夠好,就會(huì)自動(dòng)寫一個(gè)好一些的等價(jià)SQL去執(zhí)行。
SQL優(yōu)化器自動(dòng)優(yōu)化功能【會(huì)干擾】我們的人為優(yōu)化功能。當(dāng)我們查看了SQL執(zhí)行計(jì)劃以后,如果寫的不好,我們會(huì)去優(yōu)化自己的SQL。當(dāng)我們以為自己優(yōu)化的很好的時(shí)候,最終的執(zhí)行計(jì)劃,并不是按照我們優(yōu)化好的SQL語(yǔ)句來(lái)執(zhí)行的,而是有時(shí)候?qū)⑽覀儍?yōu)化好的SQL改變了,去執(zhí)行。
SQL優(yōu)化是一種概率問題,有時(shí)候系統(tǒng)會(huì)按照我們優(yōu)化好的SQL去執(zhí)行結(jié)果(優(yōu)化器覺得你寫的差不多,就不會(huì)動(dòng)你的SQL)。有時(shí)候優(yōu)化器仍然會(huì)修改我們優(yōu)化好的SQL,然后再去執(zhí)行。
(1)查看執(zhí)行計(jì)劃
語(yǔ)法:explain + SQL語(yǔ)句
eg:explain select * from tb;
(2)“執(zhí)行計(jì)劃”中需要知道的幾個(gè)“關(guān)鍵字”
id :編號(hào)
select_type :查詢類型
table :表
type :類型
possible_keys :預(yù)測(cè)用到的索引
key :實(shí)際使用的索引
key_len :實(shí)際使用索引的長(zhǎng)度
ref :表之間的引用
rows :通過索引查詢到的數(shù)據(jù)量
Extra :額外的信息
建表語(yǔ)句和插入數(shù)據(jù):
# 建表語(yǔ)句
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') ;
五. explain執(zhí)行計(jì)劃常用關(guān)鍵字詳解
(1)id關(guān)鍵字的使用說明
① 案例:查詢課程編號(hào)為2 或 教師證編號(hào)為3 的老師信息:
# 查看執(zhí)行計(jì)劃
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);
結(jié)果如下:
接著,在往teacher表中增加幾條數(shù)據(jù)。
insert into teacher values(4,'ta',4);
insert into teacher values(5,'tb',5);
insert into teacher values(6,'tc',6);
再次查看執(zhí)行計(jì)劃。
# 查看執(zhí)行計(jì)劃
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);
結(jié)果如下:
表的執(zhí)行順序 ,因表數(shù)量改變而改變的原因:笛卡爾積。
a b c
2 3 4
最終:2 * 3 * 4 = 6 * 4 = 24
c b a
4 3 2
最終:4 * 3 * 2 = 12 * 2 = 24
分析:最終執(zhí)行的條數(shù),雖然是一致的。但是中間過程,有一張臨時(shí)表是6,一張臨時(shí)表是12,很明顯6 < 12,對(duì)于內(nèi)存來(lái)說,數(shù)據(jù)量越小越好,因此優(yōu)化器肯定會(huì)選擇第一種執(zhí)行順序。
結(jié)論:id值相同,從上往下順序執(zhí)行。表的執(zhí)行順序因表數(shù)量的改變而改變。
② 案例:查詢教授SQL課程的老師的描述(desc)
# 查看執(zhí)行計(jì)劃
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')
);
結(jié)果如下:
結(jié)論:id值不同,id值越大越優(yōu)先查詢。這是由于在進(jìn)行嵌套子查詢時(shí),先查內(nèi)層,再查外層。
③ 針對(duì)②做一個(gè)簡(jiǎn)單的修改
# 查看執(zhí)行計(jì)劃
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') ;
結(jié)果如下:
結(jié)論:id值有相同,又有不同。id值越大越優(yōu)先;id值相同,從上往下順序執(zhí)行。
(2)select_type關(guān)鍵字的使用說明:查詢類型
① simple:簡(jiǎn)單查詢
不包含子查詢,不包含union查詢。
explain select * from teacher;
結(jié)果如下:
② primary:包含子查詢的主查詢(最外層)
③ subquery:包含子查詢的主查詢(非最外層)
④ derived:衍生查詢(用到了臨時(shí)表)
a.在from子查詢中,只有一張表;
b.在from子查詢中,如果table1 union table2,則table1就是derived表;
explain select cr.cname
from ( select * from course where tid = 1 union select * from course where tid = 2 ) cr ;
結(jié)果如下:
⑤ union:union之后的表稱之為union表,如上例
⑥ union result:告訴我們,哪些表之間使用了union查詢
(3)type關(guān)鍵字的使用說明:索引類型
system、const只是理想狀況,實(shí)際上只能優(yōu)化到index --> range --> ref這個(gè)級(jí)別。要對(duì)type進(jìn)行優(yōu)化的前提是,你得創(chuàng)建索引。
① system
源表只有一條數(shù)據(jù)(實(shí)際中,基本不可能);
衍生表只有一條數(shù)據(jù)的主查詢(偶爾可以達(dá)到)。
② const
僅僅能查到一條數(shù)據(jù)的SQL ,僅針對(duì)Primary key或unique索引類型有效。
explain select tid from test01 where tid =1 ;
結(jié)果如下:
刪除以前的主鍵索引后,此時(shí)我們添加一個(gè)其他的普通索引:
create index test01_index on test01(tid) ;
# 再次查看執(zhí)行計(jì)劃
explain select tid from test01 where tid =1 ;
結(jié)果如下:
③ eq_ref
唯一性索引,對(duì)于每個(gè)索引鍵的查詢,返回匹配唯一行數(shù)據(jù)(有且只有1個(gè),不能多 、不能0),并且查詢結(jié)果和數(shù)據(jù)條數(shù)必須一致。
此種情況常見于唯一索引和主鍵索引。
delete from teacher where tcid >= 4;
alter table teacherCard add constraint pk_tcid primary key(tcid);
alter table teacher add constraint uk_tcid unique index(tcid) ;
explain select t.tcid from teacher t,teacherCard tc where t.tcid = tc.tcid ;
結(jié)果如下:
總結(jié):以上SQL,用到的索引是t.tcid,即teacher表中的tcid字段;如果teacher表的數(shù)據(jù)個(gè)數(shù)和連接查詢的數(shù)據(jù)個(gè)數(shù)一致(都是3條數(shù)據(jù)),則有可能滿足eq_ref級(jí)別;否則無(wú)法滿足。條件很苛刻,很難達(dá)到。
④ ref
非唯一性索引,對(duì)于每個(gè)索引鍵的查詢,返回匹配的所有行(可以0,可以1,可以多)
準(zhǔn)備數(shù)據(jù):
創(chuàng)建索引,并查看執(zhí)行計(jì)劃:
# 添加索引
alter table teacher add index index_name (tname) ;
# 查看執(zhí)行計(jì)劃
explain select * from teacher where tname = 'tz';
結(jié)果如下:
⑤ range
檢索指定范圍的行 ,where后面是一個(gè)范圍查詢(between, >, <, >=, in)
in有時(shí)候會(huì)失效,從而轉(zhuǎn)為無(wú)索引時(shí)候的ALL
# 添加索引
alter table teacher add index tid_index (tid) ;
# 查看執(zhí)行計(jì)劃:以下寫了一種等價(jià)SQL寫法,查看執(zhí)行計(jì)劃
explain select t.* from teacher t where t.tid in (1,2) ;
explain select t.* from teacher t where t.tid <3 ;
結(jié)果如下:
⑥ index
查詢?nèi)克饕械臄?shù)據(jù)(掃描整個(gè)索引)
⑦ ALL
查詢?nèi)吭幢碇械臄?shù)據(jù)(暴力掃描全表)
注意:cid是索引字段,因此查詢索引字段,只需要掃描索引表即可。但是tid不是索引字段,查詢非索引字段,需要暴力掃描整個(gè)源表,會(huì)消耗更多的資源。
(4)possible_keys和key
possible_keys可能用到的索引。是一種預(yù)測(cè),不準(zhǔn)。了解一下就好。
key指的是實(shí)際使用的索引。
# 先給course表的cname字段,添加一個(gè)索引
create index cname_index on course(cname);
# 查看執(zhí)行計(jì)劃
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') ;
結(jié)果如下:
有一點(diǎn)需要注意的是:如果possible_key/key是NULL,則說明沒用索引。
(5)key_len
索引的長(zhǎng)度,用于判斷復(fù)合索引是否被完全使用(a,b,c)。
① 新建一張新表,用于測(cè)試
# 創(chuàng)建表
create table test_kl
(
name char(20) not null default ''
);
# 添加索引
alter table test_kl add index index_name(name) ;
# 查看執(zhí)行計(jì)劃
explain select * from test_kl where name ='' ;
結(jié)果如下:
結(jié)果分析:因?yàn)槲覜]有設(shè)置服務(wù)端的字符集,因此默認(rèn)的字符集使用的是latin1,對(duì)于latin1一個(gè)字符代表一個(gè)字節(jié),因此這列的key_len的長(zhǎng)度是20,表示使用了name這個(gè)索引。
② 給test_kl表,新增name1列,該列沒有設(shè)置“not null”
結(jié)果如下:
結(jié)果分析:如果索引字段可以為null,則mysql底層會(huì)使用1個(gè)字節(jié)用于標(biāo)識(shí)。
③ 刪除原來(lái)的索引name和name1,新增一個(gè)復(fù)合索引
# 刪除原來(lái)的索引name和name1
drop index index_name on test_kl ;
drop index index_name1 on test_kl ;
# 增加一個(gè)復(fù)合索引
create index name_name1_index on test_kl(name,name1);
# 查看執(zhí)行計(jì)劃
explain select * from test_kl where name1 = '' ; --121
explain select * from test_kl where name = '' ; --60
結(jié)果如下:
結(jié)果分析: 對(duì)于下面這個(gè)執(zhí)行計(jì)劃,可以看到我們只使用了復(fù)合索引的第一個(gè)索引字段name,因此key_len是20,這個(gè)很清楚。再看上面這個(gè)執(zhí)行計(jì)劃,我們雖然僅僅在where后面使用了復(fù)合索引字段中的name1字段,但是你要使用復(fù)合索引的第2個(gè)索引字段,會(huì)默認(rèn)使用了復(fù)合索引的第1個(gè)索引字段name,由于name1可以是null,因此key_len = 20 + 20 + 1 = 41呀!
④ 再次怎加一個(gè)name2字段,并為該字段創(chuàng)建一個(gè)索引。
不同的是:該字段數(shù)據(jù)類型是varchar
# 新增一個(gè)字段name2,name2可以為null
alter table test_kl add column name2 varchar(20) ;
# 給name2字段,設(shè)置為索引字段
alter table test_kl add index name2_index(name2) ;
# 查看執(zhí)行計(jì)劃
explain select * from test_kl where name2 = '' ;
結(jié)果如下:
結(jié)果分析: key_len = 20 + 1 + 2,這個(gè)20 + 1我們知道,這個(gè)2又代表什么呢?原來(lái)varchar屬于可變長(zhǎng)度,在mysql底層中,用2個(gè)字節(jié)標(biāo)識(shí)可變長(zhǎng)度。
(6)ref
這里的ref的作用,指明當(dāng)前表所參照的字段。
注意與type中的ref值區(qū)分。在type中,ref只是type類型的一種選項(xiàng)值。
# 給course表的tid字段,添加一個(gè)索引
create index tid_index on course(tid);
# 查看執(zhí)行計(jì)劃
explain select * from course c,teacher t
where c.tid = t.tid
and t.tname = 'tw';
結(jié)果如下:
結(jié)果分析: 有兩個(gè)索引,c表的c.tid引用的是t表的tid字段,因此可以看到顯示結(jié)果為【數(shù)據(jù)庫(kù)名.t.tid】,t表的t.name引用的是一個(gè)常量"tw",因此可以看到結(jié)果顯示為const,表示一個(gè)常量。
(7)rows(這個(gè)目前還是有點(diǎn)疑惑)
被索引優(yōu)化查詢的數(shù)據(jù)個(gè)數(shù) (實(shí)際通過索引而查詢到的數(shù)據(jù)個(gè)數(shù))
explain select *
from course c,teacher t
where c.tid = t.tid
and t.tname = 'tz' ;
結(jié)果如下:
(8)extra
表示其他的一些說明,也很有用。
① using filesort:針對(duì)單索引的情況
當(dāng)出現(xiàn)了這個(gè)詞,表示你當(dāng)前的SQL性能消耗較大。表示進(jìn)行了一次“額外”的排序。常見于order by語(yǔ)句中。
Ⅰ 什么是“額外”的排序?
為了講清楚這個(gè),我們首先要知道什么是排序。我們?yōu)榱私o某一個(gè)字段進(jìn)行排序的時(shí)候,首先你得先查詢到這個(gè)字段,然后在將這個(gè)字段進(jìn)行排序。
緊接著,我們查看如下兩個(gè)SQL語(yǔ)句的執(zhí)行計(jì)劃。
# 新建一張表,建表同時(shí)創(chuàng)建索引
create table test02
(
a1 char(3),
a2 char(3),
a3 char(3),
index idx_a1(a1),
index idx_a2(a2),
index idx_a3(a3)
);
# 查看執(zhí)行計(jì)劃
explain select * from test02 where a1 ='' order by a1 ;
explain select * from test02 where a1 ='' order by a2 ;
結(jié)果如下:
結(jié)果分析: 對(duì)于第一個(gè)執(zhí)行計(jì)劃,where后面我們先查詢了a1字段,然后再利用a1做了依次排序,這個(gè)很輕松。但是對(duì)于第二個(gè)執(zhí)行計(jì)劃,where后面我們查詢了a1字段,然而利用的卻是a2字段進(jìn)行排序,此時(shí)myql底層會(huì)進(jìn)行一次查詢,進(jìn)行“額外”的排序。
總結(jié):對(duì)于單索引,如果排序和查找是同一個(gè)字段,則不會(huì)出現(xiàn)using filesort;如果排序和查找不是同一個(gè)字段,則會(huì)出現(xiàn)using filesort;因此where哪些字段,就order by哪些些字段。
② using filesort:針對(duì)復(fù)合索引的情況
不能跨列(官方術(shù)語(yǔ):最佳左前綴)
# 刪除test02的索引
drop index idx_a1 on test02;
drop index idx_a2 on test02;
drop index idx_a3 on test02;
# 創(chuàng)建一個(gè)復(fù)合索引
alter table test02 add index idx_a1_a2_a3 (a1,a2,a3) ;
# 查看下面SQL語(yǔ)句的執(zhí)行計(jì)劃
explain select *from test02 where a1='' order by a3 ; --using filesort
explain select *from test02 where a2='' order by a3 ; --using filesort
explain select *from test02 where a1='' order by a2 ;
結(jié)果如下:
結(jié)果分析: 復(fù)合索引的順序是(a1,a2,a3),可以看到a1在最左邊,因此a1就叫做“最佳左前綴”,如果要使用后面的索引字段,必須先使用到這個(gè)a1字段。對(duì)于explain1,where后面我們使用a1字段,但是后面的排序使用了a3,直接跳過了a2,屬于跨列;對(duì)于explain2,where后面我們使用了a2字段,直接跳過了a1字段,也屬于跨列;對(duì)于explain3,where后面我們使用a1字段,后面使用的是a2字段,因此沒有出現(xiàn)【using filesort】。
③ using temporary
當(dāng)出現(xiàn)了這個(gè)詞,也表示你當(dāng)前的SQL性能消耗較大。這是由于當(dāng)前SQL用到了臨時(shí)表。一般出現(xiàn)在group by中。
explain select a1 from test02 where a1 in ('1','2','3') group by a1 ;
explain select a1 from test02 where a1 in ('1','2','3') group by a2 ; --using temporary
結(jié)果如下:
結(jié)果分析: 當(dāng)你查詢哪個(gè)字段,就按照那個(gè)字段分組,否則就會(huì)出現(xiàn)using temporary。
針對(duì)using temporary,我們?cè)诳匆粋€(gè)例子:
using temporary表示需要額外再使用一張表,一般出現(xiàn)在group by語(yǔ)句中。雖然已經(jīng)有表了,但是不適用,必須再來(lái)一張表。
再次來(lái)看mysql的編寫過程和解析過程。
Ⅰ 編寫過程
select dinstinct ..from ..join ..on ..where ..group by ..having ..order by ..limit ..
Ⅱ 解析過程
from .. on.. join ..where ..group by ..having ..select dinstinct ..order by ..limit ..
很顯然,where后是group by,然后才是select?;诖耍覀?cè)俨榭慈缦聝蓚€(gè)SQL語(yǔ)句的執(zhí)行計(jì)劃。
explain select * from test03 where a2=2 and a4=4 group by a2,a4;
explain select * from test03 where a2=2 and a4=4 group by a3;
分析如下: 對(duì)于第一個(gè)執(zhí)行計(jì)劃,where后面是a2和a4,接著我們按照a2和a4分組,很明顯這兩張表已經(jīng)有了,直接在a2和a4上分組就行了。但是對(duì)于第二個(gè)執(zhí)行計(jì)劃,where后面是a2和a4,接著我們卻按照a3分組,很明顯我們沒有a3這張表,因此有需要再來(lái)一張臨時(shí)表a3。因此就會(huì)出現(xiàn)using temporary。
④ using index
當(dāng)你看到這個(gè)關(guān)鍵詞,恭喜你,表示你的SQL性能提升了。
using index稱之為“索引覆蓋”。
當(dāng)出現(xiàn)了using index,就表示不用讀取源表,而只利用索引獲取數(shù)據(jù),不需要回源表查詢。
只要使用到的列,全部出現(xiàn)在索引中,就是索引覆蓋。
# 刪除test02中的復(fù)合索引idx_a1_a2_a3
drop index idx_a1_a2_a3 on test02;
# 重新創(chuàng)建一個(gè)復(fù)合索引
idx_a1_a2create index idx_a1_a2 on test02(a1,a2);
# 查看執(zhí)行計(jì)劃
explain select a1,a3 from test02 where a1='' or a3= '' ;
explain select a1,a2 from test02 where a
新聞標(biāo)題:18000字的SQL優(yōu)化大全,收藏直接起飛!
分享URL:http://m.5511xx.com/article/coghssh.html


咨詢
建站咨詢
