在 MySQL 中,可使用批次鍵存取 (BKA) 聯結演算法,該演算法會同時使用聯結表格的索引存取和聯結緩衝區。BKA 演算法支援內部聯結、外部聯結和半聯結操作,包括巢狀外部聯結。BKA 的優點包括由於更有效率的表格掃描而改善的聯結效能。此外,先前僅用於內部聯結的區塊巢狀迴圈 (BNL) 聯結演算法已擴充,並且可用於外部聯結和半聯結操作,包括巢狀外部聯結。
以下章節討論擴充原始 BNL 演算法的基礎聯結緩衝區管理、擴充的 BNL 演算法,以及 BKA 演算法。如需半聯結策略的相關資訊,請參閱使用半聯結轉換最佳化 IN 和 EXISTS 子查詢謂詞
MySQL 可以使用聯結緩衝區不僅執行內部聯結,而不需要存取內部表格的索引,還可以使用出現在子查詢扁平化後的外部聯結與半聯結。此外,當有索引存取內部表格時,可以有效地使用聯結緩衝區。
聯結緩衝區管理程式碼在儲存有趣的列欄位的值時,會稍微更有效率地利用聯結緩衝區空間:如果列欄位的值為 NULL
,則不會在緩衝區中配置額外位元組,並且會為VARCHAR
類型的任何值配置最少數量的位元組。
此程式碼支援兩種緩衝區類型,一般和遞增。假設聯結緩衝區 B1
用於聯結表格 t1
和 t2
,並且使用聯結緩衝區 B2
將此操作的結果與表格 t3
聯結。
一般聯結緩衝區包含每個聯結運算元的欄位。如果
B2
是一般聯結緩衝區,則放入B2
的每一列r
由B1
中列r1
的欄位和表格t3
中相符列r2
的有趣欄位組成。遞增聯結緩衝區僅包含第二個聯結運算元所產生之表格的列中的欄位。也就是說,它是相對於第一個運算元緩衝區中的列而言的遞增。如果
B2
是遞增聯結緩衝區,則它包含列r2
的有趣欄位,以及指向B1
中列r1
的連結。
遞增聯結緩衝區始終是相對於先前聯結操作的聯結緩衝區而言遞增,因此第一個聯結操作的緩衝區始終是一般緩衝區。在剛剛給出的範例中,用於聯結表格 t1
和 t2
的緩衝區 B1
必須是一般緩衝區。
用於聯結操作的遞增緩衝區的每一列僅包含要聯結的表格中列的有趣欄位。這些欄位會使用對第一個聯結運算元所產生之表格中相符列的有趣欄位的參考來擴增。遞增緩衝區中的數個列可以參考同一列 r
,只要這些列都與列 r
相符,其欄位就會儲存在先前的聯結緩衝區中。
增量緩衝區可以減少從先前聯結操作使用的緩衝區中複製欄位的頻率。由於在一般情況下,第一個聯結運算元產生的列可以與第二個聯結運算元產生的多個列匹配,因此可以節省緩衝區空間。沒有必要從第一個運算元複製多個列。由於複製時間的減少,增量緩衝區還可以節省處理時間。
block_nested_loop
旗標,位於 optimizer_switch
系統變數中,控制雜湊聯結。
batched_key_access
旗標控制最佳化工具如何使用批次金鑰存取聯結演算法。
預設情況下,block_nested_loop
為 on
,而 batched_key_access
為 off
。請參閱第 10.9.2 節,「可切換的最佳化」。也可以套用最佳化工具提示;請參閱區塊巢狀迴圈和批次金鑰存取演算法的最佳化工具提示。
有關半聯結策略的資訊,請參閱使用半聯結轉換最佳化 IN 和 EXISTS 子查詢述詞
MySQL BNL 演算法的原始實作已擴展為支援外部聯結和半聯結操作(並且後來被雜湊聯結演算法取代;請參閱第 10.2.1.4 節,「雜湊聯結最佳化」)。
當這些操作使用聯結緩衝區執行時,放入緩衝區的每一列都會提供一個比對旗標。
如果使用聯結緩衝區執行外部聯結操作,則會檢查第二個運算元產生的表格中的每一列,是否與聯結緩衝區中的每一列比對。當找到比對時,會形成一個新的擴展列(原始列加上第二個運算元的欄位),並將其傳送以由其餘的聯結操作進一步擴展。此外,會啟用緩衝區中比對列的比對旗標。在檢查完要聯結的表格的所有列之後,會掃描聯結緩衝區。緩衝區中每個未啟用比對旗標的列,都會以 NULL
補數(第二個運算元中每個欄位的 NULL
值)擴展,並傳送以由其餘的聯結操作進一步擴展。
block_nested_loop
旗標,位於 optimizer_switch
系統變數中,控制雜湊聯結。
如需更多資訊,請參閱第 10.9.2 節,「可切換的最佳化」。也可以套用最佳化工具提示;請參閱區塊巢狀迴圈和批次金鑰存取演算法的最佳化工具提示。
在 EXPLAIN
輸出中,當 Extra
值包含 Using join buffer (Block Nested Loop)
且 type
值為 ALL
、index
或 range
時,表示該表格使用了 BNL。
有關半聯結策略的資訊,請參閱使用半聯結轉換最佳化 IN 和 EXISTS 子查詢述詞
MySQL 實作一種稱為批次金鑰存取 (BKA) 聯結演算法的表格聯結方法。當對第二個聯結運算元產生的表格進行索引存取時,可以使用 BKA。與 BNL 聯結演算法類似,BKA 聯結演算法會使用聯結緩衝區來累積聯結操作的第一個運算元產生的列中的相關欄位。然後,BKA 演算法會建立金鑰,以存取要聯結的表格,其中包含緩衝區中所有列的金鑰,並將這些金鑰以批次方式提交至資料庫引擎以進行索引查閱。金鑰是透過多範圍讀取 (MRR) 介面提交至引擎(請參閱第 10.2.1.11 節,「多範圍讀取最佳化」)。提交金鑰後,MRR 引擎函式會以最佳方式在索引中執行查閱,擷取由這些金鑰找到的聯結表格的列,並開始以比對列饋送 BKA 聯結演算法。每個比對列都會與聯結緩衝區中的列的參考配對。
當使用 BKA 時,join_buffer_size
的值會定義每個向儲存引擎發出的請求中,金鑰批次的大小。緩衝區越大,對聯結操作右手邊表格進行的循序存取就越多,這可以顯著提高效能。
若要使用 BKA,optimizer_switch
系統變數的 batched_key_access
旗標必須設定為 on
。BKA 使用 MRR,因此 mrr
旗標也必須為 on
。目前,MRR 的成本估計過於悲觀。因此,mrr_cost_based
也必須為 off
,才能使用 BKA。以下設定啟用 BKA:
mysql> SET optimizer_switch='mrr=on,mrr_cost_based=off,batched_key_access=on';
MRR 函式透過兩種情境執行
第一種情境用於傳統的磁碟式儲存引擎,例如
InnoDB
和MyISAM
。對於這些引擎,通常會將聯結緩衝區中所有列的金鑰一次提交至 MRR 介面。引擎特定的 MRR 函式會針對提交的金鑰執行索引查閱,從中取得列 ID(或主索引鍵),然後依據 BKA 演算法的要求,逐一擷取所有這些選取之列 ID 的列。每個列都會與關聯參考傳回,這可讓您存取聯結緩衝區中的比對列。MRR 函式會以最佳方式擷取列:它們會以列 ID(主索引鍵)順序擷取。這可以提高效能,因為讀取是依磁碟順序而不是隨機順序進行的。第二種情境用於遠端儲存引擎,例如
NDB
。來自聯結緩衝區部分列的金鑰套件及其關聯會由 MySQL Server (SQL 節點) 傳送至 MySQL Cluster 資料節點。作為回報,SQL 節點會收到一個(或多個)比對列套件,並與對應的關聯配對。BKA 聯結演算法會使用這些列來建立新的聯結列。然後,會將一組新的金鑰傳送至資料節點,並使用從傳回的套件取得的列來建立新的聯結列。此程序會持續到聯結緩衝區中的最後一個金鑰傳送至資料節點,且 SQL 節點已接收並聯結所有符合這些金鑰的列。這可以提高效能,因為 SQL 節點傳送至資料節點的較少金鑰承載套件,表示在執行聯結操作時,與資料節點之間往返的次數較少。
在第一種情境中,聯結緩衝區的一部分保留用於儲存透過索引查閱選取並作為參數傳遞至 MRR 函式的列 ID(主索引鍵)。
沒有特殊的緩衝區來儲存為聯結緩衝區中的列建立的金鑰。相反地,會將為緩衝區中下一列建立金鑰的函式,作為參數傳遞至 MRR 函式。
在 EXPLAIN
輸出中,當 Extra
值包含 Using join buffer (Batched Key Access)
且 type
值為 ref
或 eq_ref
時,表示該表格使用了 BKA。
除了使用 optimizer_switch
系統變數來控制最佳化工具在整個工作階段中使用 BNL 和 BKA 演算法之外,MySQL 還支援最佳化工具提示,以影響每個陳述式最佳化工具。請參閱第 10.9.3 節,「最佳化工具提示」。
若要使用 BNL 或 BKA 提示來為外部聯結的任何內部表格啟用聯結緩衝,則必須為外部聯結的所有內部表格啟用聯結緩衝。