MySQL 僅支持 Nested Loop Join,優化器有時會選錯驅動表導致連接次數爆炸。STRAIGHT_JOIN 可強制連接順序(類似 Oracle USE_NL + LEADING)。本文實戰案例:通過調整驅動表、添加覆蓋索引、配合 STRAIGHT_JOIN,將查詢從 72 秒優化至 1.43 秒。
SELECT DATE(practicetime) date_time, COUNT(DISTINCT a.userid) people_rows FROM test_log a, USER b WHERE a.userid = b.userid AND b.isfree = 0 AND LENGTH(b.username) > 4 GROUP BY DATE(practicetime);
執行 1 分 12 秒。
ALTER TABLE test_log DROP INDEX ix_test_log_userid, ADD INDEX ix_test_log_userid (userid, practicetime);
執行計劃變為 type: index、Extra: Using index,但耗時僅略微下降,仍 70 秒級。核心瓶頸在 Using filesort 排序 38 萬行數據。
SELECT DATE(practicetime) date_time, COUNT(DISTINCT a.userid) people_rows FROM USER b STRAIGHT_JOIN test_log a WHERE a.userid = b.userid AND b.isfree = 0 AND LENGTH(b.username) > 4 GROUP BY DATE(practicetime);
執行計劃:
table: b type: ALL rows: 42806 Extra: Using where; Using temporary; Using filesort table: a type: ref key: ix_test_log_userid rows: 38 Extra: Using index
驅動表行數 4.2 萬,被驅動表單次掃描僅 38 行,**總掃描行數從 38 萬降至 4.2 萬**,耗時 **2.56 秒**。
ALTER TABLE USER DROP INDEX ix_user_username, ADD INDEX ix_user_username (username, isfree);
執行計劃 type: index、Extra: Using index; Using temporary; Using filesort,耗時 **1.43 秒**。
| 階段 | 驅動表 | 關鍵索引 | 耗時 |
|---|---|---|---|
| 原始 | test_log (38萬) | 單列 userid | 72.62s |
| 覆蓋索引 | test_log | (userid, practicetime) | ~70s |
| STRAIGHT_JOIN | USER (4.2萬) | (userid, practicetime) | 2.56s |
| 驅動表覆蓋 | USER | (username, isfree) + (userid, practicetime) | 1.43s |