MySql 查詢優(yōu)化器(Optimizer)解析
一、查詢優(yōu)化器(Optimizer)簡介
查詢優(yōu)化器是數(shù)據(jù)庫內(nèi)核中最復雜、最核心的模塊之一。它的作用是根據(jù)解析器和預處理器生成的語法樹,結合表的統(tǒng)計信息、索引、SQL語句結構等,生成一套最優(yōu)的執(zhí)行計劃,以盡可能高效地完成查詢。
查詢優(yōu)化器的主要目標是:用最少的資源、最快的速度獲取正確的結果。
二、查詢優(yōu)化器的主要任務
1. 邏輯優(yōu)化
作用
- 對SQL語句的結構做變換和簡化,減少不必要的計算。
過程
- 謂詞簡化:如將
WHERE age > 25 AND age > 20簡化為WHERE age > 25。 - 表達式重寫:如將
WHERE NOT (a = b)改寫為WHERE a <> b。 - 子查詢改寫:如將某些相關子查詢改寫為JOIN,提高效率。
- 等價變換:如分布律、交換律等,將SQL語句轉換為等價但更高效的形式。
2. 物理優(yōu)化
作用
- 決定具體的物理操作方式和順序,選擇最優(yōu)的執(zhí)行路徑。
過程
- 訪問路徑選擇:決定是否走索引,選擇哪個索引。
- 連接順序優(yōu)化:多表JOIN時,決定各表的訪問順序(如驅動表、被驅動表)。
- 連接算法選擇:如嵌套循環(huán)連接(Nested Loop Join)、Block Nested Loop Join等。
- 索引覆蓋:如果查詢字段都在索引中,則只掃描索引而不訪問數(shù)據(jù)頁。
- 謂詞下推:將過濾條件盡量提前到數(shù)據(jù)讀取階段,減少數(shù)據(jù)量。
- LIMIT優(yōu)化:如只掃描滿足LIMIT條件的最少數(shù)據(jù)。
3. 執(zhí)行計劃生成
作用
- 根據(jù)邏輯和物理優(yōu)化結果,生成一份詳細的執(zhí)行計劃(Execution Plan)。
過程
- 執(zhí)行計劃描述了每一步的操作方式、訪問對象、使用的索引、連接方式、過濾條件等。
- 可以用
EXPLAIN命令查看執(zhí)行計劃。
三、查詢優(yōu)化器的關鍵技術
1. 統(tǒng)計信息
- 優(yōu)化器依賴表的統(tǒng)計信息(如行數(shù)、索引分布、字段基數(shù)等)來估算不同執(zhí)行路徑的成本。
- 統(tǒng)計信息由存儲引擎維護,可通過
ANALYZE TABLE命令刷新。
2. 成本模型(Cost Model)
- 優(yōu)化器會為每種可能的執(zhí)行計劃估算“成本”,包括CPU、IO、內(nèi)存消耗等。
- 選擇成本最低的執(zhí)行計劃。
3. 索引選擇
- 優(yōu)化器會分析WHERE條件、ORDER BY、GROUP BY等子句,判斷哪些索引可以使用。
- 優(yōu)先選擇“選擇性高”的索引(即過濾能力強)。
4. 連接順序優(yōu)化
- 多表JOIN時,優(yōu)化器會嘗試多種連接順序,選擇成本最低的方案。
- 例如:A JOIN B JOIN C,可能嘗試(A→B→C)、(B→C→A)等不同順序。
5. 子查詢與視圖優(yōu)化
- 優(yōu)化器會嘗試將相關子查詢轉換為JOIN,或將視圖進行“內(nèi)聯(lián)”展開。
四、優(yōu)化器的執(zhí)行流程
- 接收語法樹
- 收集統(tǒng)計信息
- 枚舉所有可能的執(zhí)行計劃
- 計算每種計劃的成本
- 選擇成本最低的計劃
- 輸出最終執(zhí)行計劃
五、EXPLAIN命令與執(zhí)行計劃
通過 EXPLAIN 可以查看優(yōu)化器生成的執(zhí)行計劃。例如:
EXPLAIN SELECT name FROM users WHERE age > 25 ORDER BY name LIMIT 10;
輸出(示例):
| id | select_type | table | type | possible_keys | key | rows | Extra |
|---|---|---|---|---|---|---|---|
| 1 | SIMPLE | users | range | age_idx | age_idx | 500 | Using where; Using index; Using filesort |
含義:
type:訪問類型(如 ALL、index、range、ref、const、eq_ref)。key:使用的索引。rows:優(yōu)化器估算需掃描的行數(shù)。Extra:額外信息(如是否排序、是否用索引覆蓋)。
六、常見優(yōu)化器相關問題
- 索引未被使用:可能SQL寫法不合理,或統(tǒng)計信息不準確。
- JOIN順序不優(yōu):可以用 STRAIGHT_JOIN 強制順序。
- 子查詢未被優(yōu)化:可嘗試改寫為JOIN。
- 執(zhí)行計劃不理想:可通過分析EXPLAIN結果,調(diào)整SQL或表結構。
七、優(yōu)化器的局限與補充
- MySQL優(yōu)化器并非“全知全能”,有時因統(tǒng)計信息不準或算法限制,選擇的計劃并非最優(yōu)。
- 可以通過SQL重寫、添加/調(diào)整索引、更新統(tǒng)計信息來“引導”優(yōu)化器。
- MySQL支持部分“優(yōu)化器提示”(Optimizer Hint),如
USE INDEX、FORCE INDEX、STRAIGHT_JOIN等。
八、流程圖
解析樹
↓
收集統(tǒng)計信息
↓
枚舉執(zhí)行計劃
↓
計算成本
↓
選擇最優(yōu)計劃
↓
輸出執(zhí)行計劃
九. 優(yōu)化器的內(nèi)部算法與決策流程
1 枚舉所有可能的執(zhí)行計劃
優(yōu)化器會根據(jù) SQL 的結構,嘗試多種執(zhí)行路徑。例如三表 JOIN,理論上有 6 種連接順序,優(yōu)化器會枚舉這些順序,結合不同的索引選擇和連接算法,形成多種候選執(zhí)行計劃。
2 計算成本
每個執(zhí)行計劃都會被打分。成本模型會估算:
- IO成本:需要讀取多少數(shù)據(jù)頁
- CPU成本:需要進行多少計算、比較、排序
- 內(nèi)存成本:排序、臨時表等需要多少內(nèi)存
- 網(wǎng)絡成本:分布式場景下考慮網(wǎng)絡傳輸
優(yōu)化器會選出成本最低的執(zhí)行計劃。
3 選擇連接算法
常見的連接算法有:
- Nested Loop Join(嵌套循環(huán)連接):最常用,適合小表驅動大表或有索引的連接
- Block Nested Loop Join:批量處理一組數(shù)據(jù),減少IO
- Hash Join:MySQL暫不支持,但部分商業(yè)數(shù)據(jù)庫支持
- Sort-Merge Join:適合大表,先排序后合并
4 謂詞下推與索引覆蓋
謂詞下推(Predicate Pushdown)是指將 WHERE 條件盡量提前到最底層的數(shù)據(jù)訪問階段,減少無效數(shù)據(jù)傳遞。
索引覆蓋(Covering Index)指查詢所需字段全部在索引中,無需訪問數(shù)據(jù)頁,極大提升性能。
十. 常見優(yōu)化場景與實例
1 多表 JOIN 順序優(yōu)化
SELECT * FROM a JOIN b ON a.id = b.a_id JOIN c ON b.id = c.b_id WHERE a.status = 1;
- 優(yōu)化器會根據(jù) a 表的 status 過濾條件,優(yōu)先驅動 a 表(小表/過濾性強的表)。
- 可能的執(zhí)行計劃:
- a → b → c
- b → a → c
- c → b → a
- 優(yōu)化器會估算每種方案的成本,選最優(yōu)。
2 子查詢改寫為 JOIN
SELECT * FROM users WHERE id IN (SELECT user_id FROM orders WHERE amount > 100);
優(yōu)化器會嘗試將 IN 子查詢改寫為半連接或 JOIN,提高效率。
3 索引選擇
SELECT * FROM users WHERE age > 30 AND city = 'Beijing';
- 若 age 和 city 都有索引,優(yōu)化器會根據(jù)選擇性(過濾效果)決定用哪個索引。
- 也可能選擇聯(lián)合索引。
十一. 優(yōu)化器提示(Optimizer Hint)
有時優(yōu)化器選擇的執(zhí)行計劃并非我們期望的,可以用優(yōu)化器提示強制指定計劃:
- USE INDEX:建議優(yōu)化器使用某個索引
- FORCE INDEX:強制優(yōu)化器使用某個索引
- IGNORE INDEX:忽略某個索引
- STRAIGHT_JOIN:強制 JOIN 順序
示例:
SELECT * FROM users USE INDEX (idx_age) WHERE age > 30; SELECT * FROM a STRAIGHT_JOIN b ON a.id = b.a_id;
十二. EXPLAIN 結果詳細解讀
EXPLAIN 是診斷 SQL 性能的核心工具。常見字段說明如下:
| 字段 | 說明 |
|---|---|
| id | 查詢的標識符,復雜查詢會有多個 id |
| select_type | 查詢類型(SIMPLE、PRIMARY、SUBQUERY、DERIVED 等) |
| table | 當前訪問的表名 |
| type | 連接類型(ALL、index、range、ref、eq_ref、const、system) |
| possible_keys | 可能用到的索引 |
| key | 實際用到的索引 |
| key_len | 索引長度 |
| ref | 連接條件引用的字段 |
| rows | 估算掃描行數(shù) |
| filtered | 過濾后剩余行比例(MySQL 5.7+) |
| Extra | 額外信息(Using index, Using where, Using filesort, etc.) |
示例解讀:
- type=ALL:全表掃描,性能最差
- type=range:索引范圍掃描
- type=ref/eq_ref:索引等值查詢,性能較好
- Extra=Using index:索引覆蓋,無需回表
- Extra=Using filesort:需要額外排序,可能影響性能
十三. 與其他數(shù)據(jù)庫優(yōu)化器的對比
- MySQL:主要采用基于成本的優(yōu)化器,支持多種連接算法和索引優(yōu)化。
- Oracle/PostgreSQL:優(yōu)化器更復雜,支持 Hash Join、Merge Join、并行執(zhí)行等高級特性。
- SQL Server:優(yōu)化器也非常強大,支持更多執(zhí)行計劃緩存和并行優(yōu)化。
MySQL 優(yōu)化器更適合中小型業(yè)務,大型復雜查詢可能需要手動干預或SQL改寫。
十四. 執(zhí)行計劃的生成與結構
優(yōu)化器最終會輸出一份執(zhí)行計劃,描述 SQL 查詢的每一步操作。執(zhí)行計劃包括:
- 訪問哪些表,順序如何;
- 使用哪些索引;
- 采用什么連接算法;
- 何時做排序、分組、聚合;
- 是否需要臨時表、文件排序等。
執(zhí)行計劃的結構通常是一個樹狀結構,每個節(jié)點代表一個操作(如表掃描、索引查找、JOIN、排序等),節(jié)點之間有父子關系,表示數(shù)據(jù)流動的順序。
示例
假設有如下 SQL:
SELECT u.name, o.amount FROM users u JOIN orders o ON u.id = o.user_id WHERE u.age > 30 AND o.status = 'paid' ORDER BY o.amount DESC LIMIT 5;
優(yōu)化器會考慮:
- 先掃描 users 表,利用 age 索引過濾;
- 以 users 為驅動表,關聯(lián) orders 表(orders 的 user_id 有索引);
- 對結果按 amount 排序,取前 5 條。
最終執(zhí)行計劃大致為:
TableScan(users) → Filter(age > 30) → NestedLoopJoin(orders, user_id) → Filter(status='paid') → Sort(amount DESC) → Limit(5)
十五. EXPLAIN 實踐與分析
基本用法
EXPLAIN SELECT ...;
典型輸出解讀
| id | select_type | table | type | key | rows | Extra |
|---|---|---|---|---|---|---|
| 1 | SIMPLE | users | range | age_idx | 200 | Using where |
| 1 | SIMPLE | orders | ref | user_idx | 500 | Using where; Using index; Using filesort |
- type=range:users 表用 age 索引做范圍掃描,性能較好;
- type=ref:orders 表用 user_id 索引做等值連接;
- Using filesort:需要額外排序,可能影響性能;
- rows:優(yōu)化器估算的需掃描行數(shù),實際越少越好。
進階技巧
- 使用
EXPLAIN ANALYZE(MySQL 8.0.18+)可獲得真實執(zhí)行時間與行數(shù),幫助識別瓶頸。 - 結合
SHOW WARNINGS查看優(yōu)化器為何沒用索引。
十六. 優(yōu)化器的局限與手動優(yōu)化
局限性
- 統(tǒng)計信息不準確:如果表/索引統(tǒng)計信息過時,優(yōu)化器可能選錯執(zhí)行路徑。
- 索引選擇不理想:有時優(yōu)化器未選用最優(yōu)索引,需手動干預。
- 復雜 SQL 難以優(yōu)化:如大量嵌套子查詢、復雜表達式,優(yōu)化器可能“放棄”優(yōu)化。
- 文件排序/臨時表:ORDER BY、GROUP BY 涉及大數(shù)據(jù)量時,容易使用文件排序或臨時表,影響性能。
手動優(yōu)化手段
- 更新統(tǒng)計信息:
ANALYZE TABLE,讓優(yōu)化器有最新數(shù)據(jù)。 - 優(yōu)化器提示:用
USE INDEX、FORCE INDEX、STRAIGHT_JOIN強制優(yōu)化器按指定方式執(zhí)行。 - SQL改寫:將子查詢改為 JOIN,減少不必要的計算。
- 索引設計:為常用過濾條件、排序字段建立復合索引。
- 分解復雜查詢:將復雜 SQL 拆分為多步處理,減少優(yōu)化器壓力。
示例
SELECT /*+ USE_INDEX(users, age_idx) */ name FROM users WHERE age > 30;
十七. 優(yōu)化器與存儲引擎協(xié)作
- 優(yōu)化器負責策略,存儲引擎負責執(zhí)行。優(yōu)化器只決定“怎么做”,具體的數(shù)據(jù)訪問、索引遍歷、鎖管理等由存儲引擎(如 InnoDB)完成。
- 優(yōu)化器會向存儲引擎請求統(tǒng)計信息,決定索引選擇、訪問順序。
- 存儲引擎可以影響優(yōu)化器的能力(如 InnoDB 支持行鎖、MVCC,有些優(yōu)化策略才可用)。
十八. 性能調(diào)優(yōu)建議
- 定期用 EXPLAIN 檢查慢查詢;
- 保持統(tǒng)計信息新鮮;
- 合理設計索引,避免冗余、重復;
- 避免復雜嵌套查詢,能用 JOIN 就不用子查詢;
- 用 LIMIT 限制返回數(shù)據(jù)量,減少內(nèi)存壓力;
- 對大表的 ORDER BY、GROUP BY 盡量用索引覆蓋。
十九. 參考工具
- EXPLAIN / EXPLAIN ANALYZE:分析執(zhí)行計劃和實際消耗
- SHOW INDEX FROM 表名:查看索引情況
- SHOW PROFILE:分析 SQL 各階段消耗
- 慢查詢?nèi)罩?/strong>:定位性能瓶頸
二十、總結
查詢優(yōu)化器決定了SQL的執(zhí)行效率,是數(shù)據(jù)庫性能的核心。理解優(yōu)化器的工作原理,有助于編寫高效SQL、設計合理索引、診斷性能瓶頸。
- 查詢優(yōu)化器是 SQL 性能的核心,決定了查詢的執(zhí)行效率。
- 了解優(yōu)化器的決策邏輯,有助于編寫高效 SQL、設計合理索引。
- 善用 EXPLAIN 和優(yōu)化器提示,可以診斷和優(yōu)化慢查詢。
- 對于復雜業(yè)務,建議定期分析統(tǒng)計信息、合理設計表結構和索引。
到此這篇關于MySql 查詢優(yōu)化器(Optimizer)詳解的文章就介紹到這了,更多相關mysql查詢優(yōu)化器內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Mysql數(shù)據(jù)庫百萬級數(shù)據(jù)測試索引效果
這篇文章主要為大家介紹了Mysql數(shù)據(jù)庫百萬數(shù)據(jù)測試索引效果,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-06-06
MySQL優(yōu)化之如何了解SQL的執(zhí)行頻率
MySQL 客戶端連接成功后,通過 show [session|global]status 命令 可以提供服務器狀態(tài)信息,也可以在操作系統(tǒng)上使用 mysqladmin extended-status 命令獲得這些消息2014-05-05
Mysql如何在select查詢時追加(添加)一個字段并指定值
這篇文章主要介紹了Mysql如何在select查詢時追加(添加)一個字段并指定值,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-09-09
MySQL的match函數(shù)在sp中使用BUG解決分析
這篇文章主要為大家介紹了MySQL的match函數(shù)在sp中使用BUG解決分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-07-07

