MySQL - 索引原理與優化:深入解析B+Tree與高效查詢策略

文章目錄

    • 引言
    • 一、B+Tree索引核心原理
      • 1.1 索引數據結構演化
      • 1.2 B+Tree的存儲結構
        • 通過主鍵查詢(主鍵索引)商品數據的過程
        • 通過非主鍵(輔助索引)查詢商品數據的過程
      • MySQL InnoDB 的索引原理
    • 二、執行計劃深度解析
    • 三、索引失效的六大陷阱
      • 3.1 隱式類型轉換
      • 3.2 索引列參與運算
      • 3.3 模糊查詢通配符前置
      • 3.4 最左前綴原則違反
      • 3.5 OR條件使用不當
      • 3.6 統計信息不準確
    • 四、高性能索引設計策略
      • 4.1 覆蓋索引優化
      • 4.2 前綴索引技巧
      • 4.3 聯合索引排序策略
      • 4.4 索引下推優化(ICP)
    • 五、真實場景案例解析
      • 5.1 電商訂單查詢優化
      • 5.2 社交平臺好友推薦
    • 結語

在這里插入圖片描述

引言

數據庫性能直接影響系統響應速度。作為關系型數據庫的典型代表,MySQL的索引設計與優化是每個開發者必須掌握的技能。接下來我們將深入剖析MySQL的索引原理,結合真實場景案例,揭秘B+Tree的獨特優勢,并通過執行計劃分析與優化策略,探討如何構建高性能數據庫系統。

我們經常會碰到如下問題:

  • 數據庫索引底層使用的是什么數據結構和算法呢?

  • 為什么 MySQL InnoDB 選擇 B+Tree 當默認的索引數據結構?

  • 如何通過執行計劃查看索引使用詳情?

  • 有哪些情況會導致索引失效?

  • 平時有哪些常見的優化索引的方法?

  • ……

無非就是對應

  • MySQL InnoDB 的索引原理;

  • B+Tree 相比于其他索引數據結構(如 B-Tree、二叉樹,以及 Hash 表)的優勢;

  • MySQL 執行計劃的方法;

  • 導致索引失效的常見情況;

  • 常用的建立高效索引的技巧(如前綴索引、建立覆蓋索引等)。


一、B+Tree索引核心原理

1.1 索引數據結構演化

深入理解二叉樹、B樹與B+樹:原理、應用與實現

數據結構查詢復雜度范圍查詢磁盤I/O效率適用場景
二叉樹O(log n)小數據量精確查詢
B-TreeO(log n)較好通用場景
B+TreeO(log n)優秀大數據量范圍查詢
Hash表O(1)不支持等值查詢

1.2 B+Tree的存儲結構

建個表如下:

CREATE TABLE `product`  (`id` int(11) NOT NULL,`product_no` varchar(20)  DEFAULT NULL,`name` varchar(255) DEFAULT NULL,`price` decimal(10, 2) DEFAULT NULL,PRIMARY KEY (`id`) USING BTREE
) CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
);

新增幾條數據如下:
在這里插入圖片描述

通過主鍵查詢(主鍵索引)商品數據的過程

此時當我們使用主鍵索引查詢商品 15 的時候,那么按照 B+Tree 索引原理,是如何找到對應數據的呢?

select * from product where id = 15

我們可以通過數據手動構建一個 B+Tree,它的每個節點包含 3 個子節點(B+Tree 每個節點允許有 M 個子節點,且 M>2),根節點中的數據值 1、18、36 分別是子節點(1,6,12),(18,24,30)和(36,41,52)中的最小值。

每一層父節點的數據值都會出現在下層子節點的數據值中,因此在葉子節點中,包括了所有的數據值信息,并且每一個葉子節點都指向下一個葉子節點,形成一個鏈表。如圖所

在這里插入圖片描述
比如想要查找數據值 15,B+Tree 會自頂向下逐層進行查找:

  • 將 15 與根節點的數據 (1,18,36) 比較,15 在 1 和 18 之間,所以根據 B+Tree的搜索邏輯,找到第二層的數據塊 (1,6,12);

  • 在第二層的數據塊 (1,6,12) 中進行查找,因為 15 大于 12,所以找到第三層的數據塊 (12,15,17);

  • 在葉子節點的數據塊 (12,15,17) 中進行查找,然后我們找到了數據值 15;

  • 最終根據數據值 15 找到葉子節點中存儲的數據。

整個過程一共進行了 3 次 I/O 操作,所以 B+Tree 相比于 B 樹和二叉樹來說,最大的優勢在于查詢效率


通過非主鍵(輔助索引)查詢商品數據的過程

如果使用商品編碼查詢商品(即使用輔助索引進行查詢),會先檢索輔助索引中的 B+Tree 的 商品編碼,找到對應的葉子節點,獲取主鍵值,然后再通過主鍵索引中的 B+Tree 樹查詢到對應的葉子節點,然后獲取整行數據。這個過程叫回表。


B+Tree特點:

  • 多叉結構:單個節點存儲多個鍵值,降低樹高度(3-4層可支撐千萬級數據)
  • 數據聚集:葉子節點形成有序雙向鏈表,支持高效范圍查詢
  • 分層存儲:非葉節點僅存索引鍵,葉節點存儲完整數據(聚簇索引)或主鍵(輔助索引)

MySQL InnoDB 的索引原理

從數據結構的角度來看, MySQL 常見索引有 B+Tree 索引、HASH 索引、Full-Text 索引。

在這里插入圖片描述

在實際應用中,InnoDB 是 MySQL 建表時默認的存儲引擎,B+Tree 索引類型也是 MySQL 存儲引擎采用最多的索引類型。

在創建表時,InnoDB 存儲引擎默認使用表的主鍵作為主鍵索引,該主鍵索引就是聚簇索引(Clustered Index),如果表沒有定義主鍵,InnoDB 就自己產生一個隱藏的 6 個字節的主鍵 ID 值作為主鍵索引,而創建的主鍵索引默認使用的是 B+Tree 索引。


二、執行計劃深度解析

1存儲商品信息的演示表 product:

CREATE TABLE `product`  (`id` int(11) NOT NULL,`product_no` varchar(20)  DEFAULT NULL,`name` varchar(255) DEFAULT NULL,`price` decimal(10, 2) DEFAULT NULL,PRIMARY KEY (`id`) USING BTREE,KEY 'index_name' ('name').KEY 'index_id_name' ('id', 'name')
) CHARACTER SET = utf8 COLLATE = utf8_general_ci

表中包含了主鍵索引、name 字段上的普通索引,以及 id 和 name 兩個字段的聯合索引。現在我們來看一條簡單查詢語句的執行計劃:

在這里插入圖片描述

重點關注 type 字段, 表示數據掃描類型,也就是描述了找到所需數據時使用的掃描方式是什么,常見掃描類型的執行效率從低到高的順序為(考慮到查詢效率問題,全表掃描和全索引掃描要盡量避免):

    ALL(全表掃描);index(全索引掃描);range(索引范圍掃描);ref(非唯一索引掃描);eq_ref(唯一索引掃描);const(結果只有一條的主鍵或唯一索引掃描)。

關鍵指標解讀:

  • type:掃描類型(性能排序:const > ref > range > index > ALL)
  • key_len:索引使用長度(可判斷是否使用完整索引)
  • possible_keys 字段表示可能用到的索引
  • key 字段表示實際用的索引
  • rows:預估掃描行數
  • Extra:額外信息(Using index/Using filesort等)

三、索引失效的六大陷阱

來看一個索引失效的例子:
在這里插入圖片描述

這條帶有 like 查詢的 SQL 語句,沒有用到 product 表中的 index_name 索引。

我們結合普通索引的 B+Tree 結構看一下索引失效的原因: 當 MySQL 優化器根據 name like ‘%路由器’ 這個條件,到索引 index_name 的 B+Tree 結構上進行查詢評估時,發現當前節點的左右子節點上的值都有可能符合 ‘%路由器’ 這個條件,于是優化器判定當前索引需要掃描整個索引,并且還要回表查詢,不如直接全表掃描。

