0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

關(guān)于SQL優(yōu)化的硬核文章

數(shù)據(jù)分析與開發(fā) ? 來源:數(shù)據(jù)分析不是個(gè)事兒 ? 2023-05-06 14:19 ? 次閱讀

分享一篇關(guān)于SQL優(yōu)化的硬核文章,全文有點(diǎn)長(zhǎng),建議收藏后慢慢看。

很多朋友在做數(shù)據(jù)分析時(shí),分析兩分鐘,跑數(shù)兩小時(shí)?

在使用SQL過程中不僅要關(guān)注數(shù)據(jù)結(jié)果,同樣要注意SQL語句的執(zhí)行效率。

本文涉及三部分:

  • SQL介紹
  • SQL優(yōu)化方法
  • SQL優(yōu)化實(shí)例

1、MySQL的基本架構(gòu)

1)MySQL的基礎(chǔ)架構(gòu)圖

2a9e6bb8-ebc9-11ed-90ce-dac502259ad0.jpg

左邊的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ù)庫的引擎

① show engines;

2aabda5a-ebc9-11ed-90ce-dac502259ad0.png

② show variables like “%storage_engine%”;

2ab4c9c6-ebc9-11ed-90ce-dac502259ad0.png

3)指定數(shù)據(jù)庫對(duì)象的存儲(chǔ)引擎

createtabletb(
idint(4)auto_increment,
namevarchar(5),
deptvarchar(5),
primarykey(id)
)engine=myISAMauto_increment=1defaultcharset=utf8;

SQL優(yōu)化

1)為什么需要進(jìn)行SQL優(yōu)化?

在進(jìn)行多表連接查詢、子查詢等操作的時(shí)候,由于你寫出的SQL語句欠佳,導(dǎo)致的服務(wù)器執(zhí)行時(shí)間太長(zhǎng),我們等待結(jié)果的時(shí)間太長(zhǎng)。基于此,我們需要學(xué)習(xí)怎么優(yōu)化SQL。

2)mysql的編寫過程和解析過程

① 編寫過程

selectdinstinct..from..join..on..where..groupby..having..orderby..limit..

② 解析過程

from..on..join..where..groupby..having..selectdinstinct..orderby..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+樹】。

② 索引圖示說明(這里用二叉樹來幫助我們理解索引)

樹形結(jié)構(gòu)的特點(diǎn)是:子元素比父元素小的,放在左側(cè);子元素比父元素大的,放在右側(cè)。

這個(gè)圖示只是為了幫我們簡(jiǎn)單理解索引的,真實(shí)的關(guān)于【B+樹】的說明,我們會(huì)在下面進(jìn)行說明。

2abe2494-ebc9-11ed-90ce-dac502259ad0.png

索引是怎么查找數(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】查詢語句為例。當(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)然相比于源表來說,還是相當(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)。

2ac704ec-ebc9-11ed-90ce-dac502259ad0.jpg

關(guān)于B+樹的說明:

首先,Btree一般指的都是【B+樹】,數(shù)據(jù)全部存放在葉子節(jié)點(diǎn)中。對(duì)于上圖來說,最下面的第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+樹】可以存放上百萬條數(shù)據(jù)。這么多數(shù)據(jù)怎么放的呢?增加“節(jié)點(diǎn)數(shù)”。圖中我們只有三個(gè)節(jié)點(diǎn)。

最后,【B+樹】中查詢?nèi)我鈹?shù)據(jù)的次數(shù),都是n次,n表示的是【B+樹】的高度。

3、索引的分類與創(chuàng)建

1)索引分類

單值索引

唯一索引

復(fù)合索引

① 單值索引

利用表中的某一個(gè)字段創(chuàng)建單值索引。一張表中往往有多個(gè)字段,也就是說每一列其實(shí)都可以創(chuàng)建一個(gè)索引,這個(gè)根據(jù)我們實(shí)際需求來進(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)建索引

① 語法

語法:create 索引類型 索引名 on 表(字段);

建表語句如下:

查詢表結(jié)構(gòu)如下:

2acf7ab4-ebc9-11ed-90ce-dac502259ad0.png

② 創(chuàng)建索引的第一種方式

Ⅰ 創(chuàng)建單值索引

createindexdept_indexontb(dept);

Ⅱ 創(chuàng)建唯一索引:這里我們假定name字段中的值都是唯一的

createuniqueindexname_indexontb(name);

Ⅲ 創(chuàng)建復(fù)合索引

createindexdept_name_indexontb(dept,name);

③ 創(chuàng)建索引的第二種方式

先刪除之前創(chuàng)建的索引以后,再進(jìn)行這種創(chuàng)建索引方式的測(cè)試;

語法:alter table 表名 add 索引類型 索引名(字段)

Ⅰ 創(chuàng)建單值索引

altertabletbaddindexdept_index(dept);

Ⅱ 創(chuàng)建唯一索引:這里我們假定name字段中的值都是唯一的

altertabletbadduniqueindexname_index(name);

Ⅲ 創(chuàng)建復(fù)合索引

altertabletbaddindexdept_name_index(dept,name);

④ 補(bǔ)充說明

如果某個(gè)字段是primary key,那么該字段默認(rèn)就是主鍵索引。

主鍵索引和唯一索引非常相似。相同點(diǎn):該列中的數(shù)據(jù)都不能有相同值;不同點(diǎn):主鍵索引不能有null值,但是唯一索引可以有null值。

3)索引刪除和索引查詢

① 索引刪除

語法:drop index 索引名 on 表名;

dropindexname_indexontb;

② 索引查詢

語法:show index from 表名;

showindexfromtb;

結(jié)果如下:

2ad749ce-ebc9-11ed-90ce-dac502259ad0.png

4、SQL性能問題的探索

人為優(yōu)化: 需要我們使用explain分析SQL的執(zhí)行計(jì)劃。該執(zhí)行計(jì)劃可以模擬SQL優(yōu)化器執(zhí)行SQL語句,可以幫助我們了解到自己編寫SQL的好壞。

SQL優(yōu)化器自動(dòng)優(yōu)化: 最開始講述MySQL執(zhí)行原理的時(shí)候,我們已經(jīng)知道MySQL有一個(gè)優(yōu)化器,當(dāng)你寫了一個(gè)SQL語句的時(shí)候,SQL優(yōu)化器如果認(rèn)為你寫的SQL語句不夠好,就會(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語句來執(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ì)劃

語法:explain + SQL語句

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 :額外的信息

建表語句和插入數(shù)據(jù):

#建表語句
createtablecourse
(
cidint(3),
cnamevarchar(20),
tidint(3)
);

createtableteacher
(
tidint(3),
tnamevarchar(20),
tcidint(3)
);

createtableteacherCard
(
tcidint(3),
tcdescvarchar(200)
);

#插入數(shù)據(jù)
insertintocoursevalues(1,'java',1);
insertintocoursevalues(2,'html',1);
insertintocoursevalues(3,'sql',2);
insertintocoursevalues(4,'web',3);

insertintoteachervalues(1,'tz',1);
insertintoteachervalues(2,'tw',2);
insertintoteachervalues(3,'tl',3);

insertintoteacherCardvalues(1,'tzdesc');
insertintoteacherCardvalues(2,'twdesc');
insertintoteacherCardvalues(3,'tldesc');

explain執(zhí)行計(jì)劃常用關(guān)鍵字詳解

1)id關(guān)鍵字的使用說明

① 案例:查詢課程編號(hào)為2 或 教師證編號(hào)為3 的老師信息:

#查看執(zhí)行計(jì)劃
explainselectt.*
fromteachert,coursec,teacherCardtc
wheret.tid=c.tidandt.tcid=tc.tcid
and(c.cid=2ortc.tcid=3);

結(jié)果如下:

2ae35b1a-ebc9-11ed-90ce-dac502259ad0.png

接著,在往teacher表中增加幾條數(shù)據(jù)。

insertintoteachervalues(4,'ta',4);
insertintoteachervalues(5,'tb',5);
insertintoteachervalues(6,'tc',6);

再次查看執(zhí)行計(jì)劃。

#查看執(zhí)行計(jì)劃
explainselectt.*
fromteachert,coursec,teacherCardtc
wheret.tid=c.tidandt.tcid=tc.tcid
and(c.cid=2ortc.tcid=3);

結(jié)果如下:

2aede4cc-ebc9-11ed-90ce-dac502259ad0.png

表的執(zhí)行順序 ,因表數(shù)量改變而改變的原因:笛卡爾積。

abc
234
最終:2 * 3 * 4 = 6 * 4 = 24
cba
432
最終:4 * 3 * 2 = 12 * 2 = 24

分析:最終執(zhí)行的條數(shù),雖然是一致的。但是中間過程,有一張臨時(shí)表是6,一張臨時(shí)表是12,很明顯6 < 12,對(duì)于內(nèi)存來說,數(shù)據(jù)量越小越好,因此優(yōu)化器肯定會(huì)選擇第一種執(zhí)行順序。

結(jié)論:id值相同,從上往下順序執(zhí)行。表的執(zhí)行順序因表數(shù)量的改變而改變。

② 案例:查詢教授SQL課程的老師的描述(desc)

#查看執(zhí)行計(jì)劃
explainselecttc.tcdescfromteacherCardtc
wheretc.tcid=
(
selectt.tcidfromteachert
wheret.tid=
(selectc.tidfromcoursecwherec.cname='sql')
);

結(jié)果如下:

2afa2890-ebc9-11ed-90ce-dac502259ad0.png

結(jié)論:id值不同,id值越大越優(yōu)先查詢。這是由于在進(jìn)行嵌套子查詢時(shí),先查內(nèi)層,再查外層。

③ 針對(duì)②做一個(gè)簡(jiǎn)單的修改

#查看執(zhí)行計(jì)劃
explainselectt.tname,tc.tcdescfromteachert,teacherCardtc
wheret.tcid=tc.tcid
andt.tid=(selectc.tidfromcoursecwherecname='sql');

結(jié)果如下:

2b0299d0-ebc9-11ed-90ce-dac502259ad0.png

結(jié)論:id值有相同,又有不同。id值越大越優(yōu)先;id值相同,從上往下順序執(zhí)行。

2)select_type關(guān)鍵字的使用說明:查詢類型

2b122ca6-ebc9-11ed-90ce-dac502259ad0.png

simple:簡(jiǎn)單查詢

不包含子查詢,不包含union查詢。

explainselect*fromteacher;

結(jié)果如下:

2b184dd4-ebc9-11ed-90ce-dac502259ad0.png

② primary:包含子查詢的主查詢(最外層)

subquery:包含子查詢的主查詢(非最外層)

④ derived:衍生查詢(用到了臨時(shí)表)

a.在from子查詢中,只有一張表;

b.在from子查詢中,如果table1 union table2,則table1就是derived表;

explainselectcr.cname
from(select*fromcoursewheretid=1unionselect*fromcoursewheretid=2)cr;

結(jié)果如下:

2b20269e-ebc9-11ed-90ce-dac502259ad0.png

⑤ 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)建索引。

2b2adbca-ebc9-11ed-90ce-dac502259ad0.png

① system

源表只有一條數(shù)據(jù)(實(shí)際中,基本不可能);

衍生表只有一條數(shù)據(jù)的主查詢(偶爾可以達(dá)到)。

② const

僅僅能查到一條數(shù)據(jù)的SQL ,僅針對(duì)Primary key或unique索引類型有效。

explainselecttidfromtest01wheretid=1;

結(jié)果如下:

2b37adbe-ebc9-11ed-90ce-dac502259ad0.png

刪除以前的主鍵索引后,此時(shí)我們添加一個(gè)其他的普通索引:

createindextest01_indexontest01(tid);
#再次查看執(zhí)行計(jì)劃
explainselecttidfromtest01wheretid=1;

結(jié)果如下:

2b410f08-ebc9-11ed-90ce-dac502259ad0.png

③ eq_ref

唯一性索引,對(duì)于每個(gè)索引鍵的查詢,返回匹配唯一行數(shù)據(jù)(有且只有1個(gè),不能多 、不能0),并且查詢結(jié)果和數(shù)據(jù)條數(shù)必須一致。

此種情況常見于唯一索引和主鍵索引。

deletefromteacherwheretcid>=4;
altertableteacherCardaddconstraintpk_tcidprimarykey(tcid);
altertableteacheraddconstraintuk_tciduniqueindex(tcid);
explainselectt.tcidfromteachert,teacherCardtcwheret.tcid=tc.tcid;

結(jié)果如下:

2b4be112-ebc9-11ed-90ce-dac502259ad0.png

總結(jié):以上SQL,用到的索引是t.tcid,即teacher表中的tcid字段;如果teacher表的數(shù)據(jù)個(gè)數(shù)和連接查詢的數(shù)據(jù)個(gè)數(shù)一致(都是3條數(shù)據(jù)),則有可能滿足eq_ref級(jí)別;否則無法滿足。條件很苛刻,很難達(dá)到。

④ ref

非唯一性索引,對(duì)于每個(gè)索引鍵的查詢,返回匹配的所有行(可以0,可以1,可以多)

準(zhǔn)備數(shù)據(jù):

2b54dab0-ebc9-11ed-90ce-dac502259ad0.png

創(chuàng)建索引,并查看執(zhí)行計(jì)劃:

#添加索引
altertableteacheraddindexindex_name(tname);
#查看執(zhí)行計(jì)劃
explainselect*fromteacherwheretname='tz';

結(jié)果如下:

2b60f494-ebc9-11ed-90ce-dac502259ad0.png

⑤ range

檢索指定范圍的行 ,where后面是一個(gè)范圍查詢(between, >, <, >=, in)

in有時(shí)候會(huì)失效,從而轉(zhuǎn)為無索引時(shí)候的ALL

#添加索引
altertableteacheraddindextid_index(tid);
#查看執(zhí)行計(jì)劃:以下寫了一種等價(jià)SQL寫法,查看執(zhí)行計(jì)劃
explainselectt.*fromteachertwheret.tidin(1,2);
explainselectt.*fromteachertwheret.tid<3?;

結(jié)果如下:

2b6b885a-ebc9-11ed-90ce-dac502259ad0.png

⑥ index

查詢?nèi)克饕械臄?shù)據(jù)(掃描整個(gè)索引)

⑦ ALL

查詢?nèi)吭幢碇械臄?shù)據(jù)(暴力掃描全表)

2b77e8f2-ebc9-11ed-90ce-dac502259ad0.png

注意:cid是索引字段,因此查詢索引字段,只需要掃描索引表即可。但是tid不是索引字段,查詢非索引字段,需要暴力掃描整個(gè)源表,會(huì)消耗更多的資源。

4)possible_keys和key

possible_keys可能用到的索引。是一種預(yù)測(cè),不準(zhǔn)。了解一下就好。

key指的是實(shí)際使用的索引。

#先給course表的cname字段,添加一個(gè)索引
createindexcname_indexoncourse(cname);
#查看執(zhí)行計(jì)劃
explainselectt.tname,tc.tcdescfromteachert,teacherCardtc
wheret.tcid=tc.tcid
andt.tid=(selectc.tidfromcoursecwherecname='sql');

結(jié)果如下:

2b823dc0-ebc9-11ed-90ce-dac502259ad0.png

有一點(diǎn)需要注意的是:如果possible_key/key是NULL,則說明沒用索引。

5)key_len

索引的長(zhǎng)度,用于判斷復(fù)合索引是否被完全使用(a,b,c)。

① 新建一張新表,用于測(cè)試

#創(chuàng)建表
createtabletest_kl
(
namechar(20)notnulldefault''
);
#添加索引
altertabletest_kladdindexindex_name(name);
#查看執(zhí)行計(jì)劃
explainselect*fromtest_klwherename='';

結(jié)果如下:

2b93bba4-ebc9-11ed-90ce-dac502259ad0.png

結(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é)果如下:

2ba11574-ebc9-11ed-90ce-dac502259ad0.png

結(jié)果分析:如果索引字段可以為null,則mysql底層會(huì)使用1個(gè)字節(jié)用于標(biāo)識(shí)。

③ 刪除原來的索引name和name1,新增一個(gè)復(fù)合索引

#刪除原來的索引name和name1
dropindexindex_nameontest_kl;
dropindexindex_name1ontest_kl;
#增加一個(gè)復(fù)合索引
createindexname_name1_indexontest_kl(name,name1);
#查看執(zhí)行計(jì)劃
explainselect*fromtest_klwherename1='';--121
explainselect*fromtest_klwherename='';--60

結(jié)果如下:

2baa3be0-ebc9-11ed-90ce-dac502259ad0.png

結(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
altertabletest_kladdcolumnname2varchar(20);
#給name2字段,設(shè)置為索引字段
altertabletest_kladdindexname2_index(name2);
#查看執(zhí)行計(jì)劃
explainselect*fromtest_klwherename2='';

結(jié)果如下:

2bb08a5e-ebc9-11ed-90ce-dac502259ad0.png

結(jié)果分析: key_len = 20 + 1 + 2,這個(gè)20 + 1我們知道,這個(gè)2又代表什么呢?原來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è)索引
createindextid_indexoncourse(tid);
#查看執(zhí)行計(jì)劃
explainselect*fromcoursec,teachert
wherec.tid=t.tid
andt.tname='tw';

結(jié)果如下:

2bb8b9f4-ebc9-11ed-90ce-dac502259ad0.png

結(jié)果分析: 有兩個(gè)索引,c表的c.tid引用的是t表的tid字段,因此可以看到顯示結(jié)果為【數(shù)據(jù)庫名.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ù))

explainselect*
fromcoursec,teachert
wherec.tid=t.tid
andt.tname='tz';

結(jié)果如下:

2bc114a0-ebc9-11ed-90ce-dac502259ad0.png

8)extra

表示其他的一些說明,也很有用。

① using filesort:針對(duì)單索引的情況

當(dāng)出現(xiàn)了這個(gè)詞,表示你當(dāng)前的SQL性能消耗較大。表示進(jìn)行了一次“額外”的排序。常見于order by語句中。

Ⅰ 什么是“額外”的排序?

為了講清楚這個(gè),我們首先要知道什么是排序。我們?yōu)榱私o某一個(gè)字段進(jìn)行排序的時(shí)候,首先你得先查詢到這個(gè)字段,然后在將這個(gè)字段進(jìn)行排序。

緊接著,我們查看如下兩個(gè)SQL語句的執(zhí)行計(jì)劃。

#新建一張表,建表同時(shí)創(chuàng)建索引
createtabletest02
(
a1char(3),
a2char(3),
a3char(3),
indexidx_a1(a1),
indexidx_a2(a2),
indexidx_a3(a3)
);
#查看執(zhí)行計(jì)劃
explainselect*fromtest02wherea1=''orderbya1;
explainselect*fromtest02wherea1=''orderbya2;

結(jié)果如下:

2bce9c74-ebc9-11ed-90ce-dac502259ad0.png

結(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ù)語:最佳左前綴)

#刪除test02的索引
dropindexidx_a1ontest02;
dropindexidx_a2ontest02;
dropindexidx_a3ontest02;
#創(chuàng)建一個(gè)復(fù)合索引
altertabletest02addindexidx_a1_a2_a3(a1,a2,a3);
#查看下面SQL語句的執(zhí)行計(jì)劃
explainselect*fromtest02wherea1=''orderbya3;--usingfilesort
explainselect*fromtest02wherea2=''orderbya3;--usingfilesort
explainselect*fromtest02wherea1=''orderbya2;

結(jié)果如下:

2bd998a4-ebc9-11ed-90ce-dac502259ad0.png

結(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中。

explainselecta1fromtest02wherea1in('1','2','3')groupbya1;
explainselecta1fromtest02wherea1in('1','2','3')groupbya2;--usingtemporary

結(jié)果如下:

2be584a2-ebc9-11ed-90ce-dac502259ad0.png

結(jié)果分析: 當(dāng)你查詢哪個(gè)字段,就按照那個(gè)字段分組,否則就會(huì)出現(xiàn)using temporary。

針對(duì)using temporary,我們?cè)诳匆粋€(gè)例子:

using temporary表示需要額外再使用一張表,一般出現(xiàn)在group by語句中。雖然已經(jīng)有表了,但是不適用,必須再來一張表。

再次來看mysql的編寫過程和解析過程。

Ⅰ 編寫過程

selectdinstinct..from..join..on..where..groupby..having..orderby..limit..

Ⅱ 解析過程

from..on..join..where..groupby..having..selectdinstinct..orderby..limit..

很顯然,where后是group by,然后才是select?;诖?,我們?cè)俨榭慈缦聝蓚€(gè)SQL語句的執(zhí)行計(jì)劃。

explainselect*fromtest03wherea2=2anda4=4groupbya2,a4;
explainselect*fromtest03wherea2=2anda4=4groupbya3;

分析如下: 對(duì)于第一個(gè)執(zhí)行計(jì)劃,where后面是a2和a4,接著我們按照a2和a4分組,很明顯這兩張表已經(jīng)有了,直接在a2和a4上分組就行了。但是對(duì)于第二個(gè)執(zhí)行計(jì)劃,where后面是a2和a4,接著我們卻按照a3分組,很明顯我們沒有a3這張表,因此有需要再來一張臨時(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
dropindexidx_a1_a2_a3ontest02;
#重新創(chuàng)建一個(gè)復(fù)合索引
idx_a1_a2createindexidx_a1_a2ontest02(a1,a2);
#查看執(zhí)行計(jì)劃
explainselecta1,a3fromtest02wherea1=''ora3='';
explainselecta1,a2fromtest02wherea1=''anda2='';

結(jié)果如下:

2bec1e84-ebc9-11ed-90ce-dac502259ad0.png

結(jié)果分析: 我們創(chuàng)建的是a1和a2的復(fù)合索引,對(duì)于第一個(gè)執(zhí)行計(jì)劃,我們卻出現(xiàn)了a3,該字段并沒有創(chuàng)建索引,因此沒有出現(xiàn)using index,而是using where,表示我們需要回表查詢。對(duì)于第二個(gè)執(zhí)行計(jì)劃,屬于完全的索引覆蓋,因此出現(xiàn)了using index。

針對(duì)using index,我們?cè)诓榭匆粋€(gè)案例:

explainselecta1,a2fromtest02wherea1=''ora2='';
explainselecta1,a2fromtest02;

結(jié)果如下:

如果用到了索引覆蓋(using index時(shí)),會(huì)對(duì)possible_keys和key造成影響:

a.如果沒有where,則索引只出現(xiàn)在key中;

b.如果有where,則索引 出現(xiàn)在key和possible_keys中。

⑤ using where

表示需要【回表查詢】,表示既在索引中進(jìn)行了查詢,又回到了源表進(jìn)行了查詢。

#刪除test02中的復(fù)合索引idx_a1_a2
dropindexidx_a1_a2ontest02;
#將a1字段,新增為一個(gè)索引
createindexa1_indexontest02(a1);
#查看執(zhí)行計(jì)劃
explainselecta1,a3fromtest02wherea1=""anda3="";

結(jié)果如下:

2bf66a9c-ebc9-11ed-90ce-dac502259ad0.png

結(jié)果分析: 我們既使用了索引a1,表示我們使用了索引進(jìn)行查詢。但是又對(duì)于a3字段,我們并沒有使用索引,因此對(duì)于a3字段,需要回源表查詢,這個(gè)時(shí)候出現(xiàn)了using where。

⑥ impossible where(了解)

當(dāng)where子句永遠(yuǎn)為False的時(shí)候,會(huì)出現(xiàn)impossible where

#查看執(zhí)行計(jì)劃
explainselecta1fromtest02wherea1="a"anda1="b";

結(jié)果如下:

2bfdacc6-ebc9-11ed-90ce-dac502259ad0.png

6、優(yōu)化示例

1)引入案例

#創(chuàng)建新表
createtabletest03
(
a1int(4)notnull,
a2int(4)notnull,
a3int(4)notnull,
a4int(4)notnull
);
#創(chuàng)建一個(gè)復(fù)合索引
createindexa1_a2_a3_test03ontest03(a1,a2,a3);
#查看執(zhí)行計(jì)劃
explainselecta3fromtest03wherea1=1anda2=2anda3=3;

結(jié)果如下:

2c09e130-ebc9-11ed-90ce-dac502259ad0.png

推薦寫法: 復(fù)合索引順序和使用順序一致。

下面看看【不推薦寫法】:復(fù)合索引順序和使用順序不一致。

#查看執(zhí)行計(jì)劃
explainselecta3fromtest03wherea3=1anda2=2anda1=3;

結(jié)果如下:

2c12f342-ebc9-11ed-90ce-dac502259ad0.png

結(jié)果分析: 雖然結(jié)果和上述結(jié)果一致,但是不推薦這樣寫。但是這樣寫怎么又沒有問題呢?這是由于SQL優(yōu)化器的功勞,它幫我們調(diào)整了順序。

最后再補(bǔ)充一點(diǎn):對(duì)于復(fù)合索引,不要跨列使用

#查看執(zhí)行計(jì)劃
explainselecta3fromtest03wherea1=1anda3=2groupbya3;

結(jié)果如下:

2c254e5c-ebc9-11ed-90ce-dac502259ad0.png

結(jié)果分析: a1_a2_a3是一個(gè)復(fù)合索引,我們使用a1索引后,直接跨列使用了a3,直接跳過索引a2,因此索引a3失效了,當(dāng)使用a3進(jìn)行分組的時(shí)候,就會(huì)出現(xiàn)using where。

2)單表優(yōu)化

#創(chuàng)建新表
createtablebook
(
bidint(4)primarykey,
namevarchar(20)notnull,
authoridint(4)notnull,
publicidint(4)notnull,
typeidint(4)notnull
);
#插入數(shù)據(jù)
insertintobookvalues(1,'tjava',1,1,2);
insertintobookvalues(2,'tc',2,1,2);
insertintobookvalues(3,'wx',3,2,1);
insertintobookvalues(4,'math',4,2,3);

結(jié)果如下:

2c2f9ede-ebc9-11ed-90ce-dac502259ad0.png

案例:查詢authorid=1且typeid為2或3的bid,并根據(jù)typeid降序排列。

explain
selectbidfrombook
wheretypeidin(2,3)andauthorid=1
orderbytypeiddesc;

結(jié)果如下:

2c35eab4-ebc9-11ed-90ce-dac502259ad0.png

這是沒有進(jìn)行任何優(yōu)化的SQL,可以看到typ為ALL類型,extra為using filesort,可以想象這個(gè)SQL有多恐怖。

優(yōu)化:添加索引的時(shí)候,要根據(jù)MySQL解析順序添加索引,又回到了MySQL的解析順序,下面我們?cè)賮砜纯碝ySQL的解析順序。

from..on..join..where..groupby..having..selectdinstinct..orderby..limit..

① 優(yōu)化1:基于此,我們進(jìn)行索引的添加,并再次查看執(zhí)行計(jì)劃。

#添加索引
createindextypeid_authorid_bidonbook(typeid,authorid,bid);
#再次查看執(zhí)行計(jì)劃
explain
selectbidfrombook
wheretypeidin(2,3)andauthorid=1
orderbytypeiddesc;

結(jié)果如下:

2c43b856-ebc9-11ed-90ce-dac502259ad0.png

結(jié)果分析: 結(jié)果并不是和我們想象的一樣,還是出現(xiàn)了using where,查看索引長(zhǎng)度key_len=8,表示我們只使用了2個(gè)索引,有一個(gè)索引失效了。

② 優(yōu)化2:使用了in有時(shí)候會(huì)導(dǎo)致索引失效,基于此有了如下一種優(yōu)化思路。

將in字段放在最后面。需要注意一點(diǎn):每次創(chuàng)建新的索引的時(shí)候,最好是刪除以前的廢棄索引,否則有時(shí)候會(huì)產(chǎn)生干擾(索引之間)。

#刪除以前的索引
dropindextypeid_authorid_bidonbook;
#再次創(chuàng)建索引
createindexauthorid_typeid_bidonbook(authorid,typeid,bid);
#再次查看執(zhí)行計(jì)劃
explain
selectbidfrombook
whereauthorid=1andtypeidin(2,3)
orderbytypeiddesc;

結(jié)果如下:

2c4fad00-ebc9-11ed-90ce-dac502259ad0.png

結(jié)果分析: 這里雖然沒有變化,但是這是一種優(yōu)化思路。

總結(jié)如下:

a.最佳做前綴,保持索引的定義和使用的順序一致性

b.索引需要逐步優(yōu)化(每次創(chuàng)建新索引,根據(jù)情況需要?jiǎng)h除以前的廢棄索引)

c.將含In的范圍查詢,放到where條件的最后,防止失效。

本例中同時(shí)出現(xiàn)了Using where(需要回原表); Using index(不需要回原表):原因,where authorid=1 and typeid in(2,3)中authorid在索引(authorid,typeid,bid)中,因此不需要回原表(直接在索引表中能查到);而typeid雖然也在索引(authorid,typeid,bid)中,但是含in的范圍查詢已經(jīng)使該typeid索引失效,因此相當(dāng)于沒有typeid這個(gè)索引,所以需要回原表(using where);

