Mysql中的索引


索引是數據庫優化中最常用也最重要的手段之一,通過索引可以解決大多數的sql性能問題。在mysql中,索引是在存儲引引擎層而不是服務器層實現的,所以,並沒有統一的索引標准:不同的存儲引擎的索引的工作方式並不一樣,也不是所有的存儲引擎都支持所有類型的索引。即使多個存儲引擎支持同一種類型的索引,其底層的實現也可能不通過。

一、索引的分類

InnoDB存儲引擎支持以下幾種常見的索引:

  • B+樹索引:我們通常在討論索引的時候,如果沒有特別指明,通常說的就是B+樹索引。MyISAM和InnoDB存儲引擎的表默認創建的都是B+樹索引。B+樹索引中的B不代表二叉樹(binary),而是代表平衡樹(balanced)。B+樹索引並不是一顆二叉樹。
  • Hash索引:InnoDB存儲引擎是支持hash索引的,並且是自適應的,InnoDB存儲引擎會根據表的使用情況自動為表生成創建hash索引,不能人為干預是否在表中創建hash索引
  • 全文索引:InnoDB從Mysql5.6版本開始提供對全文索引的支持。僅限於CHAR、VARCHAR和TEXT列。全文索引只能用於英文,如果出現中文,還是應該使用Lucene等第三方開源框架來處理。

Mysql官方文檔Introduction to InnoDB中圖表已經表明了InnoDB存儲引擎並不支持Hash索引,但InnoDB內部會使用自適應Hash索引來進行字典的查找,但僅僅是內部使用,並不能手動進行干預。

二、B+樹索引

B+樹是由二叉查找樹(BST),再由平衡二叉樹(AVL),B樹演化而來,這幾種樹的知識可以參考排序二叉樹、平衡二叉樹、紅黑樹、B樹、B+樹。B+樹的操作可以參考B+樹的插入和刪除操作

1.為什么數據庫索引使用B+樹結構,而不選擇Hash結構或其它樹結構?

索引為什么不用Hash結構?

我們可以看一下Mysql參考官方文檔中B樹索引和Hash索引的對比:8.3.8 Comparison of B-Tree and Hash Indexes,其中Hash索引的特點中已經說的很清楚了:

  • 對於等值查找非常快,但不適合等范圍比較操作,如<,between等。
  • 不適合順序查找。(優化器不能使用Hash索引進行ORDER BY操作)
  • MySQL不能判斷兩個值間大致有多少行記錄(范圍優化器會根據這來決定選用哪個索引)
  • 不適合前綴匹配查找

所以Hash索引不適合作為數據庫索引。不適合並不表示數據庫不支持,實際上Mysql是支持Hash索引的,其Memory存儲引擎就支持Hash索引,前面也提到了InnoDB內部會使用自適應Hash索引來進行字典的查找。

索引為什么不用其它樹型結構?

普通的二叉樹雖然查找時間為O(logN),但極端條件下會退化到O(N)。

平衡二叉樹(AVL樹和紅黑樹)解決了極端條件下查找效率低的問題,但無論如何,二叉的結構決定了樹的高度太大,導致IO次數過度,不太適合。

所以只有平衡的多路查找樹才比較適合,其中由B樹和B+樹的不同特點,決定了B+樹才適合作為數據庫的索引。

①磁盤IO

一般來說,索引本身也很大,不可能全部存儲在內存中,因此索引往往以索引文件的形式存儲的磁盤上。這樣的話,索引查找過程中就要產生磁盤I/O消耗,所以索引的組織結構要盡量減少查找過程中磁盤I/O的存取次數。B樹的節點既要存索引信息,又要存數據,與B+樹相比,在相同數據量下,B樹相對需要更多次的IO操作。

②查找方式

數據庫需要經常進行范圍查找,B+由於其數據都按照順序保存在葉子節點中,且相互間有指針相連。這樣的特點非常適合順序查找和范圍查找。

2.OLTP和OLAP應用中B+樹索引的使用

應用程序分為兩種:OLTP和OLAP。OLTP應用中,查詢操作只從數據庫中取得一小部分數據,一般在10條記錄以內,甚至很多時候只取一條記錄,如根據主鍵獲取用戶信息,根據訂單號取得訂單詳細信息,這都是典型的OLTP應用。在這種情況下,B+樹索引建立后,對該索引的使用應該只是通過該索引取得表中少部分數據。這時建立B+樹索引才是有意義的,否則即使建立了,優化器也可能選擇不適用索引。

而對於OLAP應用,則需要訪問表中大量的數據,根據這些數據來產生查詢的結果,這些查詢多是面向分析的查詢,目的是為決策者提供支持。如本月每個用戶的消費情況,銷售額同比、環比增長情況等。這些復雜的查詢要涉及到多張表之間的連接操作,因此索引的添加依然是有意義的。但是,如果連接操作使用的Hash Join,那么所以可能又變得不是非常重要。這就需要仔細研究自己的應用了。OLAP應用通常會需要對時間字段進行索引,這是因為大多數統計需要根據時間維度來進行數據的篩選。

三、聚集索引和輔助索引

數據庫中的B+樹索引可以分為聚集索引輔助索引(非聚集索引)無論是聚集的還是輔助的,其內部都是B+樹,即高度平衡的,葉子存放着所有的數據。聚集索引與輔助索引不同的是,葉子節點存放的是否是一整行的信息。

1.聚集索引(clustered index)

官方文檔:14.6.2.1 Clustered and Secondary Indexes