當然,還有其他類似的索引失效的情況:

  • 索引列上做了計算、函數、類型轉換操作,這些情況下索引失效是因為查詢過程需要掃描整個索引并回表,代價高于直接全表掃描;

  • like 匹配使用了前綴匹配符 ‘%abc’;

  • 字符串不加引號導致類型轉換;

所以, 如果 MySQL 查詢優化器預估走索引的代價比全表掃描的代價還要大,則不走對應的索引,直接全表掃描,如果走索引比全表掃描代價小,則使用索引。

3.1 隱式類型轉換

-- 字符串字段使用數字查詢
SELECT * FROM user WHERE phone = 13800138000;

3.2 索引列參與運算

-- DATE_FORMAT函數導致索引失效
SELECT * FROM orders 
WHERE DATE_FORMAT(create_time, '%Y-%m') = '2023-07';

3.3 模糊查詢通配符前置

-- 前導通配符無法使用索引
SELECT * FROM article WHERE content LIKE '%數據庫%';

3.4 最左前綴原則違反

-- 聯合索引(a,b,c)無法命中
SELECT * FROM table WHERE b = 2 AND c = 3;

3.5 OR條件使用不當

-- 其中一個條件無索引將導致全表掃描
SELECT * FROM products 
WHERE category_id = 5 OR price > 1000;

3.6 統計信息不準確

當數據分布變化超過10%時,需執行ANALYZE TABLE更新統計信息


四、高性能索引設計策略

4.1 覆蓋索引優化

覆蓋索引是指 SQL 中 query 的所有字段,在索引 B+tree 的葉子節點上都能找得到的那些索引,從輔助索引中查詢得到記錄,而不需要通過聚簇索引查詢獲得。假設我們只需要查詢商品的名稱、價格,有什么方式可以避免回表呢?

我們可以建立一個組合索引,即商品ID、名稱、價格作為一個組合索引。如果索引中存在這些數據,查詢將不會再次檢索主鍵索引,從而避免回表。所以,使用覆蓋索引的好處很明顯,即不需要查詢出包含整行記錄的所有信息,也就減少了大量的 I/O 操作

-- 建立包含所有查詢字段的聯合索引
CREATE INDEX idx_order_status_time 
ON orders(status, create_time, total_amount);SELECT status, create_time, total_amount 
FROM orders 
WHERE status = 1 
ORDER BY create_time DESC;

4.2 前綴索引技巧

前綴索引就是用某個字段中,字符串的前幾個字符建立索引,比如我們可以在訂單表上對商品名稱字段的前 5 個字符建立索引。使用前綴索引是為了減小索引字段大小,可以增加一個索引頁中存儲的索引值,有效提高索引的查詢速度。在一些大字符串的字段作為索引時,使用前綴索引可以幫助我們減小索引項的大小。

但是,前綴索引有一定的局限性,例如 order by 就無法使用前綴索引,無法把前綴索引用作覆蓋索引。

-- 對長文本字段前20字符建立索引
CREATE INDEX idx_product_desc 
ON products(product_desc(20));-- 計算最佳前綴長度
SELECT COUNT(DISTINCT LEFT(product_desc, 20)) / COUNT(*) 
FROM products;

4.3 聯合索引排序策略

聯合索引時,存在最左匹配原則,也就是按照最左優先的方式進行索引的匹配。比如聯合索引 (userpin, username),如果查詢條件是 WHERE userpin=1 AND username=2,就可以匹配上聯合索引;或者查詢條件是 WHERE userpin=1,也能匹配上聯合索引,但是如果查詢條件是 WHERE username=2,就無法匹配上聯合索引。

另外,建立聯合索引時的字段順序,對索引效率也有很大影響。越靠前的字段被用于索引過濾的概率越高,實際開發工作中建立聯合索引時,要把區分度大的字段排在前面,這樣區分度大的字段越有可能被更多的 SQL 使用到。

-- 區分度高的字段在前
CREATE INDEX idx_user_region_gender 
ON users(region_code, gender);

在這里插入圖片描述
區分度就是某個字段 column 不同值的個數除以表的總行數,比如性別的區分度就很小,不適合建立索引或不適合排在聯合索引列的靠前的位置,而 uuid 這類字段就比較適合做索引或排在聯合索引列的靠前的位置。


