SELECT post.* FROM post INNER JOIN post_tag ON post.id = post_tag.post_id WHERE post.status = 1 AND post_tag.tag_id = 123 ORDER BY post.created DESC LIMIT 100;
EXPLAIN 顯示:驅動表為 post_tag(過濾後 71,220 行),需 Using filesort 排序 post.created,**導致慢查詢**。
SELECT post.* FROM post STRAIGHT_JOIN post_tag ON post.id = post_tag.post_id WHERE post.status = 1 AND post_tag.tag_id = 123 ORDER BY post.created DESC LIMIT 100;
EXPLAIN 顯示:驅動表變為 post(走 status_created 索引,119,340 行),**無 filesort**,效能顯著提升。
post_tag.tag_id=123 過濾性極好(7萬行)優於 post.status=1(12萬行),故優化器選 post_tag 驅動。ORDER BY post.created 排序列**不在驅動表**,MySQL 無法利用索引順序,只能臨時表 + filesort。STRAIGHT_JOIN **強制按書寫順序連接**(左表驅動右表),使 post 成為驅動表,排序列在驅動表上 → **可用索引順序直接輸出,省去排序**。ANALYZE TABLE/統計信息更新無效。| 風險 | 說明 |
|---|---|
| 數據分佈變化導致退化 | 強制順序固化,若駕駛表數據暴增,反而變慢 |
| 不可移植 | 非標準 SQL,遷移到 PostgreSQL/Oracle 需改寫 |
| 掩蓋根因 | 延遲解決統計信息不準、索引缺失等深層問題 |
優先嘗試:
ANALYZE TABLE post, post_tag; 更新統計信息。post(status, created) 復合索引支援 WHERE status=1 ORDER BY created。WITH filtered_posts AS (
SELECT id FROM post WHERE status=1 ORDER BY created DESC LIMIT 100
)
SELECT p.*
FROM filtered_posts fp
JOIN post p ON p.id = fp.id
JOIN post_tag pt ON pt.post_id = p.id
WHERE pt.tag_id = 123;