數據庫索引實現

數據庫(Mysql)索引實現

索引定義

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

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

B-Tree和B+Tree

目前大部分數據庫系統及文件系統都採用B-Tree或其變種B+Tree作爲索引結構。

B-Tree

在這裏插入圖片描述
由於B-Tree的特性,在B-Tree中按key檢索數據的算法非常直觀:首先從根節點進行二分查找,如果找到則返回對應節點的data,否則對相應區間的指針指向的節點遞歸進行查找,直到找到節點或找到null指針,前者查找成功,後者查找失敗。

另外,由於插入刪除新的數據記錄會破壞B-Tree的性質,因此在插入刪除時,需要對樹進行一個分裂、合併、轉移等操作以保持B-Tree性質。

B+Tree

在這裏插入圖片描述
B-Tree有許多變種,其中最常見的是B+Tree,MySQL就普遍使用B+Tree實現其索引結構。

與B-Tree相比,B+Tree有以下不同點:

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

由於並不是所有節點都具有相同的域,因此B+Tree中葉節點和內節點一般大小不同。這點與B-Tree不同,雖然B-Tree中不同節點存放的key和指針可能數量不一致,但是每個節點的域和上限是一致的,所以在實現中B-Tree往往對每個節點申請同等大小的空間。
一般來說,B+Tree比B-Tree更適合實現外存儲索引結構。

在這裏插入圖片描述
一般在數據庫系統或文件系統中使用的B+Tree結構都在經典B+Tree的基礎上進行了優化,增加了順序訪問指針。
在B+Tree的每個葉子節點增加一個指向相鄰葉子節點的指針,就形成了帶有順序訪問指針的B+Tree。做這個優化的目的是爲了提高區間訪問的性能,例如要查詢key爲從18到49的所有數據記錄,當找到18後,只需順着節點和指針順序遍歷就可以一次性訪問到所有數據節點,極大提到了區間查詢效率。

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

爲什麼使用B-Tree(B+Tree)
紅黑樹等數據結構也可以用來實現索引,但是文件系統及數據庫系統普遍採用B-/+Tree作爲索引結構。

一般來說,索引本身也很大,不可能全部存儲在內存中,因此索引往往以索引文件的形式存儲的磁盤上。這樣的話,索引查找過程中就要產生磁盤I/O消耗,相對於內存存取,I/O存取的消耗要高几個數量級,所以評價一個數據結構作爲索引的優劣最重要的指標就是在查找過程中磁盤I/O操作次數的漸進複雜度。換句話說,索引的結構組織要儘量減少查找過程中磁盤I/O的存取次數。

爲了提高效率,要儘量減少磁盤I/O,爲了達到這個目的,磁盤按需讀取,要求每次都會預讀的長度一般爲頁的整數倍。而且數據庫系統將一個節點的大小設爲等於一個頁,這樣每個節點只需要一次I/O就可以完全載入。每次新建節點時,直接申請一個頁的空間,這樣就保證一個節點物理上也存儲在一個頁裏,加之計算機存儲分配都是按頁對齊的,就實現了一個node只需一次I/O。

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

爲什麼B+Tree比B-Tree更適合數據庫索引

  • 不同於B-Tree只適合隨機檢索,B+Tree同時支持隨機檢索和順序檢索;
  • B+Tree的磁盤讀寫代價更低。B+Tree的內部結點並沒有指向關鍵字具體信息的指針,其內部結點比B-Tree小,盤塊能容納的結點中關鍵字數量更多,一次性讀入內存中可以查找的關鍵字也就越多,相對的,IO讀寫次數也就降低了。而IO讀寫次數是影響索引檢索效率的最大因素。
  • B+Tree的查詢效率更加穩定。B-Tree搜索有可能會在非葉子結點結束,越靠近根節點的記錄查找時間越短,只要找到關鍵字即可確定記錄的存在,其性能等價於在關鍵字全集內做一次二分查找。而在B+Tree中,順序檢索比較明顯,隨機檢索時,任何關鍵字的查找都必須走一條從根節點到葉節點的路,所有關鍵字的查找路徑長度相同,導致每一個關鍵字的查詢效率相當。
  • 數據庫索引採用B+Tree的主要原因是,B-樹在提高了磁盤IO性能的同時並沒有解決元素遍歷的效率低下的問題。B+Tree的葉子節點使用指針順序連接在一起,只要遍歷葉子節點就可以實現整棵樹的遍歷。而且在數據庫中基於範圍的查詢是非常頻繁的,而B-Tree不支持這樣的操作(或者說效率太低)
  • 增刪節點時,B+Tree效率更高,B+Tree的葉子節點包含所有關鍵字,並以有序的鏈表結構存儲,這樣可很好提高增刪效率,而對於B-Tree,由於插入刪除新的數據記錄會破壞B-Tree的性質,因此在插入刪除時,需要對樹進行一個分裂、合併、轉移等操作以保持B-Tree性質

MySQL索引實現

MyISAM/非聚簇索引

MyISAM引擎使用B+Tree作爲索引結構,葉節點的data域存放的是數據記錄的地址。
MyISAM的索引文件僅僅保存數據記錄的地址。在MyISAM中,主索引和輔助索引(Secondary key)在結構上沒有任何區別,只是主索引要求key是唯一的,而輔助索引的key可以重複。

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

InnoDB/聚簇索引

InnoDB的數據文件本身就是索引文件。
從上文知道,MyISAM索引文件和數據文件是分離的,索引文件僅保存數據記錄的地址。
而在InnoDB中,表數據文件本身就是按B+Tree組織的一個索引結構,這棵樹的葉節點data域保存了完整的數據記錄(所以聚簇索引的key,不能過長)。
這個索引的key是數據表的主鍵,因此InnoDB表數據文件本身就是主索引。

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

第二個與MyISAM索引的不同是InnoDB的輔助索引data域存儲相應記錄主鍵的值而不是地址。
換句話說,InnoDB的所有輔助索引都引用主鍵作爲data域。

參考資料:
http://blog.codinglabs.org/articles/theory-of-mysql-index.html
https://www.cnblogs.com/liqiangchn/p/9060521.html
https://blog.csdn.net/mine_song/article/details/63251546
https://blog.csdn.net/tongdanping/article/details/79878302

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章