索引背後的數據結構和算法原理

提問:常見索引有哪些?

1、從數據結構角度
* B-Tree/B+Tree索引:B-Tree/B+Tree簡介。
* Hash索引:1、查詢效率非常高,一次查詢即可;2、僅能滿足=、in,不能用於範圍查詢;3、* 只有memory引擎顯示支持。
* 全文索引:用於查找文本中的關鍵詞,InnoDB和MyISAM都支持。
* R-Tree索引:用於對GIS數據類型創建SPATIAL索引。

2、從物理存儲角度
* 聚集索引:InnoDB的B+Tree索引,葉子節點保存數據,索引和數據都存在數據文件裏,根據主鍵聚集。
* 非聚集索引:MyISAM的B+Tree索引,葉子節點保存具體數據的地址,索引和數據分別存在兩個文件裏,數據不聚集。

3、從邏輯角度
* 主鍵索引:一種特殊的唯一索引,不允許有空值。
普通索引或者單列索引。
* 多列索引(複合索引):在多個字段上創建的索引,只有在查詢條件中使用了創建索引時的第一個字段,索引纔會被使用。使用複合索引時遵循最左前綴集合。
* 唯一索引或者非唯一索引。
* 空間索引:空間索引是對空間數據類型的字段建立的索引,MYSQL中的空間數據類型有4種,分別是GEOMETRY、POINT、LINESTRING、POLYGON。

1、索引的本質

MySQL官方對索引的定義爲:索引(Index)是幫助MySQL高效獲取數據的數據結構。提取句子主幹,就可以得到索引的本質:索引是數據結構

我們知道,數據庫查詢是數據庫的最主要功能之一。我們都希望查詢數據的速度能儘可能的快,因此數據庫系統的設計者會從查詢算法的角度進行優化。最基本的查詢算法當然是順序查找(linear search),這種複雜度爲O(n)的算法在數據量很大時顯然是糟糕的,當然我們也有很多更優秀的查找算法,例如二分查找(binary search)、二叉樹查找(binary tree search)等。如果稍微分析一下會發現,每種查找算法都只能應用於特定的數據結構之上,例如二分查找要求被檢索數據有序,而二叉樹查找只能應用於二叉查找樹上,但是數據本身的組織結構不可能完全滿足各種數據結構(例如,理論上不可能同時將兩列都按順序進行組織),所以,在數據之外,數據庫系統還維護着滿足特定查找算法的數據結構,這些數據結構以某種方式引用(指向)數據,這樣就可以在這些數據結構上實現高級查找算法。這種數據結構,就是索引。

image.png

2、B-Tree和B+Tree

目前大部分數據庫系統及文件系統都採用B-Tree或其變種B+Tree作爲索引結構,所以本小節先簡單介紹一下這兩種數據結構。

2.1、B-Tree

  • d爲大於1的一個正整數,稱爲B-Tree的度。
  • h爲一個正整數,稱爲B-Tree的高度。
  • 每個非葉子節點由n-1個key和n個指針組成,其中d<=n<=2d。
  • 每個葉子節點最少包含一個key和兩個指針,最多包含2d-1個key和2d個指針,葉節點的指針均爲null 。
  • 所有葉節點具有相同的深度,等於樹高h。
  • key和指針互相間隔,節點兩端是指針。
  • 一個節點中的key從左到右非遞減排列。

image.png

2.2、B+Tree

B-Tree的變種,與B-Tree相比有以下不同點:

  • 每個節點的指針上限爲2d而不是2d+1。
  • 內節點不存儲data,只存儲key;葉子節點不存儲指針。

image.png

2.3、帶順序訪問指針的B+Tree

一般在數據庫系統或文件系統中使用的B+Tree結構都在經典B+Tree的基礎上進行了優化,增加了順序訪問指針。

image.png

如圖所示,在B+Tree的每個葉子節點增加一個指向相鄰葉子節點的指針,就形成了帶有順序訪問指針的B+Tree。做這個優化的目的是爲了提高區間訪問的性能,例如圖4中如果要查詢key爲從18到49的所有數據記錄,當找到18後,只需順着節點和指針順序遍歷就可以一次性訪問到所有數據節點,極大提到了區間查詢效率。

3、爲什麼使用B-/+Tree

3.1、主存的存取原理

  • 讀取主存時,將地址信號放到地址總線上傳給主存,主存讀到地址信號後,解析信號並定位到指定存儲單元,然後將此存儲單元數據放到數據總線上,供其它部件讀取。
  • 寫主存時,系統將要寫入單元地址和數據分別放在地址總線和數據總線上,主存讀取兩個總線的內容,做相應的寫操作。

這裏可以看出,主存存取的時間僅與存取次數呈線性關係,因爲不存在機械操作,兩次存取的數據的“距離”不會對時間有任何影響,例如,先取A0再取A1和先取A0再取D3的時間消耗是一樣的。

image.png

3.2、磁盤的存取原理