例如以下沒有了In,則不會(huì)出現(xiàn)using where:

explainselectbidfrombook
whereauthorid=1andtypeid=3
orderbytypeiddesc;

結(jié)果如下:

2c580fd6-ebc9-11ed-90ce-dac502259ad0.png

3)兩表優(yōu)化

#創(chuàng)建teacher2新表
createtableteacher2
(
tidint(4)primarykey,
cidint(4)notnull
);
#插入數(shù)據(jù)
insertintoteacher2values(1,2);
insertintoteacher2values(2,1);
insertintoteacher2values(3,3);
#創(chuàng)建course2新表
createtablecourse2
(
cidint(4),
cnamevarchar(20)
);
#插入數(shù)據(jù)
insertintocourse2values(1,'java');
insertintocourse2values(2,'python');
insertintocourse2values(3,'kotlin');

案例:使用一個(gè)左連接,查找教java課程的所有信息。

explain
select*
fromteacher2t
leftouterjoincourse2c
ont.cid=c.cid
wherec.cname='java';

結(jié)果如下:

2c621c42-ebc9-11ed-90ce-dac502259ad0.png

① 優(yōu)化

對(duì)于兩張表,索引往哪里加?答:對(duì)于表連接,小表驅(qū)動(dòng)大表。索引建立在經(jīng)常使用的字段上。

為什么小表驅(qū)動(dòng)大表好一些呢?

小表:10
大表:300
#小表驅(qū)動(dòng)大表
select...where小表.x10=大表.x300;
for(inti=0;i<小表.length10;i++)
{???
????for(intj=0;j<大表.length300;j++)??
????{???????
????????...???
????}
}
#大表驅(qū)動(dòng)小表
select...where大表.x300=小表.x10;
for(inti=0;i<大表.length300;i++)
{??
????for(intj=0;j<小表.length10;j++)???
????{?????
????????...???
????}
}

分析: 以上2個(gè)FOR循環(huán),最終都會(huì)循環(huán)3000次;但是對(duì)于雙層循環(huán)來說:一般建議,將數(shù)據(jù)小的循環(huán),放外層。數(shù)據(jù)大的循環(huán),放內(nèi)層。不用管這是為什么,這是編程語言的一個(gè)原則,對(duì)于雙重循環(huán),外層循環(huán)少,內(nèi)存循環(huán)大,程序的性能越高。

結(jié)論:當(dāng)編寫【…on t.cid=c.cid】時(shí),將數(shù)據(jù)量小的表放左邊(假設(shè)此時(shí)t表數(shù)據(jù)量小,c表數(shù)據(jù)量大。)

我們已經(jīng)知道了,對(duì)于兩表連接,需要利用小表驅(qū)動(dòng)大表,例如【…on t.cid=c.cid】,t如果是小表(10條),c如果是大表(300條),那么t每循環(huán)1次,就需要循環(huán)300次,即t表的t.cid字段屬于,經(jīng)常使用的字段,因此需要給cid字段添加索引。

更深入的說明: 一般情況下,左連接給左表加索引。右連接給右表加索引。其他表需不需要加索引,我們逐步嘗試。

#給左表的字段加索引
createindexcid_teacher2onteacher2(cid);
#查看執(zhí)行計(jì)劃
explain
select*
fromteacher2t
leftouterjoincourse2c
ont.cid=c.cid
wherec.cname='java';

結(jié)果如下:

2c6b51a4-ebc9-11ed-90ce-dac502259ad0.png

當(dāng)然你可以下去接著優(yōu)化,給cname添加一個(gè)索引。索引優(yōu)化是一個(gè)逐步的過程,需要一點(diǎn)點(diǎn)嘗試。

#給cname的字段加索引
createindexcname_course2oncourse2(cname);
#查看執(zhí)行計(jì)劃
explain
selectt.cid,c.cname
fromteacher2t
leftouterjoincourse2c
ont.cid=c.cid
wherec.cname='java';

結(jié)果如下:

2c768c5e-ebc9-11ed-90ce-dac502259ad0.png

最后補(bǔ)充一個(gè):Using join buffer是extra中的一個(gè)選項(xiàng),表示Mysql引擎使用了“連接緩存”,即MySQL底層動(dòng)了你的SQL,你寫的太差了。

4)三表優(yōu)化

  • 大于等于張表,優(yōu)化原則一樣
  • 小表驅(qū)動(dòng)大表
  • 索引建立在經(jīng)常查詢的字段上

7、避免索引失效的一些原則

① 復(fù)合索引需要注意的點(diǎn)

  • 復(fù)合索引,不要跨列或無序使用(最佳左前綴)
  • 復(fù)合索引,盡量使用全索引匹配,也就是說,你建立幾個(gè)索引,就使用幾個(gè)索引

② 不要在索引上進(jìn)行任何操作(計(jì)算、函數(shù)、類型轉(zhuǎn)換),否則索引失效

explainselect*frombookwhereauthorid=1andtypeid=2;
explainselect*frombookwhereauthorid*2=1andtypeid=2;

結(jié)果如下:

2c7f4524-ebc9-11ed-90ce-dac502259ad0.png

③ 索引不能使用不等于(!= <>)或is null (is not null),否則自身以及右側(cè)所有全部失效(針對(duì)大多數(shù)情況)。復(fù)合索引中如果有>,則自身和右側(cè)索引全部失效。

#針對(duì)不是復(fù)合索引的情況
explainselect*frombookwhereauthorid!=1andtypeid=2;
explainselect*frombookwhereauthorid!=1andtypeid!=2;

結(jié)果如下:

2c8716f0-ebc9-11ed-90ce-dac502259ad0.png

再觀看下面這個(gè)案例:

