問題:CREATE TABLE ... AS SELECT 建表不會自動複製索引
-- 錯誤做法
CREATE TABLE tmp_stats AS SELECT * FROM big_table WHERE ...;
-- 正確做法:建表後立即加索引
CREATE TABLE tmp_stats AS SELECT * FROM big_table WHERE ...;
ALTER TABLE tmp_stats ADD INDEX idx_join_key (join_key);
單表 1000 萬行,無索引全表掃描 ~30 秒 → 加索引後 毫秒級
-- 原始:直接查大表
SELECT ... FROM huge_table ht JOIN other o ON ht.id = o.id WHERE ht.status = 1;
-- 優化:先提取小集合
CREATE TEMPORARY TABLE tmp_ht AS
SELECT id, col1, col2 FROM huge_table WHERE status = 1; -- 從 1000萬 縮減至 50萬
-- 後續所有查詢針對 tmp_ht
SELECT ... FROM tmp_ht ht JOIN other o ON ht.id = o.id;
MySQL 優化器會自動重排,但複雜查詢時手動調整有幫助:
EXPLAIN 觀察 rows 估算行數變化-- 低效:相關子查詢逐行執行
SELECT * FROM orders o WHERE o.user_id IN (SELECT id FROM users WHERE vip=1);
-- 高效:JOIN 或半連接
SELECT o.* FROM orders o JOIN users u ON o.user_id = u.id WHERE u.vip = 1;
-- 或 MySQL 8.0+ 半連接優化自動處理
DISTINCT / GROUP BY 在大結果集上極耗資源| 優化點 | 工具/命令 | 預期收益 |
|---|---|---|
| 補充缺失索引 | EXPLAIN、pt-index-usage | ★★★★★ (數量級) |
| 拆分大查詢、建臨時表 | CREATE TEMPORARY TABLE | ★★★★☆ |
| 子查詢轉 JOIN | EXPLAIN select_type=DEPENDENT SUBQUERY | ★★★★☆ |
| 避免 SELECT * | 代碼審查 | ★★★☆☆ (減少 I/O、網路) |
| 分頁優化 (深分頁) | 延遲關聯/游標分頁 | ★★★★☆ |
| 調整 JOIN 順序 | STRAIGHT_JOIN、小表驅動大表 | ★★★☆☆ |
| 使用覆蓋索引 | EXPLAIN Extra=Using index | ★★★★☆ |
type (all→ref→eq_ref)、rows、Extrapt-query-digest 分析慢查詢日誌ANALYZE TABLE 或啟用 innodb_stats_persistent=ON「索引是銀彈,但不是萬能藥;縮小掃描範圍才是優化的本質。」
千萬級數據下,先加對索引,再縮小集合,最後才是調整 SQL 寫法。本案例 12 倍提升中,索引貢獻 ~70%,臨時表貢獻 ~20%,SQL 重寫 ~10%。