什么是聚集索引、非聚集索引、覆蓋索引?


什么是覆蓋索引?

本文為筆者近來學習的筆記,在解釋覆蓋索引之前勢必簡單回顧一下索引基本知識?

索引

索引是數據庫管理系統中一個排序的數據結構,以協助快速查詢、更新數據庫表中數據。通常類比為圖書目錄。

聚集索引與非聚集索引

聚集索引:

聚集索引中鍵值的邏輯順序決定了表中相應行的物理順序,例如電話本,索引為(姓,名),數據值為電話號,在一個表中通常只有一個聚集索引, 聚集索引對於那些經常要搜索范圍值的列特別有效。使用聚集索引找到包含第一個值的行后,便可以確保包含后續索引值的行在物理相鄰。

​ 例如,如果應用程序執行 的一個查詢經常檢索某一日期范圍內的記錄,則使用聚集索引可以迅速找到包含開始日期的行,然后檢索表中所有相鄰的行,直到到達結束日期。這樣有助於提高此類查詢的性能。同樣,如果對從表中檢索的數據進行排序時經常要用到某一列,則可以將該表在該列上聚集(物理排序),避免每次查詢該列時都進行排序,從而節 省成本。
  當索引值唯一時,使用聚集索引查找特定的行也很有效率。

非聚集索引

一種索引,該索引中索引的邏輯順序與磁盤上行的物理存儲順序不同。我們可以這么理解聚簇索引:索引的葉節點就是數據節點。而非聚簇索引的葉節點仍然是索引節點(例如存儲數據存放的地址,而非數據),有一個指針指向對應的數據塊。

​ 我們的漢語字典的正文本身就是一個聚集索引。比如,我們要查“安”字,就會很自然地翻開字典的前幾頁,因為“安”的拼音是“an”,而按照拼音排序漢字的字典是以英文字母“a”開頭並以“z”結尾的,那么“安”字就自然地排在字典的前部。如果您翻完了所有以“a”開頭的部分仍然找不到這個字,那么就說明您的字典中沒有這個字;同樣的,如果查“張”字,那您也會將您的字典翻到最后部分,因為“張”的拼音是“zhang”。也就是說,字典的正文部分本身就是一個目錄,您不需要再去查其他目錄來找到您需要找的內容。我們把這種正文內容本身就是一種按照一定規則排列的目錄稱為“聚集索引”。

​ 如果您認識某個字,您可以快速地從自動中查到這個字。但您也可能會遇到您不認識的字,不知道它的發音,這時候,您就不能按照剛才的方法找到您要查的字,而需要去根據“偏旁部首”查到您要找的字,然后根據這個字后的頁碼直接翻到某頁來找到您要找的字。但您結合“部首目錄”和“檢字表”而查到的字的排序並不是真正的正文的排序方法,比如您查“張”字,我們可以看到在查部首之后的檢字表中“張”的頁碼是672頁,檢字表中“張”的上面是“馳”字,但頁碼卻是63頁,“張”的下面是“弩”字,頁面是390頁。很顯然,這些字並不是真正的分別位於“張”字的上下方,現在您看到的連續的“馳、張、弩”三字實際上就是他們在非聚集索引中的排序,是字典正文中的字在非聚集索引中的映射。我們可以通過這種方式來找到您所需要的字,但它需要兩個過程,先找到目錄中的結果,然后再翻到您所需要的頁碼。我們把這種目錄純粹是目錄,正文純粹是正文的排序方式稱為“非聚集索引”。

索引分類

主鍵索引(PRIMARY),以主鍵列作為索引,最常用的索引。

普通索引(INDEX):以普通列作為索引,唯一任務是加快對數據的訪問速度,因此,應該只為那些最經常出現在查詢 條件(WHERE column=)或者排序條件(ORDERBY column)中的數據列創建索引。

唯一索引(UNIQUE):以唯一值作為索引,目的往往不是為了提高訪問速度,而只是為了避免數據出現重復。

組合索引(Composite):多列組合起來作為索引,例如前文中提到的電話簿,就是以姓+名的形式作為索引

覆蓋索引前因:回表

​ InnoDB引用的是B+樹索引模型,前文中對索引的種類划分為兩大類:主鍵(聚集)索引和非聚集索引,那么問題就在於比較兩種索引的區別了,我們這里建立一張學生表,其中包含字段id設置主鍵索引、name設置普通索引、age(無處理),並向數據庫中插入4條數據:("小趙", 10)("小王", 11)("小李", 12)("小陳", 13)。

CREATE TABLE `student` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '自增主鍵',
  `name` varchar(32) COLLATE utf8_bin NOT NULL COMMENT '名稱',
  `age` int(3) unsigned NOT NULL DEFAULT '1' COMMENT '年齡',
  PRIMARY KEY (`id`),
  KEY `I_name` (`name`)
) ENGINE=InnoDB;

INSERT INTO student (name, age) VALUES("小趙", 10),("小王", 11),("小李", 12),("小陳", 13);

此時表中的數據:

mysql> select * from student;
+----+------+-----+
| id | name | age |
+----+------+-----+
|  1 | 小趙 |  10 |
|  2 | 小王 |  11 |
|  3 | 小李 |  12 |
|  4 | 小陳 |  13 |
+----+------+-----+
4 rows in set (0.00 sec)

每一個索引在 InnoDB 里面對應一棵B+樹,那么此時就存着兩棵B+樹:

可以發現區別在與葉子節點中,主鍵索引存儲了整行數據,而非主鍵索引中存儲的值為主鍵id

當我們執行以下語句:

select age from student where name = '小李';

執行順序:

  1. 在name索引樹上找到名稱為小李的節點 id為03
  2. 從id索引樹上找到id為03的節點 獲取所有數據
  3. 從數據中獲取字段命為age的值12,返回.

這樣從非主鍵索引樹搜索再回到主鍵索引樹搜索的過程稱為:回表

回表一定程度上消耗性能,那么如何降低這種性能損耗呢?於是提出了一種方法:覆蓋索引.

覆蓋索引

覆蓋索引(covering index ,或稱為索引覆蓋)即從非主鍵索引中就能查到的記錄,而不需要查詢主鍵索引中的記錄,避免回表的產生減少了樹的搜索次數,顯著提升性能。

覆蓋索引的使用

如果一個業務中,很多類似於根據姓名查找年齡的業務,那么可以將這些熱點業務重新根據(name , age)建立聯合索引,先刪除之前以name構建的索引:

ALTER TABLE student DROP INDEX I_name;
ALTER TABLE student ADD INDEX I_name_age(name, age);

聯合索引:

再次執行如下sql:

select age from student where name = '小李';

執行流程:

  1. 在name,age聯合索引樹上找到名稱為小李的節點。
  2. 此時節點索引里包含信息age直接返回 12,從而避免回表。

如何確定使用的是覆蓋索引還是主鍵索引呢?

當發起一個索引覆蓋查詢時,在explain的extra列可以看到using index的信息:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM