MySQL梳理三:查詢與優化

MySQL查詢優化完整指南:從理論到實踐

本文從MySQL查詢的基礎機制出發,深入探討單表查詢訪問方法、聯表查詢策略、成本計算原理、基于規則的優化技術,最后通過實際案例展示慢SQL的診斷和優化過程。

目錄

  • 一、單表查詢的訪問方法
  • 二、聯表查詢機制
  • 三、查詢成本計算
  • 四、基于規則的查詢優化
  • 五、執行計劃分析
  • 六、慢SQL治理實戰案例

一、單表查詢的訪問方法

MySQL 的訪問方法定義了查詢語句的執行方式,類似于從起點到終點的路線選擇。查詢優化器根據條件、索引和統計信息選擇最優訪問方法,以最小化執行成本(I/O、CPU、內存等)。雖然查詢結果相同,但不同訪問方法的效率差異顯著。

1.1 訪問方法性能對比

不同訪問方法的性能差異如下圖所示,從左到右效率遞減:

🚀 const
坐火箭
主鍵/唯一索引等值
🚄 ref
坐高鐵
普通索引等值
🚌 ref_or_null
坐大巴
ref + NULL查詢
🚗 range
開汽車
索引范圍查詢
🚲 index_merge
騎自行車
多索引合并
🛵 index
騎摩托
索引全掃描
🐢 ALL
坐烏龜
全表掃描

1.2 具體訪問方法詳解

1.2.1 const - 最高效訪問
  • 描述: 通過主鍵或唯一二級索引與常數等值比較,定位單條記錄
  • 性能: 效率最高(“坐火箭”),成本為常數級別,無需掃描或回表
  • 適用場景: 主鍵或唯一索引的等值查詢,聯合索引需所有列等值匹配
  • 注意: 唯一索引查詢 NULL 值(如 key IS NULL)無法使用 const,因 NULL 可重復
  • 示例: SELECT * FROM table WHERE id = 100; 使用主鍵,定位單條記錄
1.2.2 ref - 普通索引等值查詢
  • 描述: 使用普通二級索引進行等值查詢,可能匹配多條記錄,需回表獲取完整記錄
  • 性能: 效率次于 const(“坐高鐵”),適合選擇性高的索引,成本隨匹配記錄數增加
  • 適用場景: 普通二級索引或聯合索引最左連續列等值匹配,包括 NULL 值查詢
  • 示例: SELECT * FROM table WHERE key1 = 'abc'; 使用普通索引 idx_key1
1.2.3 ref_or_null - 擴展NULL查詢
  • 描述: 擴展 ref,額外查找索引列為 NULL 的記錄
  • 性能: 效率略低于 ref,因需處理 NULL 檢查
  • 適用場景: 等值查詢結合 NULL 檢查,如 WHERE key1 = 'abc' OR key1 IS NULL
  • 注意: 若無 NULL 值,可能降級為 ref
1.2.4 range - 范圍查詢
  • 描述: 使用索引(聚簇或二級索引)進行范圍查詢,條件對應數軸上的區間(單點或連續區間)
  • 性能: 效率依賴范圍大小,選擇性高時接近 ref,需回表
  • 適用場景: 支持 >, <, IN, BETWEEN, LIKE(前綴匹配)等操作符
  • 示例: SELECT * FROM table WHERE key1 IN ('a', 'b'); 生成單點區間
1.2.5 index_merge - 多索引合并
  • 描述: 使用多個二級索引合并結果,分為三種算法:

    • Intersection: 取索引結果交集,適用于等值匹配或主鍵范圍匹配,需按主鍵排序
    • Union: 取索引結果并集,適用于 OR 連接的等值匹配
    • Sort-Union: 范圍查詢結果按主鍵排序后取并集,成本略高
  • 性能: 效率低于單索引查詢(range/ref),因涉及多索引操作和回表,但優于全表掃描

  • 適用場景: 復雜條件涉及多個索引,如 WHERE key1 = 'a' OR key2 = 'b'

  • 注意: 優化器根據成本選擇是否使用,聯合索引可替代以降低成本

1.2.6 index - 索引全掃描
  • 描述: 直接掃描二級索引全部葉子節點,查詢列和條件均包含在索引中,免回表
  • 性能: 優于全表掃描,因索引記錄小,但仍需全索引掃描
  • 適用場景: 查詢列和條件全在索引中,如 SELECT key1 FROM table WHERE key1 = 'a';
  • 注意: 需確保查詢不涉及非索引列
1.2.7 all - 全表掃描
  • 描述: 掃描整個聚簇索引,逐行檢查條件
  • 性能: 效率最低(“坐烏龜”),掃描全表記錄,I/O 成本高
  • 適用場景: 無索引可用或條件選擇性低
  • 示例: SELECT * FROM table WHERE non_indexed_col = 1;

1.3 重要注意事項

二級索引與回表

通常使用單個二級索引,先定位記錄,再回表獲取完整數據。非索引條件在回表后過濾。

