彙整自生產優化經驗與社區最佳實踐,涵蓋索引、查詢、架構、配置四大維度。每條附帶反例與正例,可直接作為 Code Review 清單。
-- 反例 ALTER TABLE users ADD INDEX idx_sex(sex); -- sex 只有 M/F -- 正例:組合索引包含高選擇性欄位 ALTER TABLE users ADD INDEX idx_sex_created(sex, created_at);
INDEX (a,b,c) 支援 a、a,b、a,b,c,不支援 b、c、b,c。SELECT a,b FROM t WHERE c=1 若有 INDEX (c,a,b),Extra 顯示 Using index,無需回主鍵查詢。INDEX (name(10)),長度取區分度 ≥ 0.9 時的最小值,節省空間。pt-duplicate-key-checker 或 sys.schema_unused_indexes 定期掃描。SELECT *:只查需要欄位,減少網路傳輸、利用覆蓋索引、減少緩存污染。-- 反例:索引失效 WHERE DATE(created_at) = '2023-01-01' WHERE YEAR(created_at) = 2023 -- 正例:範圍查詢走索引 WHERE created_at >= '2023-01-01' AND created_at < '2023-01-02'
!=、NOT IN、IS NULL:導致全表掃描。改用 NOT EXISTS、LEFT JOIN ... IS NULL、預設值代替 NULL。IN 改 EXISTS、JOIN:子查詢 IN 易產生臨時表,EXISTS 可半連接優化。UNION ALL 代替 UNION:無去重需求時,UNION ALL 避免排序去重開銷。LIMIT 優化:LIMIT 100000, 10 改為覆蓋索引子查詢 + 回表:
SELECT * FROM t WHERE id >= (SELECT id FROM t ORDER BY id LIMIT 100000,1) ORDER BY id LIMIT 10;
OR 拆分為 UNION ALL:WHERE a=1 OR b=2 無法用組合索引,拆分後各走各索引再合併。WHERE id > last_id ORDER BY id LIMIT 20,極大偏移也極快。innodb_buffer_pool_size = 物理記憶體 60~70% innodb_log_file_size = 1~4 GB innodb_flush_log_at_trx_commit = 1 (資料安全) 或 2 (效能優先) sync_binlog = 1 max_connections = 2000~5000
slow_query_log=1、long_query_time=1,配合 pt-query-digest 定期分析 Top SQL。避坑指南:
WHERE varchar_col = 123 → WHERE varchar_col = '123'COUNT(*) 在 InnoDB 無 WHERE 時需全表掃描,改用 SHOW TABLE STATUS 或維護計數表。FLUSH TABLES WITH READ LOCK 造成阻塞,改用 --single-transaction 熱備份。結語:優化無銀彈,但「建對索引、寫對 SQL、調對參數、分對架構」四步走能解決 90% 效能問題。建議團隊建立 SQL 審核機制,上線前必經 EXPLAIN 驗證。