當需要從磁盤讀取數據時,系統會將數據邏輯地址傳給磁盤,磁盤的控制電路按照尋址邏輯將邏輯地址翻譯成物理地址,即確定要讀的數據在哪個磁道,哪個扇區。爲了讀取這個扇區的數據,需要將磁頭放到這個扇區上方,爲了實現這一點,磁頭需要移動對準相應磁道,這個過程叫做尋道,所耗費時間叫做尋道時間,然後磁盤旋轉將目標扇區旋轉到磁頭下,這個過程耗費的時間叫做旋轉時間。

由於存儲介質的特性,磁盤本身存取就比主存慢很多,再加上機械運動耗費,磁盤的存取速度往往是主存的幾百分分之一,因此爲了提高效率,要儘量減少磁盤I/O。爲了達到這個目的,磁盤往往不是嚴格按需讀取,而是每次都會預讀,即使只需要一個字節,磁盤也會從這個位置開始,順序向後讀取一定長度的數據放入內存。這樣做的理論依據是計算機科學中著名的局部性原理:當一個數據被用到時,其附近的數據也通常會馬上被使用。

image.png

3.3、B-/+Treed的性能分析

一般使用磁盤I/O次數評價索引結構的優劣。根據B-/+Tree的定義,可知檢索一次最多需要訪問h個節點。數據庫系統的設計者巧妙利用了磁盤預讀原理,將一個節點的大小設爲等於一個頁,這樣每個節點只需要一次I/O就可以完全載入。爲了達到這個目的,在實際實現B-Tree還需要使用如下技巧:

  • 每次新建節點時,直接申請一個頁的空間,這樣就保證一個節點物理上也存儲在一個頁裏,加之計算機存儲分配都是按頁對齊的,就實現了一個node只需一次I/O。
  • B-/+Tree中一次檢索最多需要h-1次I/O(根節點常駐內存),漸進複雜度爲O(h)=O(logdN)。一般實際應用中,出度d是非常大的數字,通常超過100,因此h非常小(通常不超過3)。

綜上所述,用B-Tree作爲索引結構效率是非常高的。

而紅黑樹這種結構,h明顯要深的多。由於邏輯上很近的節點(父子)物理上可能很遠,無法利用局部性,所以紅黑樹的I/O漸進複雜度也爲O(h),效率明顯比B-Tree差很多。

爲什麼mysql的索引使用B+樹而不是B樹呢?

  • B+樹更適合外部存儲(一般指磁盤存儲),由於內節點(非葉子節點)不存儲data,所以一個節點可以存儲更多的內節點,每個節點能索引的範圍更大更精確。也就是說使用B+樹單次磁盤IO的信息量相比較B樹更大,IO效率更高。
  • mysql是關係型數據庫,經常會按照區間來訪問某個索引列,B+樹的葉子節點間按順序建立了鏈指針,加強了區間訪問性,所以B+樹對索引列上的區間範圍查詢很友好。而B樹每個節點的key和data在一起,無法進行區間查找。

4、Mysql索引的實現原理

4.1、MyISAM實現原理

MyISAM中索引檢索的算法爲首先按照B+Tree搜索算法搜索索引,如果指定的Key存在,則取出其data域的值,然後以data域的值爲地址,讀取相應數據記錄。

  • MyISAM的索引文件僅僅保存數據記錄的地址。
  • MyISAM的索引方式也叫做“非聚集”的,之所以這麼稱呼是爲了與InnoDB的聚集索引區分。

image.png

4.2、InnoDB實現原理

雖然InnoDB也使用B+Tree作爲索引結構,但具體實現方式卻與MyISAM截然不同。

一個比較大區別是InnoDB的數據文件本身就是索引文件。從上文知道,MyISAM索引文件和數據文件是分離的,索引文件僅保存數據記錄的地址。而在InnoDB中,表數據文件本身就是按B+Tree組織的一個索引結構,這棵樹的葉節點data域保存了完整的數據記錄。這個索引的key是數據表的主鍵,因此InnoDB表數據文件本身就是主索引。

image.png

上圖是InnoDB主索引(同時也是數據文件)的示意圖,可以看到葉節點包含了完整的數據記錄。這種索引叫做聚集索引。因爲InnoDB的數據文件本身要按主鍵聚集,所以InnoDB要求表必須有主鍵(MyISAM可以沒有),如果沒有顯式指定,則MySQL系統會自動選擇一個可以唯一標識數據記錄的列作爲主鍵,如果不存在這種列,則MySQL自動爲InnoDB表生成一個隱含字段作爲主鍵,這個字段長度爲6個字節,類型爲長整形。

5、總結

  1. 索引的本質:數據結構。
  2. 索引普遍使用的數據結構:B-Tree和B+Tree。
  3. 爲什麼使用B-Tree和B+Tree:具有更好的性能。
  4. Mysql索引的實現原理:B+Tree(MyISAM、InnoDB)。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章