根據預設,MySQL 會盡可能採用雜湊聯結。可以使用 BNL
和 NO_BNL
最佳化工具提示之一來控制是否採用雜湊聯結,或透過設定 block_nested_loop=on
或 block_nested_loop=off
作為 optimizer_switch 伺服器系統變數設定的一部分。
對於任何每個聯結都有相等聯結條件,且沒有任何索引可套用至任何聯結條件的查詢,MySQL 都會採用雜湊聯結,例如這個查詢
SELECT *
FROM t1
JOIN t2
ON t1.c1=t2.c1;
當有一個或多個索引可用於單一資料表述詞時,也可以使用雜湊聯結。
在剛剛顯示的範例和本章節的其餘範例中,我們假設已使用下列陳述式建立三個資料表 t1
、t2
和 t3
CREATE TABLE t1 (c1 INT, c2 INT);
CREATE TABLE t2 (c1 INT, c2 INT);
CREATE TABLE t3 (c1 INT, c2 INT);
您可以使用 EXPLAIN
來查看是否正在採用雜湊聯結,如下所示
mysql> EXPLAIN
-> SELECT * FROM t1
-> JOIN t2 ON t1.c1=t2.c1\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: t1
partitions: NULL
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 1
filtered: 100.00
Extra: NULL
*************************** 2. row ***************************
id: 1
select_type: SIMPLE
table: t2
partitions: NULL
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 1
filtered: 100.00
Extra: Using where; Using join buffer (hash join)
EXPLAIN ANALYZE
也會顯示有關所使用雜湊聯結的資訊。
雜湊聯結也適用於涉及多個聯結的查詢,只要每對資料表至少有一個聯結條件是相等聯結,如這裡顯示的查詢
SELECT * FROM t1
JOIN t2 ON (t1.c1 = t2.c1 AND t1.c2 < t2.c2)
JOIN t3 ON (t2.c1 = t3.c1);
在剛剛顯示的案例中,其使用內部聯結,任何不是相等聯結的額外條件會在聯結執行後作為篩選器套用。(對於外部聯結,例如左聯結、半聯結和反聯結,它們會列印為聯結的一部分。)這可以在此處的 EXPLAIN
輸出中看到
mysql> EXPLAIN FORMAT=TREE
-> SELECT *
-> FROM t1
-> JOIN t2
-> ON (t1.c1 = t2.c1 AND t1.c2 < t2.c2)
-> JOIN t3
-> ON (t2.c1 = t3.c1)\G
*************************** 1. row ***************************
EXPLAIN: -> Inner hash join (t3.c1 = t1.c1) (cost=1.05 rows=1)
-> Table scan on t3 (cost=0.35 rows=1)
-> Hash
-> Filter: (t1.c2 < t2.c2) (cost=0.70 rows=1)
-> Inner hash join (t2.c1 = t1.c1) (cost=0.70 rows=1)
-> Table scan on t2 (cost=0.35 rows=1)
-> Hash
-> Table scan on t1 (cost=0.35 rows=1)
從剛剛顯示的輸出也可以看出,多個雜湊聯結可用於 (且會使用) 具有多個相等聯結條件的聯結。
即使任何一對已聯結的資料表沒有至少一個相等聯結條件,也會使用雜湊聯結,如下所示
mysql> EXPLAIN FORMAT=TREE
-> SELECT * FROM t1
-> JOIN t2 ON (t1.c1 = t2.c1)
-> JOIN t3 ON (t2.c1 < t3.c1)\G
*************************** 1. row ***************************
EXPLAIN: -> Filter: (t1.c1 < t3.c1) (cost=1.05 rows=1)
-> Inner hash join (no condition) (cost=1.05 rows=1)
-> Table scan on t3 (cost=0.35 rows=1)
-> Hash
-> Inner hash join (t2.c1 = t1.c1) (cost=0.70 rows=1)
-> Table scan on t2 (cost=0.35 rows=1)
-> Hash
-> Table scan on t1 (cost=0.35 rows=1)
(本節稍後會提供其他範例。)
雜湊聯結也適用於笛卡爾乘積,也就是說,當未指定任何聯結條件時,如下所示
mysql> EXPLAIN FORMAT=TREE
-> SELECT *
-> FROM t1
-> JOIN t2
-> WHERE t1.c2 > 50\G
*************************** 1. row ***************************
EXPLAIN: -> Inner hash join (cost=0.70 rows=1)
-> Table scan on t2 (cost=0.35 rows=1)
-> Hash
-> Filter: (t1.c2 > 50) (cost=0.35 rows=1)
-> Table scan on t1 (cost=0.35 rows=1)
並非一定要在聯結中包含至少一個等值聯結條件,才能使用雜湊聯結。這表示可以使用雜湊聯結進行最佳化的查詢類型包含以下清單中的類型(以及範例):
內部非等值聯結:
mysql> EXPLAIN FORMAT=TREE SELECT * FROM t1 JOIN t2 ON t1.c1 < t2.c1\G *************************** 1. row *************************** EXPLAIN: -> Filter: (t1.c1 < t2.c1) (cost=4.70 rows=12) -> Inner hash join (no condition) (cost=4.70 rows=12) -> Table scan on t2 (cost=0.08 rows=6) -> Hash -> Table scan on t1 (cost=0.85 rows=6)
半聯結:
mysql> EXPLAIN FORMAT=TREE SELECT * FROM t1 -> WHERE t1.c1 IN (SELECT t2.c2 FROM t2)\G *************************** 1. row *************************** EXPLAIN: -> Hash semijoin (t2.c2 = t1.c1) (cost=0.70 rows=1) -> Table scan on t1 (cost=0.35 rows=1) -> Hash -> Table scan on t2 (cost=0.35 rows=1)
反聯結:
mysql> EXPLAIN FORMAT=TREE SELECT * FROM t2 -> WHERE NOT EXISTS (SELECT * FROM t1 WHERE t1.c1 = t2.c1)\G *************************** 1. row *************************** EXPLAIN: -> Hash antijoin (t1.c1 = t2.c1) (cost=0.70 rows=1) -> Table scan on t2 (cost=0.35 rows=1) -> Hash -> Table scan on t1 (cost=0.35 rows=1) 1 row in set, 1 warning (0.00 sec) mysql> SHOW WARNINGS\G *************************** 1. row *************************** Level: Note Code: 1276 Message: Field or reference 't3.t2.c1' of SELECT #2 was resolved in SELECT #1
左外部聯結:
mysql> EXPLAIN FORMAT=TREE SELECT * FROM t1 LEFT JOIN t2 ON t1.c1 = t2.c1\G *************************** 1. row *************************** EXPLAIN: -> Left hash join (t2.c1 = t1.c1) (cost=0.70 rows=1) -> Table scan on t1 (cost=0.35 rows=1) -> Hash -> Table scan on t2 (cost=0.35 rows=1)
右外部聯結(請注意,MySQL 會將所有右外部聯結改寫為左外部聯結)
mysql> EXPLAIN FORMAT=TREE SELECT * FROM t1 RIGHT JOIN t2 ON t1.c1 = t2.c1\G *************************** 1. row *************************** EXPLAIN: -> Left hash join (t1.c1 = t2.c1) (cost=0.70 rows=1) -> Table scan on t2 (cost=0.35 rows=1) -> Hash -> Table scan on t1 (cost=0.35 rows=1)
預設情況下,MySQL 會盡可能使用雜湊聯結。可以使用 BNL
和 NO_BNL
最佳化提示來控制是否使用雜湊聯結。
雜湊聯結的記憶體使用量可以使用 join_buffer_size
系統變數來控制;雜湊聯結無法使用超過此數量的記憶體。當雜湊聯結所需的記憶體超過可用量時,MySQL 會使用磁碟上的檔案來處理。如果發生這種情況,您應該注意,如果雜湊聯結無法放入記憶體,並且建立的檔案數量超過 open_files_limit
的設定值,則聯結可能會失敗。為了避免這類問題,請進行以下任一變更
增加
join_buffer_size
,使雜湊聯結不會溢出到磁碟。增加
open_files_limit
。
雜湊聯結的聯結緩衝區會逐步配置;因此,您可以將 join_buffer_size
設定得更高,而小型查詢不會配置非常大量的 RAM,但是外部聯結會配置整個緩衝區。雜湊聯結也用於外部聯結(包括反聯結和半聯結),因此這不再是問題。