范圍區間確定
  • AND 連接取交集,OR 連接取并集
  • 無法使用索引的條件替換為 TRUE,簡化區間計算
  • 復雜條件需逐一分析,提取有效區間
聯合索引優化

多列查詢可通過聯合索引替代 index_merge,但需平衡其他查詢對單列索引的需求。

優化器決策

優化器基于統計信息(ANALYZE TABLE 更新)選擇最低成本訪問方法,通過合理設計索引和查詢,MySQL 優化器能選擇高效訪問方法,顯著提升查詢性能。


二、聯表查詢機制

2.1 聯表查詢的本質

聯表查詢將多個表的記錄組合成笛卡爾積,通過過濾條件生成結果集。笛卡爾積是各表記錄逐一匹配形成的組合,記錄數為各表行數的乘積(如兩表各 100 行,生成 100×100=10000 行)。MySQL 語法簡單,在 FROM 子句列出表名即可,但需通過過濾條件(涉及表間比較,如 table1.col1 = table2.col2)控制結果集規模。

2.2 聯表查詢的分類

2.2.1 內連接(INNER JOIN)
  • 僅保留符合連接條件和過濾條件的記錄組合,驅動表與被驅動表可互換
  • 示例: SELECT * FROM table1 INNER JOIN table2 ON table1.col1 = table2.col2; 僅返回匹配記錄(默認的聯表查詢)
2.2.2 外連接(OUTER JOIN)
  • 驅動表記錄即使無匹配也保留,被驅動表字段補 NULL
  • 左外連接: 左表為驅動表,SELECT * FROM table1 LEFT JOIN table2 ON ... [WHERE ...]
  • 右外連接: 右表為驅動表,SELECT * FROM table1 RIGHT JOIN table2 ON ...

2.3 聯表查詢執行過程

聯表查詢由優化器驅動,執行過程如下圖所示:

開始聯表查詢
選擇驅動表
(根據成本估算)
執行驅動表查詢
(使用單表訪問方法)
獲得驅動表結果集
(N條記錄)
對每條驅動表記錄
查詢被驅動表
(應用連接條件)
應用WHERE條件過濾
將匹配結果加入結果集
還有更多
驅動表記錄?
返回最終結果集
優化策略
為被驅動表添加索引
使用join buffer緩存
選擇小表做驅動表

查詢驅動表:

  • 選擇第一個表(驅動表),使用單表訪問方法(如 const, ref, all)執行查詢,獲取符合單表條件的記錄
  • 示例: SELECT * FROM table1, table2 WHERE table1.col1 > 10 AND table1.col1 = table2.col2; 先查詢 table1 滿足 col1 > 10 的記錄。

查詢被驅動表:

  • 對驅動表結果集的每條記錄,查詢被驅動表,應用兩表條件(如 table1.col1 = table2.col2)和被驅動表單表條件
  • 被驅動表訪問次數等于驅動表結果集記錄數

2.4 連接算法與優化

2.4.1 嵌套循環連接(Nested Loop Join)
  • 驅動表查詢一次,被驅動表查詢多次(次數等于驅動表結果集記錄數)
  • 多表連接時,上一輪結果集作為新驅動表,重復過程
  • 特點: 簡單但效率低,被驅動表可能多次全表掃描
  • 性能: 成本隨驅動表記錄數和被驅動表訪問方式增加
2.4.2 索引優化策略
  • 被驅動表查詢可利用索引加速,參考常見訪問方法
  • 建議: 為被驅動表的關鍵列(如連接條件中的列)添加索引,確保選擇性高,查詢列表僅包含必要列以觸發 index
2.4.3 基于塊的嵌套循環連接(Block Nested Loop Join)
  • 被驅動表數據量大時,多次全表掃描導致高 I/O 成本
  • 采用 join buffer(默認 256KB,可通過 join_buffer_size 調整)緩存驅動表結果集記錄
  • 具體過程
    1. 將驅動表查詢列和條件放入 join buffer
    2. 掃描被驅動表,記錄一次性與 join buffer 中多條記錄匹配
    3. 減少被驅動表 I/O,最佳情況為 join buffer 容納所有驅動表記錄,僅需掃描被驅動表一次

三、查詢成本計算

3.1 MySQL 查詢成本構成

MySQL 查詢的執行成本主要分為I/O成本和CPU成本:

3.1.1 I/O 成本

從磁盤加載數據或索引頁面到內存的耗時,以頁面(page)為基本單位,默認頁面大小為 16KB,成本常數默認為 1.0,出現在涉及表數據或索引的加載這樣的場景中,例如全表掃描或索引范圍查詢。

3.1.2 CPU 成本

讀取記錄、檢測搜索條件、排序等操作的耗時,讀取并檢測一條記錄是否符合條件的默認成本為 0.2(無論是否需要條件檢測),主要包括條件比較、結果集排序等。

3.1.3 成本常數

1.0(頁面讀取)和 0.2(記錄檢測)是默認值,硬編碼在 MySQL 源碼中,部分微調值(如 1.10.01)用于調整成本估算。

3.2 單表查詢成本計算流程

