`WHERE`、`JOIN`、`ORDER BY`、`GROUP BY` 出現的欄位,優先建立**複合索引**(最左前綴原則)。單表索引數建議 ≤ 5 個,權衡寫入開銷。
-- 原表(含大字段)
CREATE TABLE article (
id INT PRIMARY KEY,
title VARCHAR(200),
content LONGTEXT, -- 大字段
author_id INT,
created_at DATETIME
) ENGINE=InnoDB;
-- 拆分後
CREATE TABLE article (
id INT PRIMARY KEY,
title VARCHAR(200),
author_id INT,
created_at DATETIME
) ENGINE=InnoDB;
CREATE TABLE article_content (
article_id INT PRIMARY KEY,
content LONGTEXT
) ENGINE=InnoDB;
效益:主表行長大幅縮短,Buffer Pool 可緩存更多熱數據,查詢列表/統計不再讀取大字段頁面。
SELECT * FROM `table` PROCEDURE ANALYSE();
-- phpMyAdmin:結構 → Propose table structure
-- 輸出建議:VARCHAR(255) → VARCHAR(32)、INT → SMALLINT UNSIGNED 等
根據實際資料分佈自動推薦最小充足型別,定期執行可持續收窄定義。
-- 差
SELECT * FROM user WHERE id = 100;
-- 優
SELECT id, name, email FROM user WHERE id = 100 LIMIT 1;
減少網路傳輸、解析開銷、緩存污染;`LIMIT 1` 讓優化器停止掃描(主鍵查詢除外)。
-- 災難寫法:產生臨時表 + 排序 + 全表掃描
SELECT * FROM article ORDER BY RAND() LIMIT 10;
-- 替代方案:應用層取隨機 ID 或主鍵範圍隨機
SELECT * FROM article
WHERE id >= (SELECT FLOOR(RAND() * (SELECT MAX(id) FROM article)))
ORDER BY id LIMIT 10;
-- 差:單事務刪 100 萬行,鎖表、Undo Log 爆漲、主從延遲數小時
DELETE FROM log WHERE created_at < '2022-01-01';
-- 優:分批 5000 行,間隔睡眠
WHILE (SELECT COUNT(*) FROM log WHERE created_at < '2022-01-01') > 0 DO
DELETE FROM log WHERE created_at < '2022-01-01' LIMIT 5000;
DO SLEEP(0.1);
END WHILE;
「看到關聯 3~4 張表的巨型 SQL,只會覺得不靠譜。」
| 層面 | 行動 | 工具/指標 |
|---|---|---|
| 索引 | 定期 `pt-index-usage` 分析未用索引、`pt-duplicate-key-finder` 找重複索引 | Percona Toolkit |
| 慢查詢 | `long_query_time=0.1` + `log_queries_not_using_indexes=1`,定期 `pt-query-digest` | 慢查詢日誌 |
| 執行計畫 | 關鍵 SQL 必 `EXPLAIN`,關注 `type=ALL`、`Using temporary`、`Using filesort` | EXPLAIN FORMAT=JSON |
| 架構 | 讀寫分離、分庫分表、緩存旁路、異步寫入(MQ) | ProxySQL、ShardingSphere |
| 監控 | QPS、TPS、延遲 P99、連線數、InnoDB 緩存命中率、鎖等待 | Prometheus + Grafana |
SQL 優化不是背誦技巧,而是**建立「測量 → 分析 → 優化 → 驗證」閉環習慣**。上述 10 條建議覆蓋「索引、型別、結構、查詢寫法、事務、連線、架構」全鏈路。落地關鍵:將其內化為 Code Review 標準、CI 靜態掃描規則、新人入職必考知識點。