某中學考試成績統計系統,四張核心表:
class (班級表):7 行 (101~303)student (學生表):~300 行course (課程表):~20 行score (成績表):~1000 行目標:演示從「能跑通」到「高性能」的 JOIN 優化全過程。
| 類型 | 關鍵字 | 語義 | 維恩圖 |
|---|---|---|---|
| 內連接 | INNER JOIN / JOIN | 兩表匹配行才返回 | 交集 |
| 左連接 | LEFT JOIN | 左表全量,右表匹配則帶上,否則 NULL | 左圓全保留 |
| 右連接 | RIGHT JOIN | 右表全量,左表匹配則帶上,否則 NULL | 右圓全保留 |
| 全連接 | FULL JOIN | 任一表有匹配即返回,MySQL 不支持,用 UNION 模擬 | 并集 |
MySQL 僅支持 **Nested Loop Join** (嵌套循環連接):**驅動表** (外層循環) 逐行讀取,攜帶關聯條件去 **被驅動表** (內層) 查找匹配行,合併結果。若有第三張表,以前兩表結果集為新驅動表繼續循環。
優化核心:縮小驅動表結果集 + 被驅動表關聯列加索引 = 將內層查找從全表掃描 (ALL) 降為索引查找 (ref/eq_ref)。
+----+-------------+-------+------+---------------+------+---------+------+------+--------------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+------+---------+------+------+--------------------------------+ | 1 | SIMPLE | cl | ALL | NULL | NULL | NULL | NULL | 7 | | | 1 | SIMPLE | st | ALL | NULL | NULL | NULL | NULL | 301 | Using where; Using join buffer | +----+-------------+-------+------+---------------+------+---------+------+------+--------------------------------+
class (7 行) 全表掃描可接受。student (301 行) 也全表掃描,Using join buffer 表明使用了 Join Buffer (Block Nested Loop),但仍需 301 次比較。+----+-------------+-------+------+-----------------+-----------------+---------+---------------------+------+-------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+-----------------+-----------------+---------+---------------------+------+-------+ | 1 | SIMPLE | cl | ALL | NULL | NULL | NULL | NULL | 7 | | | 1 | SIMPLE | st | ref | idx_school_id | idx_school_id | 4 | school.cl.master_id | 1 | | +----+-------------+-------+------+-----------------+-----------------+---------+---------------------+------+-------+
type 從 ALL → ref,rows 從 301 → 1,性能提升 **300 倍**。Extra 消失 Using join buffer,無需臨時緩衝區。+----+-------------+-------+--------+---------------------------------------+--------------------+---------+---------------------+------+-------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+--------+---------------------------------------+--------------------+---------+---------------------+------+-------+ | 1 | SIMPLE | st | ref | idx_school_id | idx_school_id | 4 | const | 1 | | | 1 | SIMPLE | sc | ref | idx_school_id_sc,idx_course_id_sc | idx_school_id_sc | 4 | const | 3 | | | 1 | SIMPLE | co | eq_ref | PRIMARY | PRIMARY | 4 | school.sc.course_id | 1 | | +----+-------------+-------+--------+---------------------------------------+--------------------+---------+---------------------+------+-------+
student 通過主鍵/唯一索引定位 1 行 (ref + const)。score 通過 idx_school_id_sc 定位 3 行 (ref)。course 通過主鍵 eq_ref 定位 1 行 (最優)。rows 列均為極小值。STRAIGHT_JOIN 強制)。ON a.id = b.id → b.id 加索引。複合索引遵循最左前綴原則。SELECT *,減少網絡傳輸、內存佔用、臨時表大小;覆蓋索引 (Using index) 可避免回表。join_buffer_size (默認 256K) 過小導致磁盤臨時表,適當調大 (如 2M) 但勿過大 (佔用連接內存 = size × max_connections)。optimizer_switch='hash_join=on' 控制。MySQL JOIN 優化本質是 **「索引消除內層全表掃描」**。掌握 EXPLAIN 讀懂 type、rows、Extra 三大核心列,配合「小表驅動大表、關聯列建索引、列裁剪」三板斧,即可解決 90% 的多表查詢性能問題。複雜場景再引入 CTE、Hash Join、物化視圖等進階手段。