聚集索引,又稱為聚簇索引,它並不是一種單獨的索引類型,而是一種數據存儲方式。每張InnoDB表都有一個聚集索引,表中的數據行就存儲在聚集索引中。通常來講,聚集索引和主鍵具有相同的含義,但是並不總是如此。

  • 如果表顯式的定義了主鍵,InnoDB就會將該主鍵作為聚集索引
  • 如果表沒有顯式的定義主鍵,則會使用第一個非NULL的唯一索引作為聚集索引。非空指的是該唯一索引中的所有key都是非空的。
  • 如果沒有顯式的定義主鍵,也沒有合適的唯一索引,InnoDB就會隱式的創建一個6位的主鍵自增列,並以該列來創建名為GEN_CLUST_INDEX的聚集索引。該自增列中會記錄當前行的行ID,每插入一行記錄,對應行的行ID就會自增1,因此行的排列順序就是行的物理插入順序。

聚集索引是如何加速查詢速度的

通過聚集索引來查詢時,最終索引到的頁中就直接存儲了所有的行記錄,無需再進行額外的查詢操作,所以大大提升了查詢效率。當表非常大時,與未使用聚集索引的索引結構相比,則會節省磁盤IO。

2.輔助索引(非聚集索引,nonclustered index)

輔助索引,也稱為非聚集索引或二級索引,除聚集索引以外的所有索引都稱為輔助索引。在InnoDB中,輔助索引中的每個記錄都包含了該行的主鍵列,以及為輔助索引指定的列。InnoDB使用此主鍵值在聚集索引中搜索行,我們稱之為回表

3.兩者的區別

非聚集索引和聚集索引的區別在於:

由於聚集索引的葉子節點存放的是完整的行記錄信息,而非聚集索引的葉子節點存放的是所要查找的行記錄的主鍵值。所以通過聚集索引可以一次查到需要查找的數據,而通過非聚集索引第一次只能查到記錄對應的主鍵值,需要再次使用主鍵的值通過聚集索引查找才能到需要的數據。

聚集索引一張表只能有一個,而非聚集索引一張表可以有多個。

四、聯合索引和覆蓋索引

1.聯合索引

官方文檔:8.3.5 Multiple-Column Indexes

聯合索引是在表上的多個列進行索引。比如在如下的表中創建一個聯合索引(a,b)

CREATE TABLE t(
    a INT,
    b INT,
    PRIMARY KEY (a),   #主鍵索引
    KEY idx_a_b (a,b)  #聯合索引
)ENGINE=INNODB

聯合索引的B+樹如下。通過葉子節點可以邏輯上順序的讀出所有數據,(1,1),(1,2),(2,1),(2,4),(3,1),(3,2)

能夠使用聯合索引的情況

#全匹配
select * from t where a=xxx and b=xxx
#最左前綴匹配。
select * from t where a=xxx

不能使用聯合索引的情況

#不能使用聯合索引。葉子節點上b的值為1,2,1,4,1,2,顯然不是排序的。
select * from t where b=xxx

同理,如果建立(a,b,c)索引,則下面的查詢都能用到索引。

select * from t where a=xxx and b=xxx and c=xxx
select * from t where a=xxx and b=xxx select * from t where a=xxx select * from t where b=xxx and c=xxx

聯合索引可對第二個列進行排序處理,減少一次filesort。

在聯合索引(a,b)中,由於a相同的情況下b本來就是排序的,所以下面的查詢能夠用到(a,b)索引,且不需要額外再進行排序。

select * from t where a=xxx order by b

同理,如果建立(a,b,c)索引,下面的查詢也能少一次fileSort。

select * from t where a=xxx and b=xxx order by c
select * from t where b=xxx order by c
select * from t where a=xxx order by b

2.索引的列順序問題

聯合索引的列順序非常重要,列的順序能夠決定一個索引的好壞。

對於如何選擇索引的列順序有一個經驗法則:將選擇性最高的列放到索引最前列。當然這只是一個建議,場景不同選擇也不同,並沒有一個放之四海接皆准的法則。 

當不需要考慮排序和分組時,將選擇性最高的列放在前面通常是很好的。這時索引的作用只是用於優化WHERE條件的查找,這種情況下,設計的索引確實能夠最快地過濾出需要的行,對於在WHERE字句中只使用了索引部分前綴列的查詢來說選擇性也更高。

更具體的例子和詳細說明可以參考《高性能mysql》5.3.4 P159多列索引章節

3.覆蓋索引

要查詢的所有列都被包含在一個索引中,稱為覆蓋索引,此時索引將大大提高查詢性能。

並不是所有類型的索引都可以成為覆蓋索引。覆蓋索引必須存儲索引列的值,而hash索引、全文索引等都不存儲索引列的值,所以Mysql只能使用B+樹索引做覆蓋索引。當發起一個被索引覆蓋的查詢時,在explain的extra列中可以看到Using index的信息。

更具體的例子和詳細說明可以參考《高性能mysql》5.3.6 P159覆蓋索引章節

4.能夠使用索引和索引失效的典型場景

參考能夠使用索引和索引失效的典型場景

 

五、更多

《高性能mysql》第5章

官方文檔:

8.3 Optimization and Indexes

 

 

面試題:

1.索引的優點?缺點?

2.不適合創建索引的場景?

對於非常小的表,大部分情況下簡單的掃描更高效。

3.索引的原理?

4.索引為什么使用B+樹數據結構,而不選擇Hash或者紅黑樹數據結構?

5.聚集索引和非聚集索引的區別?

6.能有效利用索引的情況和索引失效的情況?

需要注意的是:B+樹索引並不能找到一個給定鍵值的具體行,B+樹索引能找到的只是被查找數據行所在的頁。然后數據庫通過把頁讀入到內存,再在內存中進行查找,最后得到要查找的數據。

7.索引中為什么不能有NULL值?

8.什么是索引覆蓋?

 


免責聲明!

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



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