1.?Index Nested-Loop Join
概念解釋:
假設(shè)有t1,t2兩張表吃环,在join連接的時(shí)候计露,t1表驅(qū)動(dòng)t2表,t1走的全部掃描饲嗽,t2表使用了索引,
則這個(gè)時(shí)候join就使用了“index nested-loop join”算法奈嘿,簡(jiǎn)稱:NLJ貌虾。
NLJ執(zhí)行的流程如下:
2.?Block Nested-Loop Join
基于上面t1表和t2表,join連接裙犹,t1表和t2表都沒(méi)有命中索引尽狠,都是走的全部掃描。這個(gè)
時(shí)候Join使用的是“Block Nested-Loop Join”算法叶圃,簡(jiǎn)稱:BNL袄膏。
BNL算法的流程如下:
在explain分析語(yǔ)句的時(shí)候,extra附加信息中會(huì)出現(xiàn)“Using join buffer (Block Nested Loop)”掺冠。
join_buffer是由參數(shù)join_buffer_size決定的沉馆。默認(rèn)是256k。如果放不下表 t1
的所有數(shù)據(jù)話德崭,策略很簡(jiǎn)單斥黑,就是分段放。
流程大概是:
取t1表的數(shù)據(jù)眉厨,放入join_buffer中锌奴,如果join buffer滿了,就掃描t2表的數(shù)據(jù)缺猛,跟join buffer
的數(shù)據(jù)進(jìn)行對(duì)比缨叫,滿足join條件的作為結(jié)果集返回;清空join buffer荔燎,繼續(xù)上面的過(guò)程耻姥。
能否使用Join?
1.?如果可以使用 Index Nested-Loop Join 算法有咨,也就是說(shuō)可以用上被驅(qū)動(dòng)表上的索引琐簇,
其實(shí)是沒(méi)問(wèn)題的;
2.? 如果使用 Block Nested-Loop Join 算法,掃描行數(shù)就會(huì)過(guò)多婉商。尤其是在大表上的 join
操作似忧,這樣可能要掃描被驅(qū)動(dòng)表很多次,會(huì)占用大量的系統(tǒng)資源丈秩。所以這種 join 盡量不
要用盯捌。
如果要使用 join,應(yīng)該選擇大表做驅(qū)動(dòng)表還是選擇小表做驅(qū)動(dòng)表蘑秽?
1. 如果是 Index Nested-Loop Join 算法饺著,應(yīng)該選擇小表做驅(qū)動(dòng)表;
2.?如果是 Block Nested-Loop Join 算法:
? ??在 join_buffer_size 足夠大的時(shí)候肠牲,是一樣的幼衰;
? ??在 join_buffer_size 不夠大的時(shí)候(這種情況更常見(jiàn)),應(yīng)該選擇小表做驅(qū)動(dòng)表缀雳。
????這個(gè)問(wèn)題的結(jié)論就是渡嚣,總是應(yīng)該使用小表做驅(qū)動(dòng)表。
注意:join慢的時(shí)候肥印,盡量跳大join_buffer_size的值识椰。
優(yōu)化Join
1. 優(yōu)化NLJ算法
Multi-Range Read 優(yōu)化算法 (MRR)。這個(gè)優(yōu)化的主要目的是盡量使用順序讀盤竖独。
如果隨著輔助索引(二級(jí)索引) a 的值遞增順序查詢的話裤唠,主鍵索引id 的值就變成隨機(jī)的挤牛,
那么就會(huì)出現(xiàn)隨機(jī)訪問(wèn)莹痢,性能相對(duì)較差。雖然“按行查”這個(gè)機(jī)制不能改墓赴,但是調(diào)整查詢的順序竞膳,
還是能夠加速的。這就是 MRR 優(yōu)化的設(shè)計(jì)思路诫硕。
加入了MRR優(yōu)化的執(zhí)行流程如下:
1. 根據(jù)索引 a坦辟,定位到滿足條件的記錄,將 id 值放入 read_rnd_buffer 中 ;
2.?將 read_rnd_buffer 中的 id 進(jìn)行遞增排序章办;
3. 排序后的 id 數(shù)組锉走,依次到主鍵 id 索引中查記錄,并作為結(jié)果返回藕届。
read_rnd_buffer 的大小是由 read_rnd_buffer_size 參數(shù)控制的挪蹭。
啟用MRR算法:
set optimizer_switch="mrr_cost_based=off"
Batched Key Access (BKA) 算法
MySQL 在 5.6 引入的,是對(duì) NLJ 算法的優(yōu)化休偶。
NLJ 算法優(yōu)化后的 BKA 算法的流程如下:
啟用 BKA 算法:
set optimizer_switch='mrr=on,mrr_cost_based=off,batched_key_access=on';
前兩個(gè)參數(shù)的作用是要啟用 MRR梁厉。這么做的原因是,BKA 算法的優(yōu)化要依賴于MRR踏兜。
BNL 算法的優(yōu)化
優(yōu)化的常見(jiàn)做法是词顾,給被驅(qū)動(dòng)表的 join 字段加上索引八秃,把 BNL 算法轉(zhuǎn)成 BKA 算法。
但是如果無(wú)法在被驅(qū)動(dòng)表上加索引肉盹,那么:
考慮使用臨時(shí)表昔驱,使用臨時(shí)表的大致思路是:
1. 把表 t2 中滿足條件的數(shù)據(jù)放在臨時(shí)表 tmp_t 中;
2. 為了讓 join 使用 BKA 算法上忍,給臨時(shí)表 tmp_t 的字段 b 加上索引舍悯;
3. 讓表 t1 和 tmp_t 做 join 操作。
SQL語(yǔ)句為:
create temporary table temp_t(id int primary key, a int, b int, index(b))engine=innodb;
insert into temp_t select * from t2 where b>=1 and b<=2000;
select * from t1 join temp_t on (t1.b=temp_t.b);
總體來(lái)看睡雇,不論是在原表上加索引萌衬,還是用有索引的臨時(shí)表,我們的思路都是讓 join 語(yǔ)句
能夠用上被驅(qū)動(dòng)表上的索引它抱,來(lái)觸發(fā) BKA 算法秕豫,提升查詢性能。
上面如果不適用臨時(shí)表進(jìn)行優(yōu)化观蓄,那么還有其他方式進(jìn)行優(yōu)化混移?
我們可以自己實(shí)現(xiàn)在業(yè)務(wù)端。實(shí)現(xiàn)流程大致如下:
1. select * from t1;取得表 t1 的全部 1000 行數(shù)據(jù)侮穿,在業(yè)務(wù)端存入一個(gè) hash 結(jié)構(gòu)歌径,
比如 C++ 里的 set、PHP 的數(shù)組這樣的數(shù)據(jù)結(jié)構(gòu)亲茅。
2. select * from t2 where b>=1 and b<=2000; 獲取表 t2 中滿足條件的 2000 行
數(shù)據(jù)回铛。
3. 把這 2000 行數(shù)據(jù),一行一行地取到業(yè)務(wù)端克锣,到 hash 結(jié)構(gòu)的數(shù)據(jù)表中尋找匹配的數(shù)
據(jù)茵肃。滿足匹配的條件的這行數(shù)據(jù),就作為結(jié)果集的一行袭祟。
關(guān)于臨時(shí)表的幾個(gè)問(wèn)題:
1.? binlog_format=row验残,那么跟臨時(shí)表有關(guān)的語(yǔ)句,就不會(huì)記錄到binlog 里巾乳。只在?
binlog_format=statment/mixed 的時(shí)候您没,binlog 中才會(huì)記錄臨時(shí)表的操作。
2. 在使用臨時(shí)表的時(shí)候胆绊,最后最好要寫上 DROP TEMPORARY TABLE氨鹏,刪除臨時(shí)表
3. 臨時(shí)表只對(duì)本session會(huì)話可見(jiàn),對(duì)其他的session不可見(jiàn)辑舷。