MySQL 查詢優化器在執行單表查詢前會評估所有可能執行方案的成本,選擇成本最低的方案作為執行計劃

開始單表查詢成本計算
分析搜索條件
找出可能使用的索引
計算全表掃描成本
計算各索引訪問成本
考慮index_merge可能性
比較所有方案成本
選擇成本最低方案
生成執行計劃
成本構成
I/O成本 = 頁面數 × 1.0
CPU成本 = 記錄數 × 0.2
影響因素
表統計信息
索引選擇性
條件過濾率
數據分布

具體步驟包括:

  • 根據搜索條件,找出可能使用的索引
  • 計算全表掃描的成本
  • 計算使用索引的成本
  • 考慮索引合并
  • 選擇成本最低的執行方案

3.3 聯表查詢成本計算

總成本公式

總成本 = 單次訪問驅動表的成本 + 驅動表扇出 × 單次訪問被驅動表的成本

其中:

  • 驅動表扇出: 驅動表查詢結果的記錄數
  • 當無法直接確定扇出時,優化器通過啟發式規則(heuristic)估算剩余條件的過濾效果
3.3.1 聯表查詢成本評估示例

以內連接方式舉例:

SELECT * FROM s1 INNER JOIN s2 ON s1.key1 = s2.common_field 
WHERE s1.key2 > 10 AND s1.key2 < 1000 AND s2.key2 > 1000 AND s2.key2 < 2000;

MySQL 優化器評估兩種連接順序(s1驅動s2或s2驅動s1),計算對應的總成本。

3.4 成本計算總結

MySQL 查詢優化器通過比較全表掃描和索引訪問的 I/O 和 CPU 成本,選擇成本最低的執行計劃。聯表查詢中,優化器評估不同連接順序和訪問方法,結合條件過濾和統計數據提高估算準確性。


四、基于規則的查詢優化

MySQL 查詢優化器通過基于規則的優化手段,將用戶編寫的復雜或低效查詢語句重寫為更高效的形式(包括條件化簡、外連接消除和子查詢優化等),以提升查詢性能。

4.1 條件化簡

MySQL 查詢優化器會對查詢中的搜索條件(表達式)進行化簡,以減少計算復雜度和提高執行效率。

4.1.1 移除不必要的括號

優化器會移除查詢中多余的括號,簡化表達式,例如:

-- 原始查詢
((a = 5 AND b = c) OR ((a > c) AND (c < 5)))-- 優化后
(a = 5 AND b = c) OR (a > c AND c < 5)
4.1.2 常量傳遞

當某個列與常量等值匹配,并且與其他表達式通過 AND 連接時,優化器會將常量值傳遞到其他表達式中:

-- 原始查詢
a = 5 AND b > a-- 優化后
a = 5 AND b > 5
4.1.3 等值傳遞

當多個列之間存在等值關系時,優化器會將等值條件傳遞:

-- 原始查詢
a = b AND b = c AND c = 5-- 優化后
a = 5 AND b = 5 AND c = 5
4.1.4 移除無用條件

對于恒為 TRUE 或 FALSE 的條件,優化器會直接移除:

-- 原始查詢
(a < 1 AND b = b) OR (a = 6 OR 5 != 5)-- 優化后
a < 1 OR a = 6
4.1.5 表達式計算

對于只包含常量的表達式,優化器會在查詢執行前計算其值:

-- 原始查詢
a = 5 + 1-- 優化后
a = 6

但對于涉及復雜函數或非單獨列的表達式(如 ABS(a) > 5),優化器不會進行化簡。

4.1.6 HAVING 和 WHERE 子句合并

如果查詢不包含聚合函數(如 SUM、MAX)或 GROUP BY 子句,優化器會將 HAVING 子句合并到 WHERE 子句中。

4.2 外連接消除

外連接(LEFT/RIGHT JOIN)與內連接(INNER JOIN)的區別在于,驅動表的記錄即使在被驅動表中找不到匹配記錄,也會被保留(被驅動表字段填充為 NULL)。

4.2.1 空值拒絕(Null Rejection)

如果 WHERE 子句中指定被驅動表的列不為 NULL(如 t2.n2 IS NOT NULL),外連接效果等價于內連接:

-- 原始查詢
SELECT * FROM t1 LEFT JOIN t2 ON t1.m1 = t2.m2 WHERE t2.n2 IS NOT NULL;-- 等價于
SELECT * FROM t1 INNER JOIN t2 ON t1.m1 = t2.m2;

這種轉換允許優化器調整連接順序,降低查詢成本。

4.3 子查詢優化

子查詢是嵌套在查詢中的子查詢,優化器通過物化表和半連接等策略優化其執行效率。

4.3.1 子查詢出現位置
  • SELECT 子句:如 (SELECT m1 FROM t1 LIMIT 1)
  • FROM 子句:作為派生表,如 (SELECT m2 + 1 AS m, n2 AS n FROM t2 WHERE m2 > 2) AS t
  • WHERE 或 ON 子句:如 WHERE m1 IN (SELECT m2 FROM t2)
  • ORDER BY 或 GROUP BY 子句:較少使用