#刪除單獨(dú)的索引
dropindexauthorid_indexonbook;
dropindextypeid_indexonbook;
#創(chuàng)建一個(gè)復(fù)合索引
altertablebookaddindexidx_book_at(authorid,typeid);
#查看執(zhí)行計(jì)劃
explainselect*frombookwhereauthorid>1andtypeid=2;
explainselect*frombookwhereauthorid=1andtypeid>2;

結(jié)果如下:

2c9080c8-ebc9-11ed-90ce-dac502259ad0.png

結(jié)論:復(fù)合索引中如果有【>】,則自身和右側(cè)索引全部失效。

在看看復(fù)合索引中有【<】的情況:

2c993e20-ebc9-11ed-90ce-dac502259ad0.png

我們學(xué)習(xí)索引優(yōu)化 ,是一個(gè)大部分情況適用的結(jié)論,但由于SQL優(yōu)化器等原因 該結(jié)論不是100%正確。一般而言, 范圍查詢(> < in),之后的索引失效。

④ SQL優(yōu)化,是一種概率層面的優(yōu)化。至于是否實(shí)際使用了我們的優(yōu)化,需要通過explain進(jìn)行推測(cè)。

#刪除復(fù)合索引
dropindexauthorid_typeid_bidonbook;
#為authorid和typeid,分別創(chuàng)建索引
createindexauthorid_indexonbook(authorid);
createindextypeid_indexonbook(typeid);
#查看執(zhí)行計(jì)劃
explainselect*frombookwhereauthorid=1andtypeid=2;

結(jié)果如下:

2ca54170-ebc9-11ed-90ce-dac502259ad0.png

結(jié)果分析: 我們創(chuàng)建了兩個(gè)索引,但是實(shí)際上只使用了一個(gè)索引。因?yàn)閷?duì)于兩個(gè)單獨(dú)的索引,程序覺得只用一個(gè)索引就夠了,不需要使用兩個(gè)。

當(dāng)我們創(chuàng)建一個(gè)復(fù)合索引,再次執(zhí)行上面的SQL:

#查看執(zhí)行計(jì)劃
explainselect*frombookwhereauthorid=1andtypeid=2;

結(jié)果如下:

2cae6b1a-ebc9-11ed-90ce-dac502259ad0.png

⑤ 索引覆蓋,百分之百?zèng)]問題

⑥ like盡量以“常量”開頭,不要以’%'開頭,否則索引失效

explainselect*fromteacherwheretnamelike"%x%";
explainselect*fromteacherwheretnamelike'x%';
explainselecttnamefromteacherwheretnamelike'%x%';

結(jié)果如下:

2cb422da-ebc9-11ed-90ce-dac502259ad0.png

結(jié)論如下: like盡量不要使用類似"%x%"情況,但是可以使用"x%"情況。如果非使用 "%x%"情況,需要使用索引覆蓋。

⑦ 盡量不要使用類型轉(zhuǎn)換(顯示、隱式),否則索引失效

explainselect*fromteacherwheretname='abc';
explainselect*fromteacherwheretname=123;

結(jié)果如下:

2cbcd07e-ebc9-11ed-90ce-dac502259ad0.png

⑧ 盡量不要使用or,否則索引失效

explainselect*fromteacherwheretname=''andtcid>1;
explainselect*fromteacherwheretname=''ortcid>1;

結(jié)果如下:

2cc50668-ebc9-11ed-90ce-dac502259ad0.png

注意:or很猛,會(huì)讓自身索引和左右兩側(cè)的索引都失效。

8、一些其他的優(yōu)化方法

1)exists和in的優(yōu)化

如果主查詢的數(shù)據(jù)集大,則使用i關(guān)鍵字,效率高。

如果子查詢的數(shù)據(jù)集大,則使用exist關(guān)鍵字,效率高。

select..fromtablewhereexist(子查詢);
select..fromtablewhere字段in(子查詢);

2)order by優(yōu)化

  • IO就是訪問硬盤文件的次數(shù)
  • using filesort 有兩種算法:雙路排序、單路排序(根據(jù)IO的次數(shù))
  • MySQL4.1之前默認(rèn)使用雙路排序;雙路:掃描2次磁盤(1:從磁盤讀取排序字段,對(duì)排序字段進(jìn)行排序(在buffer中進(jìn)行的排序)2:掃描其他字段)
  • MySQL4.1之后默認(rèn)使用單路排序:只讀取一次(全部字段),在buffer中進(jìn)行排序。但種單路排序會(huì)有一定的隱患(不一定真的是“單路/1次IO”,有可能多次IO)。原因:如果數(shù)據(jù)量特別大,則無法將所有字段的數(shù)據(jù)一次性讀取完畢,因此會(huì)進(jìn)行“分片讀取、多次讀取”。
  • 注意:?jiǎn)温放判?比雙路排序 會(huì)占用更多的buffer。
  • 單路排序在使用時(shí),如果數(shù)據(jù)大,可以考慮調(diào)大buffer的容量大?。?/li>
#不一定真的是“單路/1次IO”,有可能多次IO
setmax_length_for_sort_data=1024

如果max_length_for_sort_data值太低,則mysql會(huì)自動(dòng)從 單路->雙路(太低:需要排序的列的總大小超過了max_length_for_sort_data定義的字節(jié)數(shù))

① 提高order by查詢的策略:

  • 選擇使用單路、雙路 ;調(diào)整buffer的容量大小
  • 避免使用select * …(select后面寫所有字段,也比寫*效率高)
  • 復(fù)合索引,不要跨列使用 ,避免using filesort保證全部的排序字段,排序的一致性(都是升序或降序)

篇幅很長(zhǎng),內(nèi)容較多,建議收藏。

審核編輯 :李倩


聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 服務(wù)器
    +關(guān)注

    關(guān)注

    12

    文章

    8700

    瀏覽量

    84528
  • SQL
    SQL
    +關(guān)注

    關(guān)注

    1

    文章

    750

    瀏覽量

    43900
  • 數(shù)據(jù)庫
    +關(guān)注

    關(guān)注

    7

    文章

    3711

    瀏覽量

    64023

