業務系統頻繁出現執行 286~480 秒的慢 SQL,統計發現外層表 t1 掃描 90 萬列,根因是 MySQL 優化器將關聯子查詢的外層表「下推」到子查詢內部,導致大表全掃描。本文剖析優化器局限性與改寫方案。
SELECT * FROM t1
WHERE col1 IN (SELECT col2 FROM t2 WHERE t2.id = t1.id);
MySQL 會將 t1 逐列代入子查詢執行,等同於 Nested Loop Join,若 t1 為大表則災難性。
IN/EXISTS 關聯子查詢優化有限,無法自動轉為 Semi-Join 或 Materialization。EXPLAIN 會顯示 DEPENDENT SUBQUERY,rows 接近外層表全量。| 方案 | SQL 範例 | 原理 | 效果 |
|---|---|---|---|
| JOIN 改寫 | SELECT t1.* FROM t1 JOIN t2 ON t1.id=t2.id WHERE t1.col1=t2.col2; | 優化器可選擇 Hash Join / Index Join | 秒級完成 |
| 導出表 | SELECT * FROM t1 JOIN (SELECT DISTINCT col2,id FROM t2) dt ON t1.id=dt.id WHERE t1.col1=dt.col2; | 物化子查詢結果集 | 大幅降低掃描 |
| 增加索引 | ALTER TABLE t2 ADD INDEX idx_col2_id (col2,id); | 支援 Index Lookup | 輔助上述方案 |