4.3.2 子查詢分類

按返回結果集分類

  • 標量子查詢:返回單一值(如 (SELECT m1 FROM t1 LIMIT 1)
  • 行子查詢:返回單條記錄,包含多列(如 (SELECT m2, n2 FROM t2 LIMIT 1)
  • 列子查詢:返回單列多行(如 (SELECT m2 FROM t2)
  • 表子查詢:返回多行多列(如 (SELECT m2, n2 FROM t2)

按與外層查詢關系分類

  • 不相關子查詢:獨立執行,不依賴外層查詢值
  • 相關子查詢:執行依賴外層查詢值,如 WHERE m1 IN (SELECT m2 FROM t2 WHERE n1 = n2)
4.3.3 子查詢在布爾表達式中的使用
  • 比較操作符(=、>、<、等):與標量子查詢或行子查詢結合
  • IN/NOT IN、ANY/SOME、ALL:與列子查詢或表子查詢結合
  • EXISTS/NOT EXISTS:判斷子查詢結果集是否為空
4.3.4 子查詢優化策略

MySQL優化器通過不同的策略優化各類子查詢,主要優化路徑如下:

子查詢優化
標量/行子查詢
IN子查詢
EXISTS子查詢
不相關子查詢
先執行子查詢
相關子查詢
外層逐行執行
物化表策略
半連接策略
創建臨時表
去重+索引
轉換為內連接
子查詢上拉
消除重復記錄
松散索引掃描
首次匹配策略
物化后連接
轉換為半連接
或保持EXISTS

主要優化策略說明

物化表(Materialization)

  • 將不相關 IN 子查詢結果集寫入臨時表(物化表),對物化表記錄去重,建立哈希索引(內存)或 B+ 樹索引(磁盤)
  • 將外層查詢與物化表進行內連接,由優化器評估連接順序成本
  • 適用于子查詢結果集較大時,物化表通過索引加速 IN 判斷

半連接(Semi-join)

  • 將 IN 子查詢轉換為半連接,僅保留外層查詢表的記錄,不關心被驅動表匹配的記錄數
  • 適用于子查詢在 WHERE/ON 子句中,與 IN 語句結合,或者子查詢為單一查詢(無 UNION、GROUP BY、HAVING 等)
  • 不適用于子查詢與 OR 連接、使用 NOT IN或者子查詢在 SELECT 子句中

執行策略包括

  • 子查詢查詢列表為主鍵/唯一索引列時,將子查詢表上拉到外層查詢的 FROM 子句
  • 使用臨時表記錄外層查詢記錄 ID,消除重復
  • 對子查詢表使用松散索引掃描,僅取索引值相同的首條記錄
  • 逐條檢查外層查詢記錄,找到第一條匹配的子查詢記錄即停止
  • 物化子查詢后與外層查詢表連接

4.4 優化規則總結

MySQL 查詢優化器通過條件化簡、外連接消除和子查詢優化等基于規則的優化手段,將用戶編寫的低效查詢轉換為高效形式。這些規則包括移除冗余條件、合并子句、將外連接轉為內連接、以及通過物化表和半連接優化 IN 子查詢。開發者可以通過編寫簡潔的 SQL 語句、利用索引和主鍵、避免冗余操作等方式,進一步配合優化器提升查詢性能。


五、執行計劃分析

MySQL采用EXPLAIN分析SQL查詢的執行計劃,幫助優化查詢性能。它展示MySQL如何執行查詢,包括表訪問順序、索引使用情況等。

5.1 EXPLAIN 輸出字段詳解

運行EXPLAIN語句后,會返回一個結果集,包含多個字段:

5.1.1 查詢標識字段
  • id:表示查詢中每個子查詢的執行順序。值越大,優先級越高,越先執行。相同id表示同一執行層級
  • select_type:查詢類型,常見值包括:
    • SIMPLE:簡單查詢,無子查詢或聯合
    • PRIMARY:最外層查詢
    • SUBQUERY:子查詢
    • DERIVED:派生表(如FROM子句中的子查詢)
    • UNION:UNION操作中的查詢
5.1.2 表和分區信息
  • table:顯示查詢涉及的表名或別名
  • partitions:顯示查詢涉及的分區(如果表分區了)。無分區表時為空
5.1.3 訪問方法和索引信息
  • type:訪問類型(即前面所說的訪問方法),反映查詢效率,常見值從優到劣:

    • system:表只有一行數據
    • const:通過主鍵或唯一索引直接定位一行
    • eq_ref:通過主鍵或唯一索引進行等值匹配
    • ref:通過非唯一索引進行等值匹配
    • range:索引范圍掃描(如><IN
    • index:全索引掃描
    • ALL:全表掃描(效率最低)
  • possible_keys:MySQL可能使用的索引列表。為空表示沒有可用索引

  • key:實際使用的索引。為空表示未使用索引

  • key_len:使用索引的長度(字節),幫助判斷聯合索引的使用情況。值越小,索引選擇性越高

5.1.4 查詢條件和統計信息
  • ref:顯示與key索引比較的列或常量。例如,const表示常量值,表名.列名表示關聯表的列
  • rows:估計掃描的行數,值越小越好,反映查詢效率
  • filtered:表示過濾后的行數百分比(0-100%)。值越高,表示過濾效果越好
5.1.5 額外信息
  • Extra:額外信息,常見值:
    • Using index:僅使用索引完成查詢(覆蓋索引)
    • Using where:在WHERE條件中過濾
    • Using temporary:使用了臨時表(可能影響性能)
    • Using filesort:需要額外排序(可能影響性能)
    • Using join buffer:使用了連接緩沖區

5.2 執行計劃分析要點

執行計劃分析時,需要重點關注以下幾個關鍵字段:

EXPLAIN執行計劃分析
關鍵字段重點關注
type字段
訪問方法優先級
key字段
索引使用情況
rows字段
掃描行數估算
Extra字段
額外執行信息
? const/eq_ref/ref
高效訪問
?? range/index
中等效率
? ALL
避免全表掃描
? 有具體索引名
索引命中
? NULL
未使用索引
? 小數值
高效查詢
? 大數值
需要優化
? Using index
覆蓋索引
?? Using temporary
使用臨時表
?? Using filesort
額外排序
?? Using where
需要回表

分析時著重點

  • 關注type,盡量避免ALLindex,優先consteq_refref
  • 檢查keypossible_keys,確保使用合適的索引
  • 觀察rowsfiltered,減少掃描行數
  • 注意Extra中的Using temporaryUsing filesort,可能需要優化

5.3 實際分析示例

通過分析這些字段,可定位查詢瓶頸,優化索引或重寫SQL以提高性能。


六、慢SQL治理實戰案例

以下是5個經過脫敏處理的實際慢SQL案例,展示了從問題發現到優化解決的完整過程。

6.1 慢SQL治理流程圖

慢SQL治理是一個系統性的過程,需要遵循科學的流程來確保優化效果:

慢SQL治理流程
1. 問題發現
2. 問題分析
3. 制定方案
4. 實施優化
5. 效果驗證
6. 持續監控
監控告警
慢查詢日志
性能指標
EXPLAIN分析
索引使用情況
數據量統計
并發場景分析
SQL改寫優化
索引設計優化
架構調整優化
數據清理優化
去除前模糊匹配
改寫子查詢
優化連接條件
新增復合索引
調整索引順序
覆蓋索引設計
引入搜索引擎
讀寫分離
分庫分表
歷史數據歸檔
無效數據清理
數據壓縮
測試環境驗證
灰度發布
全量上線
性能指標對比
業務功能驗證
系統穩定性檢查

慢SQL治理的關鍵在于系統性思考漸進式優化

  • 發現階段:建立完善的監控體系,及時發現性能問題
  • 分析階段:深入分析根本原因,避免頭痛醫頭、腳痛醫腳
  • 方案階段:制定多層次的優化方案,從SQL到架構全方位考慮
  • 實施階段:謹慎實施,確保系統穩定性
  • 驗證階段:客觀評估優化效果,確保問題真正解決
  • 監控階段:持續觀察,防止問題反彈

6.2 案例一:模糊匹配優化

問題SQL(脫敏后)
SELECT count(*) 
FROM `脫敏表` 
WHERE (xxx_id = XXX) AND (`name` LIKE '%關鍵詞%')
執行計劃分析
字段含義
id1單表查詢
select_typeSIMPLE簡單查詢
table脫敏表查詢目標表
typeref使用了索引,但非最優
keyidx_xxxid_xxxuserid使用的索引不含 name
rows589328預計掃描 58 萬行
filtered11.11低過濾率
ExtraUsing where沒有使用 name 字段索引,存在回表操作
性能瓶頸分析
問題點描述
%關鍵詞% 前模糊匹配無法使用 B+Tree 索引
name 無索引即使改為前綴匹配 LIKE ‘xxx%’ 也無法優化
使用了錯誤索引當前索引 (xxx_id, xxxuserid) 無法支持 name 查詢
高數據量下回表掃描xxx_id = 177 的數據量大(58w),過濾性能差
優化方案

去掉前模糊匹配,改成 LIKE '關鍵詞%',并為 name 字段建立索引。


6.3 案例二:復雜條件查詢優化

問題SQL(脫敏后)
SELECT count(*) 
FROM `脫敏表` 
WHERE creator IN ('ID1', 'ID2', 'ID3', 'ID4', 'ID5', 'ID6', 'ID7') AND (xxx_id = YYY) AND (status = 1) AND (`name` LIKE '%活動關鍵詞%')
性能瓶頸分析
字段解讀
typeref使用了 corp_id 索引,但效率不高
keyidx_xxxid_xxxuserid說明使用的是 (corp_id, userid) 的索引
rows518218預估要掃描 50 萬條數據(即 corp_id=156 的數據量)
filtered0.56超低過濾率,說明大多數數據都是"掃了但沒用上"
ExtraUsing where多條件聯合過濾 + 無法使用 name 的索引,導致回表嚴重
問題分析表
問題點描述
%模糊匹配%LIKE ‘%素質學習活動%’ 無法走索引,依舊是性能殺手
無 name 索引和慢 SQL 1 一樣,name 字段未建立索引
使用錯誤索引當前使用的是 (xxx_id, xxxuserid),和查詢條件不匹配
多個 WHERE 條件creator IN (…) + status=1 + xxx_id=156 增加了查詢復雜度
掃描行數巨大掃描了 50 萬行,過濾率不到 1%,極其低效
優化方案

使用搜索引擎(如Elasticsearch)進行模糊匹配,將復雜的文本搜索從數據庫中分離出來。


6.4 案例三:URL字段查詢優化

問題SQL(脫敏后)
SELECT `id`, `media_id` 
FROM `脫敏表` 
WHERE (xxx_id = XXX) AND (user_id = ZZZ) AND (url = 'https://example.com/feed?...') LIMIT 1
執行計劃分析
字段含義
typeref使用了索引但是模糊匹配級別,不夠高效
keyidx_xxxid_xxxuserid當前使用的是 (corp_id, user_id) 索引
rows27622即便用了索引,預估掃描仍達 2 萬+ 行
filtered10%表示大部分數據被過濾掉了(說明 url 沒有參與索引)
ExtraUsing where表示 MySQL 仍需逐行判斷 url=xxx 條件
優化方案

添加新字段(如哈希值),改寫SQL

SELECT id, media_id
FROM 脫敏表
WHERE xxx_id = XXXAND xxxuser_id = ZZZAND url_hash = '哈希值'AND url = 'https://example.com/feed?...'LIMIT 1

6.5 案例四:高并發系統級性能問題

問題SQL(脫敏后)
SELECT id, xxxuser_id1, xxxuser_id2, xxx_id, task_id, ...
FROM table_task
WHERE xxxuser_id2 = '用戶ID'AND xxx_id = XXXAND create_time >= 時間戳AND task_status = 2;
執行計劃分析
字段含義
typerange范圍掃描,說明部分使用了索引
keyidx_wx_user_time使用了某個復合索引(猜測為 xxx_userid + create_time)
rows68794預估需要掃描約 6.8 萬行
filtered1.00過濾率很低(接近全掃描)
ExtraUsing index condition; Using where沒有使用覆蓋索引,部分條件未被索引消化
問題分析

這條 SQL 單條執行并不慢,索引命中了(走的是 (xxx_userid, create_time)),本質的問題是高并發下的系統級退化

問題類型原因
? 數據庫連接池耗盡協程數超出 max_open_conns,大量阻塞
? 單連接響應慢數據量大,每條查詢雖然不慢,但處理行數多,連接占用時間長
? Goroutine 累積協程爆炸,CPU、內存調度競爭,影響系統吞吐
? MySQL CPU 使用高并發下 10w+ 次篩選,造成 DB CPU 持續高壓
? 索引雖走,但行數多rows=68794 表示每次查近 7 萬條,I/O 和網絡傳輸壓力大
優化方案
  • 限制協程并發度(從4降到2)
  • 查詢范圍從10天減少到3天
  • 執行頻率從10分鐘一次改為30分鐘一次

6.6 案例五:數據傾斜與并發優化

問題SQL(脫敏后)
SELECT count(*) 
FROM `脫敏表` 
WHERE (xxx_id = YYY) AND (user_id = AAA) AND (state = '狀態值') AND (del_way != 0) AND (delete_time >= 時間戳1) AND (delete_time < 時間戳2)
執行計劃分析
字段含義
keyidx_xxxuserid_followtime使用了 (xxxuser_id, follow_time) 聯合索引,但不完全匹配查詢條件
key_len8實際只使用了索引的第一列 xxxuser_id(類型為 BIGINT,占用 8 字節)
refconstxxxuser_id = 51967 為常量匹配,用于索引過濾
rows53472預估掃描行數約為 5.3 萬,說明該 xxxuser_id 擁有大量數據
filtered0.00預估過濾率為 0%,說明幾乎所有行都需進一步篩選,回表嚴重
ExtraUsing where表示需回表做完整 WHERE 條件判斷,未能使用覆蓋索引或索引條件下推
問題分析
問題描述
??數據傾斜部分 xxxuser_id 擁有極多數據(幾十萬級),SQL執行時間呈不均衡狀態
??并發導致放大每個用戶都執行這條 SQL,量一大就撐爆連接池或 DB 資源
??索引不命中 delete_time / state這兩個高過濾條件字段只能靠回表判斷,掃描行數增多
??邏輯層并發掃描多個用戶并發執行時,每人掃幾萬,系統瞬間承壓(4核但代碼設置了10個協程數)
優化方案

理想索引

CREATE INDEX idx_xxxuserid_delway_state_deltime 
ON 脫敏表(xxxuser_id, del_way, state, delete_time);

實際采用的3種優化

優化手段背景/動機預期收益
清理數據庫表中的雙刪數據當前 del_way != 0 過濾范圍太大,無效數據長期滯留影響查詢效率;部分舊數據可歸檔或物理刪除降低數據量,減少每次掃描行數,提升過濾效率
降低協程并發數當前每個 xxxuser_id 并發觸發多條 SQL,容易造成連接池耗盡、系統抖動控制系統資源消耗,更穩定運行、更平滑負載
將時間范圍由 3 天縮小到 1 天原先掃描范圍廣、數據量大,導致執行時間不可控;改為每日定時執行或滾動聚合明確可控的數據窗口,降低查詢壓力、支持更頻繁調度

6.7 慢SQL治理總結

類型內容
數據優化清理無效數據、減少掃描量
查詢優化改寫 SQL 表達式、提升執行計劃
系統優化限制并發、平滑調度
窗口優化縮小時間范圍、減少數據量

總結

本文從MySQL查詢的基礎機制出發,系統性地介紹了查詢優化的理論基礎和實踐方法:

  1. 訪問方法:7種不同的訪問方法為查詢提供了從最高效的const到最低效的all的性能選擇
  2. 聯表查詢:理解笛卡爾積本質和連接算法,合理設計索引和選擇驅動表
  3. 成本計算:掌握I/O成本和CPU成本的計算原理,理解優化器的決策依據
  4. 規則優化:利用條件化簡、外連接消除等規則,讓查詢更高效
  5. 執行計劃:通過EXPLAIN分析瓶頸,指導索引設計和SQL優化
  6. 實戰案例:5個真實案例展示了從問題發現到解決的完整流程

通過理論學習和實踐應用相結合,可以更好地理解MySQL查詢優化的精髓,在實際開發中寫出高性能的SQL語句

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/917450.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/917450.shtml
英文地址,請注明出處:http://en.pswp.cn/news/917450.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

從零開始的python學習(九)P129+P130+P131+P132+P133

本文章記錄觀看B站python教程學習筆記和實踐感悟&#xff0c;視頻鏈接&#xff1a;【花了2萬多買的Python教程全套&#xff0c;現在分享給大家&#xff0c;入門到精通(Python全棧開發教程)】 https://www.bilibili.com/video/BV1wD4y1o7AS/?p6&share_sourcecopy_web&v…

LCL濾波器及其電容電流前饋有源阻尼設計軟件【LCLAD_designer】

本文主要介紹針對阮新波著《LCL型并網逆變器的控制技術》書籍 第二章&#xff08;LCL濾波器設計&#xff09;及第五章&#xff08;LCL型并網逆變器的電容電流反饋有源阻尼設計&#xff09;開發的一款交互式軟件【LCL&AD_designer】&#xff0c;開發平臺MATLAB_R2022b/app d…

【Conda】配置Conda鏡像源

Conda 鏡像源配置指南 適用系統&#xff1a;Windows 10&#xff08;含 Miniconda / Anaconda&#xff09; & Linux&#xff08;Ubuntu / CentOS / Debian 等&#xff09;1. 為什么要設置鏡像源 在中國大陸直接訪問 repo.anaconda.com 經常遇到速度慢、連接超時、SSL 錯誤等…

八股取士--docker

基礎概念類 1. 什么是Docker&#xff1f;它解決了什么問題&#xff1f; 解析&#xff1a; Docker是一個開源的容器化平臺&#xff0c;用于開發、交付和運行應用程序。 主要解決的問題&#xff1a; 環境一致性&#xff1a;解決"在我機器上能跑"的問題資源利用率&#…

C++:STL中的棧和隊列的適配器deque

學習完string類、容器vector和容器list&#xff0c;再去學習其他容器的學習成本就非常低&#xff0c;容器的使用方法都大差不差&#xff0c;而棧和隊列的底層使用了適配器&#xff0c;去模擬實現就沒有那么麻煩&#xff0c;適配器也是一種容器&#xff0c;但是這種容器兼備棧和…

9類主流數據庫 - 幫你更好地進行數據庫選型!

作者&#xff1a;唐叔在學習 專欄&#xff1a;數據庫學習 標簽&#xff1a;數據庫選型、MySQL、Redis、MongoDB、大數據存儲、NoSQL、數據庫優化、數據架構、AI數據庫 大家好&#xff0c;我是你們的老朋友唐叔&#xff01;今天咱們來聊聊程序員吃飯的家伙之一 —— 數據庫。在這…

推送本地項目到Gitee遠程倉庫

文章目錄前言前面已加學習了下載gitee軟件&#xff0c;網址在上一篇文章。在gitee創建賬號與倉庫。現在來學習如何講本地項目推送到Gitee遠程倉庫一、流程總結前言 前面已加學習了下載gitee軟件&#xff0c;網址在上一篇文章。在gitee創建賬號與倉庫。現在來學習如何講本地項目…

CMake 命令行參數完全指南(5)

?**40. --version**? ?解釋?&#xff1a;顯示CMake版本 ?示例?&#xff1a; cmake --version # 輸出&#xff1a;cmake version 3.25.2?**41. --warn-uninitialized**? ?解釋?&#xff1a;警告未初始化的變量 ?適用場景?&#xff1a;檢測腳本錯誤 ?示例?&#xf…

基于Python實現生產者—消費者分布式消息隊列:構建高可用異步通信系統

深入剖析分布式消息隊列的核心原理與Python實現&#xff0c;附完整架構設計和代碼實現引言&#xff1a;分布式系統的通信基石在微服務架構和云原生應用普及的今天&#xff0c;服務間的異步通信成為系統設計的核心挑戰。當單體應用拆分為數十個微服務后&#xff0c;服務間通信呈…

【大模型核心技術】Agent 理論與實戰

一、基本概念 LLM 特性&#xff1a;擅長理解和生成文本&#xff0c;但采用 “一次性” 響應模式&#xff0c;本質上是無記憶的生成模型。Agent 本質&#xff1a;包含 LLM 的系統應用&#xff0c;具備自主規劃、工具調用和環境反饋能力&#xff0c;是將 LLM 從 “聊天機器人” 升…

Maven - 依賴的生命周期詳解

作者&#xff1a;唐叔在學習 專欄&#xff1a;唐叔的Java實踐 標簽&#xff1a;Maven依賴管理、Java項目構建、依賴傳遞性、Spring Boot依賴、Maven最佳實踐、項目構建工具、依賴沖突解決、POM文件詳解 文章目錄一、開篇二、Maven依賴生命周期2.1 依賴聲明階段&#xff1a;POM文…

從零打造大語言模型--處理文本數據

從零打造大語言模型 第 1 章&#xff1a;處理文本數據 章節導讀 在把文本投喂進 Transformer 之前&#xff0c;需要兩步&#xff1a;① 將字符流切分成離散 Token&#xff1b;② 把 Token 映射成連續向量。 1.1 理解詞嵌入&#xff08;Word Embedding&#xff09; 嵌入向量 一…

【Spring】Bean的生命周期,部分源碼解釋

文章目錄Bean 的生命周期執行流程代碼演示執行結果源碼閱讀AbstractAutowireCapableBeanFactorydoCreateBeaninitializeBeanBean 的生命周期 生命周期指的是一個對象從誕生到銷毀的整個生命過程&#xff0c;我們把這個過程就叫做一個對象的聲明周期 Bean 的聲明周期分為以下 …

[spring-cloud: 服務發現]-源碼解析

DiscoveryClient DiscoveryClient 接口定義了常見的服務發現操作&#xff0c;如獲取服務實例、獲取所有服務ID、驗證客戶端可用性等&#xff0c;通常用于 Eureka 或 Consul 等服務發現框架。 public interface DiscoveryClient extends Ordered {/*** Default order of the dis…

QML 基礎語法與對象模型

QML (Qt Meta-Object Language) 是一種聲明式語言&#xff0c;專為創建流暢的用戶界面和應用程序邏輯而設計。作為 Qt 框架的一部分&#xff0c;QML 提供了簡潔、直觀的語法來描述 UI 組件及其交互方式。本文將深入解析 QML 的基礎語法和對象模型。 一、QML 基礎語法 1. 基本對…

HTTPS的概念和工作過程

一.HTTPS是什么HTTPS也是一個應用層協議&#xff0c;是在HTTP協議的基礎上引入了一個加密層&#xff08;SSL&#xff09;HTTP協議內容都是按照文本的方式明文傳輸的&#xff0c;這就導致傳輸過程中可能出現被篡改的情況最著名的就是十多年前網絡剛發展的時期&#xff0c;出現“…

Unity —— Android 應用構建與發布?

文章目錄1 ?Gradle模板??&#xff1a;了解Gradle模板的作用及使用方法&#xff0c;以增強對構建流程的控制。?2 ?Gradle模板變量??&#xff1a;參考文檔——自定義Gradle模板文件中可用的變量列表。2.1 修改Unity應用的Gradle工程文件2.1.1 通過Gradle模板文件2.1.2 導出…

【iOS】strong和copy工作流程探尋、OC屬性關鍵字復習

文章目錄前言strong和copy的區別為什么要用copy&#xff1f;什么時候用什么修飾&#xff1f;strong&#xff08;ARC自動管理&#xff09;strong修飾變量的底層流程圖底層代碼核心實現小結copy底層流程圖對比與strong的關鍵不同之處內部調用關系&#xff08;偽代碼&#xff09;小…

程序代碼篇---多循環串口程序切換

上位機版&#xff08;Python&#xff09;要實現根據串口接收結果高效切換四個 while 循環函數&#xff0c;我們可以采用狀態機模式&#xff0c;配合非阻塞串口讀取來設計程序結構。這種方式可以實現快速切換&#xff0c;避免不必要的資源消耗。下面是一個高效的實現方案&#x…

rk3568上,實現ota,計算hash,驗證簽名,判斷激活分區,并通過dd命令,寫入對應AB分區

通過自定義升級程序&#xff0c;更直觀的理解ota升級原理。 一、模擬計算hash&#xff0c;驗證簽名&#xff0c;判斷激活分區&#xff0c;并通過dd命令&#xff0c;寫入對應分區 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <u…