1. 程式人生 > 資料庫 >《MySQL慢查詢優化》之索引原理

《MySQL慢查詢優化》之索引原理

1、前言

  • 什麼是索引?

  在關係型資料庫中(RDBMS),索引是種獨立的對資料庫表中的一列或多列的值進行排序的一種資料結構。索引類似於書籍的目錄,可以根據目錄中的頁碼(地址)快速查詢書籍某頁內容(資料)。

因此,索引是建立在列上的資料結構,是幫助MySQL高效獲取資料的資料結構。在RDBMS(Relational Database Management System)中,索引儲存在硬碟中。

 

  • MySQL索引有哪些資料結構?

  在MySQL中,主要有四種資料結構的索引,分別為: B-Tree 索引, Hash 索引, Fulltext 索引和 R-Tree 索引。其中,常見為HASH索引和BTREE索引。索引屬於儲存引擎級別的概念,不同儲存引擎對索引的實現方式是不同的,其中MyISAM和InnoDB兩個儲存引擎的索引預設實現方式均為BTREE索引,MEMORY預設使用雜湊索引。

  HASH索引顧名思義是由雜湊演算法實現,該索引存放在記憶體中,Hash衝突解決繁瑣,並且對範圍查詢支援不友好,優點在於記憶體處理速度快。MySQL儲存引擎MEMORY天然支援HASH索引(MEMORY是MySQL中一類特殊的儲存引擎。它使用儲存在記憶體中的內容來建立表,而且資料全部儲存在記憶體中。),雜湊索引查詢速度比使用BTREE索引快。MySQL儲存引擎InnoDB不能手動建立HASH索引,只能支援自適應HASH。

  BTREE索引是由B+樹資料結構實現。高版本的MySQL預設儲存引擎為InnoDB,本文主要討論InnoDB和MyISAM兩種儲存引擎的BTREE索引。

 

2、BTREE索引

  • B-Tree

  B-Tree又稱多路搜尋樹、多叉平衡查詢樹,解決了平衡二叉查詢樹(AVL樹)的樹高等缺陷,降低I/O成本。 

 資料結構:

一棵m階的B-Tree有如下特性:

。樹中每個結點最多含有m個孩子(m>=2);
。除根結點和葉子結點外,其它每個結點至少有[ceil(m / 2)]個孩子(其中ceil(x)是一個取上限的函式);
。若根結點不是葉子結點,則至少有2個孩子;
。所有葉子結點都出現在同一層,葉子結點不包含任何關鍵字資訊;
。每個非終端結點中包含有n個關鍵字資訊: (P1,K1,P2,K2,P3,......,Kn,Pn+1)。其中:
  a) Ki (i=1...n)為關鍵字,且關鍵字按順序升序排序K(i-1)< Ki。

  b) Pi為指向子樹根的接點,且指標P(i)指向子樹種所有結點的關鍵字均小於Ki,但都大於K(i-1)。
  c) 關鍵字的個數n必須滿足: [ceil(m / 2)-1]<= n <= m-1。

B樹主要應用於檔案系統以及部分資料庫索引,如MongoDB。

 

  • B+Tree

  B+Tree是在B-Tree基礎上的一種優化,大部分關係型資料庫主要索引都是使用B+樹實現。

資料結構:

B+Tree相比於B-Tree特點:

。B+節點關鍵字搜尋採用閉合區間。
。B+非葉節點不儲存資料相關資訊,只儲存關鍵字和子節點的引用。
。B+關鍵字對應的資料儲存在葉子節點中。(B+tree的葉子節點要儲存所有的key-value)
。B+葉子節點是順序排列的,所有葉子節點通過鏈指標連線,形成迴圈雙向連結串列。

 

B+Tree優點:

。B+樹是B-樹的變種(優化版),他擁有B-樹的優勢。(自平衡、樹高低,I/O少)
。B+樹掃庫掃表能力更強。(葉子節點存放所有資料,並且是雙向連結串列資料結構)
。B+樹的磁碟讀寫能力更強。(非葉子節點不存放資料,磁碟塊存放更多的索引資訊,I/O更少)
。B+樹的排序能力更強。(葉子節點順序排列)
。B+樹的查詢效率更加穩定,I/O更加穩定。(資料都在葉子節點命中)

 

  • BTREE索引

  在MySQL中,索引屬於儲存引擎級別的概念。其中,InnoDB和MyISAM兩種儲存引擎的大部分索引均採用B+Tree,但是具體實現細節截然不同。

MyISAM

  MyISAM引擎使用B+Tree作為索引結構,葉子節點的data域儲存資料記錄的地址,因此,MyISAM的索引檔案僅僅儲存資料記錄的地址,MyISAM索引也歸類為“非聚集”索引(索引檔案和資料檔案分離)。主索引和輔助索引(Secondary key)在結構上沒有任何區別,只是主索引要求key是唯一的,而輔助索引的key可以重複。