原文標(biāo)題:1.8w 字的 SQL 優(yōu)化大全

文章出處:【微信號(hào):DBDevs,微信公眾號(hào):數(shù)據(jù)分析與開發(fā)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    數(shù)據(jù)庫SQL優(yōu)化

    數(shù)據(jù)庫執(zhí)行SQL都會(huì)先進(jìn)行語義解析,然后將SQL分成一步一步可執(zhí)行的計(jì)劃,然后逐步執(zhí)行。通過分析執(zhí)行計(jì)劃,我們可以清晰的看到數(shù)據(jù)庫執(zhí)行的操作,這對(duì)于數(shù)據(jù)庫SQL優(yōu)化具有重大意義。 1
    的頭像 發(fā)表于 10-09 15:43 ?940次閱讀
    數(shù)據(jù)庫<b class='flag-5'>SQL</b>的<b class='flag-5'>優(yōu)化</b>

    MySQL的執(zhí)行過程 SQL語句性能優(yōu)化常用策略

    回顧 MySQL 的執(zhí)行過程,幫助介紹如何進(jìn)行 sql 優(yōu)化。
    的頭像 發(fā)表于 12-12 10:26 ?536次閱讀
    MySQL的執(zhí)行過程 <b class='flag-5'>SQL</b>語句性能<b class='flag-5'>優(yōu)化</b>常用策略

    xilinx ddr2硬核管腳問題

    我用xilinx spartan-6fpga 它硬核的管腳是固定的還是可配置的我在xilinx提供的文檔里找不到關(guān)于硬核管腳的分配求指導(dǎo)
    發(fā)表于 08-11 09:28

    關(guān)于FPGA的硬核知識(shí),求大神科普一下。。

    FPGA的硬核和軟核有什么區(qū)別呢,有沒有使用硬核的開發(fā)板,想學(xué)習(xí)關(guān)于FPGA硬核的知識(shí),各位大神有什么建議呢?真心求教
    發(fā)表于 03-05 11:51

    Spark SQL的工作原理和性能優(yōu)化

    Spark SQL(九):工作原理和性能優(yōu)化
    發(fā)表于 06-12 16:21

    30種SQL語句優(yōu)化總結(jié)

    必須掌握的30種SQL語句優(yōu)化
    發(fā)表于 04-21 11:38

    數(shù)據(jù)庫設(shè)計(jì)及開發(fā)規(guī)范之sql性能優(yōu)化

    數(shù)據(jù)庫設(shè)計(jì)及開發(fā)規(guī)范,sql性能優(yōu)化
    發(fā)表于 05-08 10:58

    SQL語句怎么優(yōu)化

    SQL語句優(yōu)化——結(jié)合書籍論壇小結(jié)
    發(fā)表于 06-14 14:46

    內(nèi)存條配置優(yōu)化SQL Server服務(wù)器性能

    內(nèi)存條配置優(yōu)化SQL Server服務(wù)器性能  Microsoft SQL Server 2000 的 內(nèi)存管理組件消除了對(duì) SQL Server 可用的內(nèi)存進(jìn)行手工管理的需要。
    發(fā)表于 01-11 11:00 ?864次閱讀

    SQL后悔藥,SQL性能優(yōu)化SQL規(guī)范優(yōu)雅

    每一個(gè)好習(xí)慣都是一筆財(cái)富,本文基于MySQL,分SQL后悔藥, SQL性能優(yōu)化,SQL規(guī)范優(yōu)雅三個(gè)方向,分享寫SQL的21個(gè)好習(xí)慣,謝謝閱讀
    的頭像 發(fā)表于 11-14 09:54 ?1738次閱讀

    30種SQL語句優(yōu)化方法

    SQL查詢中為了提高查詢效率,我們常常會(huì)采取一些措施對(duì)查詢語句進(jìn)行SQL優(yōu)化,下面總結(jié)一些方法,供大家參考。 01 對(duì)查詢進(jìn)行優(yōu)化,應(yīng)盡量避免全表掃描,首先應(yīng)考慮在 where 及
    的頭像 發(fā)表于 11-19 16:05 ?1917次閱讀

    SQL子查詢優(yōu)化是怎么回事

    子查詢 (Subquery)的優(yōu)化一直以來都是 SQL 查詢優(yōu)化中的難點(diǎn)之一。 關(guān)聯(lián)子查詢的基本執(zhí)行方式類似于 Nested-Loop,但是這種執(zhí)行方式的效率常常低到難以忍受。 當(dāng)數(shù)據(jù)量稍大時(shí),必須
    的頭像 發(fā)表于 02-01 13:55 ?1954次閱讀
    <b class='flag-5'>SQL</b>子查詢<b class='flag-5'>優(yōu)化</b>是怎么回事

    SQL優(yōu)化技巧分享

    一、查詢SQL盡量不要使用select *,而是具體字段
    的頭像 發(fā)表于 09-06 10:24 ?1295次閱讀

    一文終結(jié)SQL子查詢優(yōu)化

    子查詢(Subquery)的優(yōu)化一直以來都是 SQL 查詢優(yōu)化中的難點(diǎn)之一。關(guān)聯(lián)子查詢的基本執(zhí)行方式類似于 Nested-Loop,但是這種執(zhí)行方式的效率常常低到難以忍受。
    的頭像 發(fā)表于 04-28 14:19 ?631次閱讀
    一文終結(jié)<b class='flag-5'>SQL</b>子查詢<b class='flag-5'>優(yōu)化</b>

    Oracle長(zhǎng)耗時(shí)SQL優(yōu)化案例

    最近在生產(chǎn)客服平臺(tái),運(yùn)營(yíng)崗老師反饋,一個(gè)2w人的企業(yè),在信息詳情查詢時(shí),加載時(shí)間過長(zhǎng),越70s左右出結(jié)果,需要后臺(tái)優(yōu)化SQL。
    的頭像 發(fā)表于 05-19 15:02 ?928次閱讀