4.4 索引下推優化(ICP)

-- MySQL 5.6+ 自動啟用,減少回表次數
SELECT * FROM employees 
WHERE last_name LIKE '張%' 
AND age > 30;

五、真實場景案例解析

5.1 電商訂單查詢優化

原始SQL:

SELECT * FROM orders 
WHERE status = 2 
AND payment_time BETWEEN '2023-07-01' AND '2023-07-31'
ORDER BY create_time DESC;

優化方案:

  1. 創建聯合索引(status, payment_time, create_time)
  2. 使用覆蓋索引減少回表
  3. 分頁查詢使用WHERE id > ?代替LIMIT深度翻頁

5.2 社交平臺好友推薦

-- 優化前(全表掃描):
SELECT user_id FROM relationships 
WHERE friend_id = 10086 
AND relation_type = 3;-- 優化后(反向索引):
CREATE INDEX idx_reverse_relation 
ON relationships(friend_id, relation_type);

結語

優秀的索引設計需要平衡查詢效率與寫入性能。建議遵循以下原則:

  1. 優先考慮最常用查詢模式
  2. 單表索引不超過5個
  3. 聯合索引字段數不超過3個
  4. 定期審查索引使用情況

通過理解B+Tree的底層原理,結合執行計劃分析與實際業務場景,開發者可以構建出高效的數據訪問方案。記住:沒有最好的索引,只有最適合業務場景的索引設計。

在這里插入圖片描述

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

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

相關文章

《K230 從熟悉到...》識別機器碼(AprilTag)

《K230 從熟悉到...》識別機器碼(aprirltag) tag id 《廬山派 K230 從熟悉到...》 識別機器碼(AprilTag) AprilTag是一種基于二維碼的視覺標記系統,最早是由麻省理工學院(MIT)在2008年開發的。A…

Linux驅動復習

應用層調用內核層函數稱為系統調用 1.硬件設備管理 1,字符設備驅動(一個一個字節)——芯片內部外設 :WDT,Timer,adc,iic,SPI,R,UART,LCD,CAMERA,USB,Keyboard,Mouse 2,塊設備驅動&a…

【FAQ】HarmonyOS SDK 閉源開放能力 —Account Kit(3)

1.問題描述: PC場景,青少年模式系統API不支持嗎? 解決方案: PC場景,青少年模式系統API不支持,另外文檔上的幾個API也不支持。 2.問題描述: 華為一鍵登錄 Beta7本地運行到手機可以拿到匿名手…

【gdutthesis模板】論文標題太長導致換頁問題解決

標題太長導致換頁問題解決方案如下: 調小下方數值即可

SAP學習筆記 - 豆知識18 - (TODO)Msg 番號 ME154 構成品目無法決定

1,現象 構成品目の決定は不可能です Msg 番號 ME154 構成品目無法決定 2,到Deepseek里找一下解決方案 SAP ME21N中錯誤「組件物料的確定不可行(ME154)」的解決步驟 此錯誤在創建分包采購訂單時出現,通常由于系統無…

10.多線程