主鍵索引(Primary key):(Col1為主鍵列)

 

輔助索引(Secondary key):(Col2為輔助索引列)

InnoDB

  InnoDB索引屬於聚簇型索引,也就是說索引和資料存放一起。InnoDB的資料檔案本身要按主鍵聚集,所以InnoDB要求表必須有主鍵(MyISAM可以沒有),相比於MyISAM,有兩大區別:

<1> 主鍵索引,InnoDB表資料檔案本身就是主索引。換句話說,InnoDB表資料檔案本身就是按B+Tree組織的主鍵索引檔案,這棵樹的葉節點data域儲存了完整的資料記錄,索引樹的key是資料表的主鍵。

<2> 輔助索引(包括單列索引、聯合索引),InnoDB輔助索引的data域儲存相應資料記錄的主鍵值而不是地址。換句話說,InnoDB的所有輔助索引都引用主鍵作為data域。採用輔助索引查詢完整資料記錄,必須先檢索輔助索引獲取記錄主鍵,再回表檢索主鍵索引獲取資料記錄。

主鍵索引(Primary key):(Col1為主鍵列)

輔助索引(Secondary key):(Col2為輔助索引列)

通過輔助索引檢索完整記錄資料流程:(回表操作)

 

  掌握了InnoDB的索引實現後,就很容易明白為什麼不建議使用過長的欄位作為主鍵,因為所有輔助索引都引用主索引,過長的主索引會令輔助索引變得過大。另外,優先考慮自增主鍵。因為InnoDB資料檔案本身是一顆B+Tree,非單調的主鍵會造成在插入新記錄時資料檔案為了維持B+Tree的特性而頻繁的分裂調整,十分低效,而使用自增欄位作為主鍵則是一個很好的選擇。

 

3、覆蓋索引

  • 定義

  SQL執行只需通過檢索索引樹就能返回查詢所需要的記錄資料,而不必先通過檢索輔助索引獲取記錄主鍵值,再去主鍵索引樹檢索完整記錄資料(回表操作)。覆蓋索引是SQL優化推崇的最優策略之一,實際上就是根據InnoDB索引實現原理,最優化使用索引。

由於輔助索引儲存的資料比聚集索引少很多,大部分情況下,會落在記憶體中,聚集索引很可能資料在磁碟中,通過輔助索引進行覆蓋索引,都不需要讀磁碟,直接從記憶體取,這種情況下,一種是記憶體讀,一種是磁碟讀,速度差異很顯著,幾乎是數量級的差異。

本人另一篇博文,對SQL優化以及索引策略詳細進行介紹,現採用其中的一條SQL進行示例。

測試資料表:

 show index from employees;

輔助索引:idx_empno_birthdate_gender(emp_no,birth_date,gender)(聯合索引)。

EG:

EXPLAIN SELECT emp_no,birth_date,gender FROM employees WHERE  gender  ='M' ;

 Using index:表示已經使用了覆蓋索引。

 

4、聯合索引

  • 定義

   聯合索引又稱複合索引,歸類於輔助索引,是由表中若干列組合而成,類似於書籍的多級目錄結構,遵循“最左字首原則”。

InnoDB引擎中,根據輔助索引實現原理,葉子節點存放聯合索引列(多列)和主鍵列數值,非葉子節點儲存所有索引列數值,而不僅僅是索引第一列。

示例:index(a,b,c)

檢索順序:按照最左原則,從左列開始逐步向右列檢索判斷。

  • 優勢

。降低開銷:建立一個多列的聯合索引,相當於建立了多個子索引。

。覆蓋索引:提供了覆蓋索引的可能性。

。效率高效:檢索過濾條件多,一次性篩選出更少的資料記錄,避免不必要的回表、分頁、排序等操作。

 

5、索引分類

  MySQL目前根據使用場景,主要有以下幾種索引型別:

  • 普通索引
  • 唯一索引
  • 主鍵索引
  • 組合索引
  • 全文索引

另外,聚簇性索引(主鍵索引)和非聚簇性索引(輔助索引)是根據索引實現原理分類。

 

6、索引操作

常用的索引操作:

索引操作 DDL語句
建立
  • 普通索引:CREATE INDEX index_name ON table(column(length));
  • 唯一索引:CREATE UNIQUE INDEX index_name ON table(column(length)) ;
  • 聯合索引:CREATE INDEX index_name ON table(column1(length),column2(length));

其中,length可以不指定,直接寫列名。

修改表方式:ALERT TABLE table_name ADD INDEX (UNIQUE INDEX)...

刪除 DROP INDEX index_name ON table_name;
檢視 SHOW INDEX FROM table_name;

 

結束語

  目前,本人並不是理論原理的創造者,只是通過相關書籍以及網路知識的學習,並結合自身的掌握與工作實踐,整理編寫了本文,希望能為更多的人提供幫助,文章中部分原理圖來源於網路。