SQL 優化的本質只有一個:**改變執行計劃,讓優化器走「捷徑」**。核心目標雙支柱:**減少 IO 次數**(90%+ 瓶頸在這)與 **降低 CPU 計算**(排序、分組、去重)。本文拆解常見誤區、確立優化原則、給出可落地的十大鐵律。
唯一手段:通過索引設計、SQL 改寫、參數調優,引導優化器選擇最優執行計劃。
| 誤區 | 真相 | 正確認知 |
|---|---|---|
| count(1)/count(pk) 優於 count(*) | 錯 | MySQL 對 count(*) 有專門優化,某些場景 count(*) 更快 |
| count(col) = count(*) | 錯 | count(col) 統計非 NULL 行,count(*) 統計總行數,語義與執行計劃均不同 |
| 少 SELECT 幾列就能少讀數據 | 多數錯 | 行式存儲下整行讀取;唯有覆蓋索引 才能避免回表少讀數據 |
| ORDER BY 一定會排序 | 錯 | 若索引順序滿足排序需求,優化器直接按索引順序返回,省略排序 |
| Using filesort = 磁盤文件排序 | 錯 | 只要有排序操作就顯示,實際可能在內存完成(sort_buffer_size 控制) |
MySQL 優化器統計信息有限,複雜多表 JOIN 易誤判。單表查詢性能可媲美 Oracle,複雜 JOIN 則明顯弱勢。拆分為多條簡單 SQL,應用層組裝,利用多核並行、緩存命中率更高。
ORDER BY/GROUP BY/DISTINCTsort_buffer 壓力(MySQL 雙路/單路排序算法受選列影響大)MySQL 子查詢執行計劃長期存在缺陷(衍生表物化、無法下推條件),雖然 8.0+ 有改善但仍不穩定。手寫 JOIN 可控性更強。
WHERE a=1 OR b=2 導致優化器放棄索引掃描全表。拆分為 SELECT ... WHERE a=1 UNION ALL SELECT ... WHERE b=2,各自走索引,再合併結果。
UNION = UNION ALL + DISTINCT(排序去重)。確定無重複或不介意重複時,**必用 UNION ALL** 省去 CPU 密集型排序。
-- 錯誤:列上用函數/轉換 → 索引失效
WHERE DATE(create_time) = '2023-10-01'
WHERE CAST(user_id AS CHAR) = '12345'
-- 正確:參數側轉換,列保持原樣
WHERE create_time >= '2023-10-01' AND create_time < '2023-10-02'
WHERE user_id = 12345
隱式轉換同樣導致索引失效(如 VARCHAR 列比較數字參數)。
高併發 SQL 一旦退化,系統秒級崩潰;低頻大查詢最多讓系統慢一會兒,有緩衝時間。保護核心鏈路優先。
調整索引需評估所有相關 SQL。新索引加速查詢 A 可能拖慢寫入 B、導致查詢 C 選錯索引。建立 EXPLAIN 基線庫,變更前後對比。
EXPLAIN 確認:type ≥ ref、key 非 NULL、rows 合理、Extra 無 Using temporary/Using filesortExtra 含 Using indexkey_len 推斷聯合索引列使用情況pt-query-digest 找出 Top 10 高負載 SQLSQL 優化無銀彈,有的是:**讀懂 EXPLAIN → 定位瓶頸(IO/CPU) → 針對性調整索引/改寫 SQL → 全局回歸驗證**。將「十大鐵律」內化為編碼直覺,配合系統化 EXPLAIN 審查機制,才能在高併發、大數據量下穩住 MySQL 性能底線。