文件首頁
MySQL 8.4 參考手冊
相關文件 下載本手冊
PDF (US Ltr) - 39.9Mb
PDF (A4) - 40.0Mb
Man Pages (TGZ) - 258.5Kb
Man Pages (Zip) - 365.5Kb
Info (Gzip) - 4.0Mb
Info (Zip) - 4.0Mb


MySQL 8.4 參考手冊  /  ...  /  雜湊聯結最佳化

10.2.1.4 雜湊聯結最佳化

預設情況下,MySQL 會盡可能採用雜湊聯結。可以使用 BNLNO_BNL 最佳化工具提示之一,或將 block_nested_loop=onblock_nested_loop=off 設定為 optimizer_switch 伺服器系統變數設定的一部分,來控制是否採用雜湊聯結。

MySQL 對於每個聯結都有等值聯結條件,且沒有任何索引可應用於任何聯結條件的任何查詢,都採用雜湊聯結,例如這個查詢

SELECT *
    FROM t1
    JOIN t2
        ON t1.c1=t2.c1;

當有一個或多個索引可用於單一資料表述詞時,也可以使用雜湊聯結。

在剛才顯示的範例和本節中的其餘範例中,我們假設已使用以下陳述式建立了三個資料表 t1t2t3

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 會盡可能採用雜湊聯結。可以使用 BNLNO_BNL 最佳化工具提示之一,來控制是否採用雜湊聯結。

雜湊聯結的記憶體使用量可以使用 join_buffer_size 系統變數來控制;雜湊聯結無法使用超過此數量的記憶體。當雜湊聯結所需的記憶體超過可用數量時,MySQL 會使用磁碟上的檔案來處理。如果發生這種情況,您應該注意,如果雜湊聯結無法放入記憶體,且建立的檔案數超過為 open_files_limit 設定的數值,則聯結可能會失敗。為了避免此類問題,請進行以下任一變更:

  • 增加 join_buffer_size,使雜湊聯結不會溢出到磁碟。

  • 增加 open_files_limit

雜湊聯結的聯結緩衝區是逐步配置的;因此,您可以將 join_buffer_size 設定得更高,而不會讓小型查詢配置大量的 RAM,但外部聯結會配置整個緩衝區。雜湊聯結也用於外部聯結(包括反聯結和半聯結),因此這不再是問題。