SQL 語句寫法不同,性能可能相差數量級。本文針對 MySQL 總結五大高頻優化策略:**數據類型精準選擇、字符集隱式轉換消除、COUNT 聚合優化、子查詢轉聯結、UNION 去重開銷規避**,並給出驗證方法與避坑清單。
IS NULL/IS NOT NULL 無法命中範圍掃描優化NOT NULL DEFAULT 默認值(0、''、'1970-01-01'、'0000-00-00' 等業務無效值)is_xxx_known BOOLEAN NOT NULL DEFAULT 1 標記| 業務範圍 | 推薦類型 | 字節 | 對比冗餘類型 |
|---|---|---|---|
| 布爾/狀態碼 0-255 | TINYINT UNSIGNED | 1 | INT (4B) |
| ID/計數器 < 6.5萬 | SMALLINT UNSIGNED | 2 | INT (4B) |
| ID/計數器 < 1.6千萬 | MEDIUMINT UNSIGNED | 3 | INT (4B) |
| 大自增 ID | BIGINT UNSIGNED | 8 | - |
| 定長代碼/標識 (≤255) | CHAR(n) / VARCHAR(n) | n / 實際+1-2 | TEXT (額外指針) |
| 金額/精確小數 | DECIMAL(p,s) | ~p/2 | FLOAT/DOUBLE (精度丟失) |
| 時間戳 (秒級) | TIMESTAMP (4B) / DATETIME (8B, 5.6+) | 4/8 | BIGINT 毫秒 (8B) |
**效益**:百萬行表,每行省 10B → 省 10MB 磁盤 + 緩存,IOPS、備份、複製全鏈路受益。
character_set_client、連接 connection、結果 results、表/列 charset 不一致 → 隱式 CONVERT(),**導致索引失效、全表掃描、CPU 飆升**utf8mb4(含 Emoji),collate utf8mb4_general_ci 或 0900_ai_ciSHOW VARIABLES LIKE 'character_set%';、SHOW CREATE TABLE 檢查列級字符集characterEncoding=utf8mb4、PHP mysqli_set_charset、Python charset='utf8mb4'| 場景 | MyISAM | InnoDB | 優化手段 |
|---|---|---|---|
| 無 WHERE | O(1) 直接讀元數據 | 掃描最小二級索引 (或主鍵) | MyISAM 無需優化;InnoDB 維護 count_cache 表 / Redis 計數器 |
| WHERE 命中索引 | 索引範圍掃描計數 | 索引範圍掃描計數 | 確保覆蓋索引,EXPLAIN 顯示 Using index |
| WHERE 未命中索引 | 全表掃描 | 全表掃描 | 加索引、分區表、物化視圖預聚合 |
| COUNT(col) 含 NULL | 同 COUNT(*) | 同 COUNT(*) | 列定義 NOT NULL 則等價 |
**大表聚合通用方案**:
1. 觸發器/應用邏輯維護計數表(強一致)
2. 定時任務刷新統計表(最終一致,低開銷)
3. SELECT TABLE_ROWS FROM information_schema.tables 近似值(僅參考)
IN/EXISTS 子查詢常被執行為 **Dependent Subquery**(外層每行內層執行一次),而非 Semi-Join-- IN 子查詢 → INNER JOIN (去重)
SELECT * FROM t1 WHERE id IN (SELECT id FROM t2);
→
SELECT DISTINCT t1.* FROM t1 JOIN t2 ON t1.id = t2.id;
-- NOT IN 子查詢 → LEFT JOIN ... IS NULL
SELECT * FROM t1 WHERE id NOT IN (SELECT id FROM t2);
→
SELECT t1.* FROM t1 LEFT JOIN t2 ON t1.id = t2.id WHERE t2.id IS NULL;
-- 相關標量子查詢 → LEFT JOIN
SELECT t1.*, (SELECT name FROM t2 WHERE t2.id = t1.t2_id) AS t2_name FROM t1;
→
SELECT t1.*, t2.name AS t2_name FROM t1 LEFT JOIN t2 ON t1.t2_id = t2.id;
否則聯結退化為 Block Nested Loop,性能更差。
MySQL 不支持 DELETE t1 FROM t1 JOIN t2 ...(MariaDB/新版本已支持)。舊版本方案:
1. 應用層兩步:SELECT id ... → DELETE FROM t1 WHERE id IN (...)
2. CONCAT() 動態生成批量 DELETE 腳本管道執行
UNION = UNION ALL + 全局排序去重(臨時表 + filesort),大數據集極慢UNION ALL 直接拼接結果,**無排序、無去重**,性能優 10-100 倍CREATE TEMPORARY TABLE tmp AS SELECT ... UNION ALL ...; ALTER TABLE tmp ADD UNIQUE KEY(...); SELECT * FROM tmp; 利用索引去重避免 filesortEXPLAIN / EXPLAIN ANALYZE (8.0.18+) 檢查執行計劃:type、key、rows、ExtraSHOW PROFILE / performance_schema.events_statements_history_long 定位耗時階段long_query_time=0 全量採集,pt-query-digest 分析 Top SQLMySQL 查詢優化核心:**小類型、統一字符集、COUNT 計數表、子查詢轉聯結、UNION ALL 代替 UNION**。每條 SQL 上線前必過 EXPLAIN、慢查詢日志必分析、大表變更必壓測。優化無終點,唯有持續度量與迭代。