關(guān)于Join
如上圖所示兜喻,left join和inner join的取值范圍還是有很明顯的不同的赡麦,看網(wǎng)上不少文章優(yōu)化left join也不交代上下文環(huán)境朴皆,直接就是教唆把left join換成inner join,只能說是章口就萊泛粹,文體兩開花...
關(guān)于笛卡爾積
簡言之就是強(qiáng)拼,不帶條件的硬拼2(多)張表晶姊,結(jié)果就是直接做乘法
SELECT * FROM a CROSS JOIN b;
SELECT * FROM a INNER JOIN b;
SELECT * FROM a,b;
SELECT * FROM a NATURE JOIN b;
SELECT * FROM a NATURA join b;
如何判斷驅(qū)動表
如果沒有where條件
左連接帽借,如果沒有where條件,則左表為驅(qū)動表
右連接脆荷,如果沒有where條件梦皮,右表為驅(qū)動表
如果有where條件
(參照《數(shù)據(jù)庫索引設(shè)計(jì)與優(yōu)化》第8章內(nèi)容對外表的判定應(yīng)該是下面寫的這個(gè)樣子)
(參考讀書筆記:索引讀書筆記)
但是實(shí)際操作中有本地謂詞的表并不一定百分百的是外表炭分,而是由優(yōu)化器來判斷的
#個(gè)人實(shí)際優(yōu)化過程中用的最多的還是下面這條
explain規(guī)則:explain語句結(jié)果中,第一行為驅(qū)動表(該定律適用于join剑肯;子查詢的話要分情況)
性能優(yōu)化相關(guān)
①選誰做驅(qū)動表
引用一個(gè)舉爛了的例子捧毛,在沒有過濾條件的情況下,外表有多少行就會被加載多少次让网;參考《索引設(shè)計(jì)與優(yōu)化》這本書的說法呀忧,每次加載相當(dāng)于一次隨機(jī)讀;
假設(shè)A表10000行溃睹,B表100行而账;也就是說如果用A表做驅(qū)動表會伴隨著10000次隨機(jī)讀,而如果是B表作為驅(qū)動表因篇,會伴隨著100次隨機(jī)讀泞辐。
所以普遍的優(yōu)化方案是小表作為外表(驅(qū)動表),用小表去驅(qū)動大表竞滓。
②創(chuàng)建合理的索引
選擇小表驅(qū)動大表咐吼,然后在內(nèi)表(被驅(qū)動的大表)的連接謂詞上建立索引,使得對內(nèi)表的查詢走索引提高效率虽界。這種做法最高效的就是外表是一張小表汽烦,而內(nèi)表的連接謂詞剛好是內(nèi)表的主鍵涛菠。
在對這種沒有查詢過濾條件的語句執(zhí)行explain的時(shí)候會非常不爽莉御,因?yàn)轵?qū)動表通常是走的全表掃描;在我的測試中俗冻,如果外表上沒有查詢過濾條件(也就是本地謂詞)的話礁叔,無論查詢項(xiàng)是否是索引項(xiàng),都是走全表迄薄。(換句話說琅关,也就是在沒有過濾條件的情況下,外表其實(shí)有沒有索引對于當(dāng)前語句的優(yōu)化意義都不太明顯(因?yàn)闀呷砺铮┘ケ危绻麑iT因?yàn)槟硞€(gè)語句去對外表建一大堆索引的可以省省了)
#用下面的例子來解釋上面2句話
m表和t表都是25W行左右涣易,大小差距不大;根據(jù)開篇說的驅(qū)動表確立規(guī)則冶伞,無查詢過濾條件的左連接新症,左表為驅(qū)動表;藍(lán)框表明了這一點(diǎn)响禽。
綠框的t.charge_no是t表的連接謂詞徒爹,在這一列上建有一個(gè)選擇性不錯(cuò)的索引荚醒,因此在explain的第二行可以看到內(nèi)表的查詢走了索引,type為ref隆嗅,效率還算不錯(cuò)界阁。(如果這個(gè)連接謂詞是t表的主鍵,效率會更高)
紅框處可以看到胖喳,對m表取的查詢值泡躯,我們僅僅取了m.charge_no這一列,而且m.charge_no上是建有索引的禀晓,但explain的第一行粉框的ALL表明m表的查詢?nèi)匀蛔吡巳恚词故俏覀僲表的查詢列只取了建有索引的列)精续。
#如何讓外表走索引?
方法:對外表增加過濾條件(本地謂詞)粹懒,并且在本地謂詞上創(chuàng)建索引
查詢過濾條件中只有t表的本地謂詞重付,因此無論左連接還是右連接,t表都是驅(qū)動表
由于過濾條件t.partner_id和t.trade_date上建有索引凫乖,因此外表t不再走全表掃描
注意2處藍(lán)框是有關(guān)聯(lián)的确垫,右下角的那個(gè)大藍(lán)框里面是沒有temporary的,這與接下來討論的有關(guān)
③合理的排序
前面2張圖里面的語句是我在項(xiàng)目中遇到的一個(gè)慢查詢帽芽,本體并非前面2張圖貼出的那樣删掀,區(qū)別在于order by后面跟的字段不同,原語句order by后面跟著的是一個(gè)內(nèi)表字段
多表join執(zhí)行時(shí)导街,優(yōu)化器內(nèi)部會先找出外表披泪,之后會對外表進(jìn)行排序;如果order by后面跟的是外表字段搬瑰,則排序會在這時(shí)完成款票。但如果order by后面的字段是內(nèi)表字段,則在所有結(jié)果集選出之后泽论,再進(jìn)行一次排序艾少,這也就是執(zhí)行計(jì)劃里面Using temporary的由來了。
項(xiàng)目中的原語句一來沒有本地謂詞翼悴,二來還用內(nèi)表進(jìn)行排序缚够,導(dǎo)致最后的執(zhí)行時(shí)間達(dá)到8s之久;與研發(fā)溝通之后將語句改成用外表字段排序鹦赎,執(zhí)行時(shí)間所短到0.3s谍椅。
④強(qiáng)行指定驅(qū)動表
左右連接都可以指定驅(qū)動表,但(inner) join無法強(qiáng)行指定古话,通常來說都是通過系統(tǒng)自動去判定
強(qiáng)行指定方式:straight_join
這個(gè)參數(shù)會強(qiáng)行指定左表是外表
例子就截個(gè)別人的圖吧
多表join優(yōu)化案例
eg.
紅框處是可能需要加索引的各種連接謂詞和本地謂詞雏吭,藍(lán)框標(biāo)示了執(zhí)行順序掃描方式等幫助信息
#對執(zhí)行計(jì)劃進(jìn)行分析
在執(zhí)行計(jì)劃的第5行可以發(fā)現(xiàn)pcb表走了全表掃描,掃描行數(shù)達(dá)到95W行煞额;通過執(zhí)行計(jì)劃的第4行可以發(fā)現(xiàn)這里有一個(gè)派生表思恐,這個(gè)派生表是由pcb和ppv做inner join形成的沾谜,這里很明顯pcb成了這個(gè)連接查詢的外表,查詢ppv表胀莹,發(fā)現(xiàn)ppv表僅僅只有19行基跑。這是典型的大表驅(qū)動小表
查詢pcb表的索引,發(fā)現(xiàn)pcb表的連接謂詞和本地謂詞上面都沒有建索引描焰!而ppv.id是ppv表的主鍵(mysql優(yōu)化器大概就是因?yàn)檫@個(gè)原因讓ppv表做了內(nèi)表吧...)
把pcb表的連接謂詞和本地謂詞做了一個(gè)聯(lián)合索引之后媳否,掃描行數(shù)從95W縮減至5.4W,優(yōu)化器很識趣的將ppv選做外表了
消滅了95W行的掃描之后荆秦,盤面上最刺眼的就是第二行那個(gè)2871了篱竭,對pc表的索引進(jìn)行查詢發(fā)現(xiàn)pc.client_no列上也沒有索引,由于pc.client_no列是一個(gè)本地謂詞步绸,在添加索引之后掺逼,pc表直接跳到了explain的第一行。優(yōu)化后的執(zhí)行計(jì)劃如下圖
對比pp表和pc表瓤介,pp表有16行數(shù)據(jù)吕喘,pc表有3000行數(shù)據(jù),都不算太大刑桑,無論誰作為驅(qū)動表都不會有太大的性能提升了氯质,優(yōu)化也到此結(jié)束;執(zhí)行時(shí)間由1s縮減到0.29s
參考文章
2.《索引設(shè)計(jì)與優(yōu)化》