前言
MySQL 性能優化不僅是「加索引、調參數」,更始於**表結構設計**、貫穿於 **SQL 編寫規範**、落地於**架構演進**。本文提煉八大核心策略,覆蓋字段屬性、連接查詢、聯合查詢、事務控制、鎖優化、外鍵權衡、索引設計、SQL 改寫,構建從開發到架構的完整優化知識鏈。
策略一:選取最適用的字段屬性 —— 空間即性能
核心原則
- 越小越好:數據類型僅夠用即可。
INT (4B) vs BIGINT (8B),CHAR(6) 郵編 vs VARCHAR(255)。
- 優先數值類型:數值比較/排序/Join 比字符串快得多。
ENUM 存「省份/性別/狀態」內部存數字。
- NOT NULL 默認:NULL 需額外 1 字節標記、索引統計複雜、
COUNT(col) 不計 NULL、NOT IN 子查詢陷阱。除非業務明確需要「未知」語義,否則 NOT NULL + DEFAULT。
- 避免 TEXT/BLOB:強制磁盤臨時表、無法內存排序、備份慢。拆分到附表 (垂直分表) 或對象存儲 (OSS/S3) 存引用。
- 時間類型選擇:
DATETIME (8B, 1000-9999年) vs TIMESTAMP (4B, 1970-2038, 時區感知)。5.6+ DATETIME(3) 支持毫秒僅 5-8B。推薦 DATETIME 存業務時間,TIMESTAMP 存元數據 (創建/更新時間)。
策略二:使用連接 (JOIN) 替代子查詢 —— 避免臨時表開銷
對比
-- 子查詢 (MySQL 5.6- 優化器弱,易產生臨時表)
SELECT * FROM customer WHERE id NOT IN (SELECT customer_id FROM orders);
-- LEFT JOIN + IS NULL (經典反模式優化)
SELECT c.* FROM customer c
LEFT JOIN orders o ON c.id = o.customer_id
WHERE o.customer_id IS NULL;
-- NOT EXISTS (5.6+ 優化器優化良好,語義清晰)
SELECT * FROM customer c
WHERE NOT EXISTS (SELECT 1 FROM orders o WHERE o.customer_id = c.id);
原理
子查詢常物化為臨時表 (Derived table),無索引、全表掃描。JOIN 可直接利用驅動表索引、被驅動表索引 (ref/eq_ref 訪問),優化器可選擇最優 Join Order。**原則:能用 JOIN/NOT EXISTS 解決的,不用 IN/NOT IN 子查詢**。
策略三:使用聯合 (UNION) 替代手動臨時表 —— 簡化邏輯、自動清理
-- 手動臨時表 (舊式)
CREATE TEMPORARY TABLE tmp AS SELECT name, phone FROM client;
INSERT INTO tmp SELECT name, birth FROM author;
INSERT INTO tmp SELECT name, supplier FROM product;
SELECT * FROM tmp;
-- UNION ALL (無去重,性能高) / UNION (去重)
SELECT name, phone FROM client
UNION ALL
SELECT name, birth FROM author
UNION ALL
SELECT name, supplier FROM product;
- UNION ALL:直接合併結果集,**無排序去重開銷**,性能極高。**業務允許重複或確知無重複時首選**。
- UNION:隱含
DISTINCT,需排序去重,消耗 CPU/內存/臨時表。
- 所有 SELECT 列數、類型、順序必須一致。
策略四:事務 (Transaction) —— 一致性基石與鎖控制
ACID 與隔離級別
| 隔離級別 | 髒讀 | 不可重複讀 | 幻讀 | InnoDB 默認 |
| READ UNCOMMITTED | Y | Y | Y | |
| READ COMMITTED (RC) | N | Y | Y | |
| REPEATABLE READ (RR) | N | N (MVCC) | N (Next-Key Lock) | ✅ 默認 |
| SERIALIZABLE | N | N | N | |
最佳實踐
- 業務邊界明確:一個業務操作 = 一個事務 (轉賬、下單扣庫存)。
- 事務盡量短:長事務持有鎖久、Undo Log 激增、主從延遲大、死鎖概率高。**業務邏輯 (RPC/HTTP) 移出事務**。
- 死鎖處理:InnoDB 自動檢測回滾代價最小事務。應用層捕獲
Deadlock found (1213) 重試 (指數退避)。
- 只讀事務優化:
SET TRANSACTION READ ONLY 或 START TRANSACTION READ ONLY (5.6+),避免分配事務 ID、減少鎖開銷。
策略五:鎖定表 (LOCK TABLES) —— 特定場景的輕量級替代
-- 針對單表計數器更新,避免事務開銷
LOCK TABLES counter WRITE;
SELECT value FROM counter WHERE id=1 FOR UPDATE; -- 事務寫法
-- 或鎖表寫法
SELECT value FROM counter WHERE id=1;
UPDATE counter SET value=value+1 WHERE id=1;
UNLOCK TABLES;
適用場景與風險
- ✅ **適用**:MyISAM 表 (無事務支持)、批量導入數據 (鎖表禁用索引加速)、特定單表高併發計數器 (性能優於事務)。
- ❌ **禁用**:InnoDB 核心業務 (破壞 MVCC/行鎖優勢)、跨表操作 (死鎖風險極高)、長時間持有 (阻塞所有讀寫)。
- ⚠️ **注意**:
LOCK TABLES 隱含提交當前事務,會釋放已持有的行鎖。
策略六:使用外鍵 (Foreign Key) —— 數據完整性 vs 性能的權衡
CREATE TABLE orders (
id INT PRIMARY KEY,
customer_id INT NOT NULL,
FOREIGN KEY (customer_id) REFERENCES customers(id)
ON DELETE CASCADE -- 級聯刪除
ON UPDATE CASCADE -- 級聯更新
) ENGINE=InnoDB;
深度權衡
| 優點 | 缺點 (生產環境常見痛點) |
| 數據庫層強制完整性,防臟數據 | 鎖開銷大:子表插入/修改需鎖父表記錄 (共享鎖);父表刪除/修改需鎖子表 (排他鎖),高併發極易死鎖/鎖等待 |
| 文檔化關係,ER 圖自動生成 | 級聯操作不可控:ON DELETE CASCADE 可能觸發連鎖刪除海量數據、長事務、主從延遲、Binlog 膨脹 |
| DDL 困難:修改父表主鍵/外鍵列需先刪除 FK,大表操作極慢 |
| 分庫分表不兼容:跨實例無法建 FK |
建議
核心交易鏈路 (訂單、支付、庫存) 禁用外鍵,應用層保證完整性 (補償事務/TCC/最終一致性)**。僅在**低併發、強一致性要求極高、不分表**的輔助表 (字典、配置、審計) 考慮使用。
策略七:使用索引 (Index) —— 查詢加速的核心引擎
索引類型選擇
- B+ Tree (默認):範圍查詢、排序、分組、全鍵/前綴匹配。InnoDB 聚簇索引 (主鍵) + 二級索引 (存主鍵值回表)。
- Hash (Memory/NDB):精確查找 O(1),不支持範圍/排序。
- Fulltext:全文搜索。5.6+ InnoDB 支持,但中文分詞需插件 (ngram) 或外部引擎。
- Spatial (R-Tree):GIS 地理空間查詢。
索引設計鐵律
- 最左前綴:
INDEX (a,b,c) 支持 a, a,b, a,b,c。
- 覆蓋索引:
SELECT a,b FROM t WHERE c=1 → INDEX (c,a,b),Extra: Using index,**避免回表**,性能提升數倍。
- 高區分度優先:
COUNT(DISTINCT col)/COUNT(*) > 0.1 適合建索引。性別/狀態/類型 (低基數) **單列索引無效**,需組合索引前綴。
- 避免冗餘:
PRIMARY KEY (id) + INDEX (id) 多餘;INDEX (a) + INDEX (a,b) 前者多餘。
- 主鍵選擇:自增整數 (BIGINT UNSIGNED) 最佳。UUID/雪花算法導致聚簇索引分裂、二級索引膨脹 (存主鍵值大)、寫入性能下降 3-5 倍。**業務唯一鍵用 Unique Index**,主鍵純內部自增。
索引失效常見模式 (必背)
WHERE YEAR(create_time) = 2024 -- 函數/運算在索引列上
WHERE phone + 0 = 13800000000 -- 隱式類型轉換 (字符串列比數字)
WHERE name LIKE '%keyword%' -- 前導通配符
WHERE status = 1 OR type = 2 -- OR 條件列無組合索引
SELECT * FROM t WHERE id > 100 -- 選取列過多導致優化器判定回表成本高而選全表掃描 (覆蓋索引可解)
-- 解決:移項常數、建覆蓋索引、改寫 SQL、強制索引 FORCE INDEX (慎用)
策略八:優化的查詢語句 —— SQL 改寫黃金法則
| 反模式 | 優化模式 | 核心原理 |
SELECT * | 指定需要的列 | 減少網絡/IO/內存,利用覆蓋索引 |
WHERE YEAR(dt)=2024 | WHERE dt >= '2024-01-01' AND dt < '2025-01-01' | 函數導致索引失效 → 範圍查詢走索引 |
WHERE amount/7 > 100 | WHERE amount > 700 | 列運算失效索引 → 常數移項 |
LIKE '%kw%' | 全文索引 / ES / 前綴 LIKE 'kw%' | 前導通配符不可用 B+Tree |
NOT IN (子查詢) | NOT EXISTS / LEFT JOIN ... IS NULL | 子查詢優化差、NULL 陷阱 |
ORDER BY RAND() LIMIT 10 | 應用層隨機 ID / 覆蓋索引掃描取隨機 | 全表排序極慢 |
隱式轉換 WHERE varchar_col = 123 | WHERE varchar_col = '123' | 類型轉換導致索引失效 |
| 大事務 / 大批量 DELETE/UPDATE | 分批處理 (每次 500-1000 行) + 間隔 Sleep | 減少鎖持有時間、Undo Log、Binlog、主從延遲 |
SELECT ... LIMIT 1000000, 10 深分頁 | 延遲關聯 / 游標 / 記錄上次最大 ID | OFFSET 大需掃描大量行丟棄 |
總結:優化閉環
- 設計期:字段類型、NOT NULL、主鍵策略、預估索引 (策略 1, 7)。
- 開發期:SQL 規範 (策略 2, 3, 8)、事務邊界 (策略 4)、避免外鍵/鎖表 (策略 5, 6)。
- 上線前:
EXPLAIN 核心 SQL、慢查詢基線、負載測試。
- 運行期:慢查詢監控 → pt-query-digest → 針對性加索引/改 SQL/調參數 → 驗證 → 固化規範。
**優化無終點,規範有傳承。** 將這八大策略內化為團隊開發規範、Code Review 標準、架構評審清單,才是最高 ROI 的優化投資。
- 上一篇文章: MySQL Root 密碼丟失/忘記重置全方案:Windows/Linux 多版本通用實戰手冊
- 下一篇文章: MySQL my.ini 參數優化三大經典模板解析
Copyright ©
程式碼之家 All Rights Reserved