MySQL 5.7 僅支援 Nested-Loop Join(NLJ),不支援 Hash Join。理解 NLJ 及其變體對 SQL 優化至關重要。本文解析 Simple NLJ、Block NLJ、Index NLJ 三種演算法原理、偽代碼與優化策略。
FOR each row IN t1 MATCHING range:
FOR each row IN t2 MATCHING ref_key:
FOR each row IN t3:
IF row 滿足 JOIN 條件:
SEND TO client
對應 EXPLAIN 的 type:range、ref、ALL。最內層 t3 全表掃描,效能極差。
STRAIGHT_JOIN 強制指定連接順序FOR each row IN t1 MATCHING range:
FOR each row IN t2 MATCHING ref_key:
STORE used columns FROM t1, t2 IN join_buffer
IF buffer FULL:
FOR each row IN t3:
FOR each t1,t2 combination IN join_buffer:
IF 滿足條件: SEND TO client
EMPTY buffer
IF buffer NOT EMPTY:
FOR each row IN t3:
FOR each t1,t2 combination IN join_buffer:
IF 滿足條件: SEND TO client
join_buffer(默認 256KB,可調 join_buffer_size)掃描次數 = (S × C) / join_buffer_size + 1join_buffer_size(如 4MB~8MB),但避免 OOMSELECT a,b 而非 SELECT *),減少緩存佔用ref 而非 ALL當 JOIN 條件列有索引,MySQL 會嘗試使用索引查找而非全表掃描,效能大幅提升。若無索引,絕不會 退回到 Simple NLJ(太慢),而是強制使用 BNLJ。
| 優化手段 | 影響算法 | 說明 |
|---|---|---|
| 小表驅動大表 | 所有 NLJ | 減少外層循環次數 |
| JOIN 列建索引 | INLJ | 將 ALL/ref 轉為 eq_ref/ref |
| 調大 join_buffer_size | BNLJ | 減少內層掃描輪數 |
| 只 SELECT 需要列 | BNLJ | 緩存更多行組合 |
| STRAIGHT_JOIN 指定順序 | 所有 | 修正優化器誤判 |
| 升級 MySQL 8.0 | Hash Join | 獲得真正的 Hash Join 支援 |
EXPLAIN 中 type=ALL 且 Extra=Using join buffer 表示 BNLJ