預備知識 預備知識一 預備知識二 預備知識三 如何理解進程和線程的關系,舉一個生活中的例子 家庭:進程家庭成員:線程 每個家庭成員都會為這個家庭做貢獻,只不過大家都在做不同的事情(比如:我們在上學&…

Python入門(8):文件

1. 文件基本概念 文件:存儲在計算機上的數據集合,Python 通過文件對象來操作文件。 文件類型: 文本文件:由字符組成,如 .txt, .py 二進制文件:由字節組成,如 .jpg, .mp3 2. 文件打開與關閉…

市場交易策略優化與波動管理

市場交易策略優化與波動管理 在市場交易中,策略的優化和波動的管理至關重要。市場價格的變化受多種因素影響,交易者需要根據市場環境動態調整策略,以提高交易的穩定性,并有效規避市場風險。 一、市場交易策略的優化方法 趨勢交易策…

HTTP數據傳輸的幾個關鍵字Header

本文著重針對http在傳輸數據時的幾種封裝方式進行描述。 1. Content-Type(描述body內容類型以及字符編碼) HTTP的Content-Type用于定義數據傳輸的媒體類型(MIME類型),主要分為以下幾類: (一)、?基礎文本類型? text/plain? …

面向教育領域的實時更新RAG系統:核心模塊設計與技術選型實踐指南

目錄 面向教育領域的實時更新RAG系統:核心模塊設計與技術選型實踐指南 一、業務需求分析 二、系統架構設計(核心模塊) 三、核心模塊詳解與技術選型建議 (一)實時更新向量知識庫 (二)教材與…

k8s patch方法更新deployment和replace方法更新deployment的區別是什么

在Kubernetes中,patch 和 replace 方法用于更新資源(如 Deployment),但它們的實現方式和適用場景有顯著差異。以下是兩者的核心區別: 1. 更新范圍 replace 方法 完全替換整個資源配置。需要用戶提供完整的資源定義&…

解決安卓手機WebView無法直接預覽PDF的問題(使用PDF.js方案)

在移動端開發中,通過 webview 組件直接加載PDF文件時,不同平臺的表現差異較大: iOS & 部分安卓瀏覽器:可正常內嵌預覽(依賴系統內置PDF渲染能力) 大多數安卓設備:由于缺乏原生PDF插件&…

基于javaweb的SSM+Maven機房管理系統設計與實現(源碼+文檔+部署講解)

技術范圍:SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬蟲、數據可視化、小程序、安卓app、大數據、物聯網、機器學習等設計與開發。 主要內容:免費功能設計、開題報告、任務書、中期檢查PPT、系統功能實現、代碼編寫、論文編寫和輔導、論文…

7-6 混合類型數據格式化輸入

本題要求編寫程序,順序讀入浮點數1、整數、字符、浮點數2,再按照字符、整數、浮點數1、浮點數2的順序輸出。 輸入格式: 輸入在一行中順序給出浮點數1、整數、字符、浮點數2,其間以1個空格分隔。 輸出格式: 在一行中…

【GPIO8個函數解釋】

函數解釋 void GPIO_DeInit(GPIO_TypeDef* GPIOx); 作用:將指定GPIO端口的所有寄存器恢復為默認值。這會清除之前對該端口的所有配置,使其回到初始狀態。使用方法:傳入要復位的GPIO端口指針,例如GPIOA、GPIOB等。 void GPIO_AF…

將圖表和表格導出為PDF的功能

<template><div><divref"pdfContent"style"position: relative; width: 800px; margin: 0 auto"><!-- ECharts 圖表 --><div id"chart" style"width: 100%; height: 400px" /><!-- Element UI 表格 …

C++中的鏈表操作

在C中&#xff0c;鏈表是一種常見的數據結構&#xff0c;它由一系列節點組成&#xff0c;每個節點包含數據部分和指向下一個節點的指針。C標準庫&#xff08;STL&#xff09;中提供了std::list和std::forward_list兩種鏈表實現&#xff0c;分別對應雙向鏈表和單向鏈表。此外&am…

蛋白設計 ProteinMPNN

傳統方法的局限性是什么&#xff1f; 傳統蛋白質設計方法的局限性&#xff1a; 基于物理的傳統方法&#xff0c;例如羅塞塔&#xff0c;面臨計算難度&#xff0c;因為需要計算所有可能結構的能量&#xff0c;包括不需要的寡聚態和聚合態。 設計目標與顯式優化之間缺乏一致性通…

有哪些開源的視頻生成模型

1. 阿里巴巴通義萬相2.1&#xff08;WanX 2.1&#xff09; 技術架構&#xff1a;基于Diffusion Transformer&#xff08;DiT&#xff09;架構&#xff0c;結合自研的高效變分自編碼器&#xff08;VAE&#xff09;和Flow Matching訓練方案&#xff0c;支持時空上下文建模。參數…

【動態規劃】最長上升子序列模板

最長上升子序列 題目傳送門 一、題目描述 給定一個長度為 N 的數列&#xff0c;求數值嚴格單調遞增的子序列的長度最長是多少。 輸入格式 第一行包含整數 N。 第二行包含 N 個整數&#xff0c;表示完整序列。 輸出格式 輸出一個整數&#xff0c;表示最大長度。 數據范圍 …