干涉MySQL優(yōu)化器使用hash?join的方法
GreatSQL社區(qū)原創(chuàng)內(nèi)容未經(jīng)授權(quán)不得隨意使用,轉(zhuǎn)載請聯(lián)系小編并注明來源。GreatSQL是MySQL的國產(chǎn)分支版本,使用上與MySQL一致。
前言
數(shù)據(jù)庫的優(yōu)化器相當(dāng)于人類的大腦,大部分時候都能做出正確的決策,制定正確的執(zhí)行計劃,走出一條高效的路,但是它畢竟是基于某些固定的規(guī)則、算法來做的判斷,有時候并沒有我們?nèi)四X思維靈活,當(dāng)我們確定優(yōu)化器選擇執(zhí)行計劃錯誤時該怎么辦呢,語句上加hint,提示它選擇哪條路是一種常見的優(yōu)化方法。
我們知道Oracle提供了比較靈活的hint提示來指示優(yōu)化器在多表連接時選擇哪種表連接方式,比如use_nl,no_use_nl控制是否使用Nest Loop Join,use_hash,no_use_hash控制是否使用hash join。
但是MySQL長期以來只有一種表連接方式,那就是Nest Loop Join,直到MySQL8.0.18版本才出現(xiàn)了hash join, 所以MySQL在控制表連接方式上沒有提供那么多豐富的hint給我們使用,hash_join與no_hash_join的hint只是驚鴻一瞥,只在8.0.18版本存在,8.0.19及后面的版本又將這個hint給廢棄了,那如果我們想讓兩個表做hash join該怎么辦呢?
實驗
我們來以MySQL8.0.25的單機(jī)環(huán)境做一個實驗。建兩個表,分別插入10000行數(shù)據(jù),使用主鍵做這兩個表的關(guān)聯(lián)查詢。
create table t1(id int primary key,c1 int,c2 int); create table t2(id int primary key,c1 int,c2 int); delimiter // CREATE PROCEDURE p_test() BEGIN declare i int; set i=1; while i<10001 do insert into t1 values(i,i,i); insert into t2 values(i,i,i); SET i = i + 1; end while; END; // delimiter ;
查詢一下兩表使用主鍵字段關(guān)聯(lián)查詢時實際的執(zhí)行計劃,如下圖所示:

查詢一下兩表使用非索引字段關(guān)聯(lián)查詢時實際的執(zhí)行計劃,如下圖所示:

從執(zhí)行計劃可以看出,被驅(qū)動表的關(guān)聯(lián)字段上有索引,優(yōu)化器在選擇表連接方式時會傾向于選擇Nest Loop Join,當(dāng)沒有可用索引時傾向于選擇hash join。
基于這一點那我們可以使用no_index提示來禁止語句使用關(guān)聯(lián)字段的索引。

從上面的執(zhí)行計劃可以看出使用no_index提示后,優(yōu)化器選擇了使用hash join。
當(dāng)索引的選擇性不好時,優(yōu)化器選擇使用索引做Nest Loop Join是效率是很低的。
我們將實驗的兩個表中c1列的數(shù)據(jù)做一下更改,使其選擇性變差,并在c1列上建普通索引。
update t1 set c1=1 where id<5000; update t2 set c1=1 where id<5000; create index idx_t1 on t1(c1); create index idx_t2 on t2(c1);
當(dāng)我們執(zhí)行sql :
select t1.*,t2.* from t1 join t2 on t1.c1=t2.c1;
這個查詢結(jié)果會返回大量數(shù)據(jù),被驅(qū)動表的關(guān)聯(lián)字段c1列的索引選擇性差,此時選擇hash join是更明智的選擇,但是優(yōu)化器會選擇走Nest Loop Join。我們可以通過實驗驗證一下hash join 與 Nest Loop Join的性能差異。

可以看出使用hash join的耗時是使用Nest Loop Join的1/6,但是優(yōu)化器根據(jù)成本估算時,使用Nest Loop Join的成本要比使用hash join的成本低很多,所以會去選擇Nest Loop Join,這個時候就需要加上hint 提示禁止使用關(guān)聯(lián)字段的索引,被驅(qū)動表上每次都全表掃描的代價是很高的,這樣優(yōu)化器估算后就會選擇走h(yuǎn)ash join。
MySQL官方文檔里提到用BNL,NO_BNL的hint提示來影響hash join的優(yōu)化,但是經(jīng)過實驗證明,在表連接關(guān)聯(lián)字段上沒有可用索引時,優(yōu)化器估算成本后不會對被驅(qū)動表使用BNL全表掃描的方式做嵌套循環(huán)連接,而是會選擇使用hash join,那這樣NO_BNL在這個場景下就沒有用武之地了。
那么既然不用這個索引,把這個索引去掉不就可以了嗎?為什么非要使用no_index的hint提示呢,我們要知道業(yè)務(wù)使用的場景何其多,此處不用,別處使用了這個索引效率可能會有大的提升啊,這個時候就凸顯了hint的優(yōu)勢,只需要控制此語句的使用就好了。
總結(jié)
Nest Loop Join有其優(yōu)勢,它是response最快的連接方式,適用于返回數(shù)據(jù)量小的場景。當(dāng)兩個大表連接,返回大量數(shù)據(jù),且關(guān)聯(lián)字段的索引比較低效時,使用hash join就會比較高效,我們可以使用no_index的hint提示禁用關(guān)聯(lián)字段的低效索引,促使優(yōu)化器選擇hash join。
到此這篇關(guān)于MySQL優(yōu)化器使用hash join的的文章就介紹到這了,更多相關(guān)MySQL優(yōu)化器使用hash join內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
MySQL參數(shù)優(yōu)化信息參考(my.cnf參數(shù)優(yōu)化)
下面針對一些參數(shù)進(jìn)行說明,當(dāng)然還有其它的設(shè)置可以起作用,取決于你的負(fù)載或硬件:在慢內(nèi)存和快磁盤、高并發(fā)和寫密集型負(fù)載情況下,你將需要特殊的調(diào)整2024-07-07
Mysql分組查詢每組最新一條數(shù)據(jù)的三種實現(xiàn)方法
我們在開發(fā)中經(jīng)常會遇到分組查詢最新數(shù)據(jù)的問題,下面這篇文章主要給大家介紹了關(guān)于Mysql分組查詢每組最新一條數(shù)據(jù)的三種實現(xiàn)方法,文中通過實例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-01-01

