MySQL InnoDB 中 B+ 樹索引,哈希索引,全文索引 詳解

在數據庫中,若是索引太多,應用程序的性能可能會受到影響,若是索引太少,又會對查詢性能產生影響。因此,咱們要追求二者的一個平衡點,足夠多的索引帶來查詢性能提升,又不由於索引過多致使修改數據等操做時負載太高。 文章會從,B+樹索引,索引的分類,哈希索引,全文索引,這個幾個方面講解面試

B+樹索引算法

  • 索引的查找
  • 索引的插入
  • 索引的刪除

索引的分類sql

  • 彙集索引
  • 輔助索引
  • 聯合索引
  • 覆蓋索引

哈希索引數據庫

  • 哈希算法
  • 自適應哈希索引

全文索引編程

  • 倒排索引
  • 全文檢索索引緩存
  • 全文索引的一些限制

InnoDB支持3種常見索引,咱們接下來要詳細講解的就是 B+ 樹索引,哈希索引,全文索引。後端

B+樹索引

一、B+樹中的B不是表明的二叉(Binary) ,而是表明平衡(Balance),由於B+樹是從最先的平衡二叉樹演化而來,可是B+樹不是一個二叉樹。數組

二、B+樹是爲磁盤或其餘直接存取輔助設備設計的一種平衡查找樹,在B+樹中,全部的記錄節點都是按照鍵值大小順序存在同一層的葉子節點,由葉子節點指針進行相連。緩存

三、B+樹在數據庫中的特色就是高扇出,所以在數據庫中B+樹的高度通常都在2~4層,這也就是說查找一個鍵值記錄時,最多隻須要2到4次IO,當前的機械硬盤每秒至少能夠有100次IO,2~4次IO意味着查詢時間只須要0.02~0.04秒。bash

四、B+樹索引並不能找到一個給定鍵值的具體行,B+樹索引能找到的只是被查找的鍵值所在行的頁,而後數據庫把頁讀到內存,再內存中進行查找,最後找到要查找的數據。微信

五、數據庫中B+樹索引能夠分爲,彙集索引和非彙集索引,可是無論是彙集索引仍是非彙集索引,其內部都是B+樹實現的,即高度是平衡的,葉子節點存放着全部的數據,彙集索引和非彙集索引不一樣的是,葉子節點是否存儲的是一整行信息。每張表只能有一個彙集索引。

六、B+樹的每一個數據頁(葉子節點)是經過一個雙向鏈表進行連接,數據頁上的數據的順序是按照主鍵順序存儲的。

先來看一個B+樹,其高度爲2,每頁能夠放4條記錄,扇出爲5。

圖:一顆高度爲2的B+樹

索引的查找

B+樹索引使用二分法查找,也稱折半查找法,基本思想就是:將記錄有序化(遞增或遞減)排列,在超找過程當中採用跳躍式方式查找,既先以有序數列的中心點位置比較對象,若是要查找的元素小於該元素的中心點元素,則將待查找的元素縮小爲左半部分,不然爲右半部分,經過一次比較,將查找區間縮小一半。

如圖所示,從有序列表中查找 48,只須要3步:

圖:二分法查找

索引的插入

B+樹的查找速度很快,可是維護一顆平衡的B+樹代價就是很是大的,一般來講,須要1次或者屢次左旋右旋來保證插入後樹的平衡性。

B+樹的插入爲了保持樹的平衡,須要作大量的頁(葉子節點)的拆分,頁的存儲基本都在磁盤,頁的拆分意味着磁盤的操做,因此應該儘可能減小頁的拆分,在採用自增加ID,做爲主鍵,會大量的減小頁的拆分,提高的性能。

B+樹 插入的三種狀況

Leaf Page滿 Index Page滿 操做
No No 直接將記錄插入葉子節點
Yes No 一、拆分Leaf Page
二、將中間的節點放入到Index Page中
三、小於中間節點的記錄放左邊
四、大於或等於中間節點的記錄放右邊
Yes Yes 一、拆分Leaf Page
二、小於中間節點的記錄放左邊
三、大於或等於中間節點的記錄放右邊
四、拆分Index Page
五、小於中間節點的記錄放左邊
六、大於中間節點的記錄放右邊
七、中間節點放入上一層
Index Page

圖:一顆高度爲2的B+樹

咱們用實例來分析B+樹的插入。

(1)咱們插入28這個鍵值,發現當前Leaf Page和Index Page都沒有滿,咱們直接插入就能夠了。

(2)此次咱們再插入一條70這個鍵值,這時原先的Leaf Page已經滿了,可是Index Page尚未滿,符合表(B+樹 插入的三種狀況)的第二種狀況,這時插入Leaf Page後的狀況爲50、5五、60、6五、70。咱們根據中間的值60拆分葉節點。將中間節點放入到Index Page中。

(3)由於圖片顯示的關係,此次我沒有能在各葉節點加上雙向鏈表指針。最後咱們來插入記錄95,這時符合表(B+樹 插入的三種狀況)討論的第三種狀況,即Leaf Page和Index Page都滿了,這時須要作兩次拆分。

能夠看到,無論怎麼變化,B+樹老是會保持平衡。可是爲了保持平衡,對於新插入的鍵值可能須要作大量的拆分頁(split)操做,而B+樹主要用於磁盤,所以頁的拆分意味着磁盤的操做,應該在可能的狀況下儘可能減小頁的拆分。所以,B+樹提供了旋轉(rotation)的功能。

索引的刪除

B+樹使用填充因子(fill factor) 來控制樹的刪除變化,50%是填充因子可設的最小值,B+樹的刪除也一樣必須保證刪除後樹的平衡性,刪除的過程當中會涉及,合併葉子節或兄弟節點,可是都是爲了保持樹的平衡。

索引的分類

在瞭解B+樹索引的本質和實現後,咱們看看索引分爲幾類,彙集索引,輔助索引,聯合索引,覆蓋索引

彙集索引

就是按照每張表的主鍵構造一顆B+樹,同時葉子節點存儲整張表的行記錄數,也將彙集索引的葉子節點成爲「數據頁」,彙集索引的特性決定了表中的行記錄數據也是索引的一部分。同B+樹數據結構同樣,每一個數據頁都經過一個雙向鏈表進行連接。

數據頁只能按照一顆B+樹進行排序,所以每張表只能有一個彙集索引,因爲數據頁定義了邏輯順序,彙集索引可以很快的在數據頁訪問指針進行範圍的查找數據。

彙集索引在物理上不是連續的,在邏輯上是連續的,前面已經說過是經過雙向鏈表進行維護,物理存儲能夠不按照主鍵順序存儲。

輔助索引

輔助索引(也稱非彙集索引),葉子節點並不包含行記錄的所有數據。葉子節點除了包含鍵值外,每一個葉子節點還包含了一個書籤,該書籤告訴InnoDB 存儲引擎能夠從哪裏找到輔助索引相對應行的記錄。所以InnoDB 存儲引擎的輔助索引的書籤就是相應整行數據的彙集索引鍵。

一個表中能夠有多個輔助索引。例如,一個輔助索引樹須要遍歷3次才能找到主鍵索引,若是彙集索引樹的高爲一樣爲3,那麼它還須要對彙集索引樹進行三次查找,最終才能找到一個完整的數據頁,所以一共須要6次IO訪問才能獲得最終的數據頁。

聯合索引

聯合索引是指對錶上多個列進行創建索引,聯合索引本質仍是一顆B+樹,不一樣的是索引的鍵值數量不是1個,而是大於等於2。聯合索引的鍵值在B+樹中也是有序的,經過葉子節點能夠在邏輯的順序上讀出全部數據。

覆蓋索引

InnoDB存儲引擎支持覆蓋索引(或稱索引覆蓋),就是從輔助索引中就能夠直接獲得查詢的記錄,而不須要再次查詢彙集索引中的記錄。使用覆蓋索引的好處就是,輔助索引不包括整行記錄的全部信息,因此覆蓋索引的大小要小於彙集索引,所以能夠減小IO操做。

通俗的解釋:

覆蓋索引是非彙集組合索引的一種形式,它包括在查詢裏的Select、Join和Where子句用到的全部列(即創建索引的字段正好是覆蓋查詢語句[select子句]與查詢條件[Where子句]中所涉及的字段,也就是索引包含了查詢正在查找的全部數據)

哈希索引

學習哈希索引以前,咱們先了解一些基礎的知識:哈希算法。哈希算法是一種經常使用的算法,時間複雜度爲O(1)。它不只應用在索引上,各個數據庫應用中也都會使用。

哈希算法

InnoDB存儲引擎使用哈希算法來對字典進行查找,哈希碰撞採用轉鏈表解決,哈希函數採用除法散列方式。

例如:當前參數InnoDB_buffer_pool_size大小爲10M,則共有640個16k的頁,對於緩衝頁內存的哈希表來講,須要分配640×2=1280個槽,可是因爲1280不是質數,因此須要取比1280更大的一點的質數,應該是1399,因此啓動的時候,會分配1399個槽的哈希表,用來哈希查詢所在的緩衝池中的頁。

InnoDB存儲引擎是經過除法散列到1399個其中的一個槽中。

自適應哈希索引

自適應哈希索引採用以前說的哈希表方式,不一樣的是哈希索引對字典類型的等值查找很是快,對範圍查詢就無能爲力了。

因此說哈希索引只能用於搜索等值查詢,範圍查詢是不能使用哈希索引。

全文索引

以前已經說過,B+樹索引的特色,對於使用以下sql,是支持B+樹索引的,只要content 加了B+樹索引,就能利用索引進項快速查詢。

咱們經過 B+ 樹索引能夠進行前綴查找,如:

select * from blog where content like 'xxx%';
複製代碼

只要爲content列添加了B+樹索引(彙集索引或輔助索引),就可快速查詢。但在更多狀況下,咱們在博客或搜索引擎中須要查詢的是某個單詞,而不是某個單詞開頭,如:

select * from blog where content like '%xxx%';
複製代碼

此時若是使用B+樹索引依然是全表掃描,而全文檢索(Full-Text Search)就是將整本書或文章內任意內容檢索出來的技術。

根據B+樹索引的特色是不支持的,InnoDB存儲引擎從1.2.x開始支持全文索引技術,其特性支MyISAM的所有功能。

具體實現原理接下來會介紹

倒排索引

全文檢索使用倒排索引來實現,倒排索引同B+樹索引同樣,也是一種數據結構,它在輔助表中存儲了單詞與單詞自身在一個或多個文檔中所在位置的映射,這一般利用關聯數組實現。

倒排索引它須要將分詞(word)存儲在一個輔助表(Auxiliary Table)中,爲了提升全文檢索的並行性能,共有6張輔助表。輔助表中存儲了單詞和單詞在各行記錄中位置的映射關係。它分爲兩種:倒排文件索引,詳細倒排索引

一、inverted file index(倒排文件索引),表現爲{單詞,單詞所在文檔ID}

二、full inverted index(詳細倒排索引),表現爲{單詞,(單詞所在文檔ID, 文檔中的位置)}

全文檢索表

DocumentID Text 文檔內容
1 Souyunku Technical team (搜雲庫技術團隊)
2 Go Technical stack (Go技術棧)

inverted file index(倒排文件索引)-輔助表存儲爲

倒排文件索引類型的輔助表存儲爲:

Number Text 分詞 Documents (單詞所在文檔ID)
1 Souyunku 1
2 Technical 1,2
3 team 1
4 Go 2
5 stack 2

full inverted index( 詳細倒排索引)-輔助表存儲爲

詳細倒排索引類型的輔助表存儲爲,佔用更多空間,也更好的定位數據,比提供更多的搜索特性:

Number Text 分詞 Documents (單詞所在文檔ID:文檔中的位置)
1 Souyunku 1:1
2 Technical 1:2 ,2:2
3 team 1:3
4 Go 2:1
5 stack 2:3

全文檢索索引緩存

輔助表是存在與磁盤上的持久化的表,因爲磁盤I/O比較慢,所以提供FTS Index Cache(全文檢索索引緩存)來提升性能。FTS Index Cache是一個紅黑樹結構,根據(word, list)排序,在有數據插入時,索引先更新到緩存中,然後InnoDB存儲引擎會批量進行更新到輔助表中。

當數據庫宕機時,還沒有落盤的索引緩存數據會自動讀取並存儲,配置參數innodb_ft_cache_size控制緩存的大小,默認爲32M,提升該值,能夠提升全文檢索的性能,但在故障時,須要更久的時間恢復。

在刪除數據時,InnoDB不會刪除索引數據,而是保存在DELETED輔助表中,所以一段時間後,索引會變得很是大,能夠經過optimize table命令手動刪除無效索引記錄。若是須要刪除的內容很是多,會影響應用程序的可用性,參數innodb_ft_num_word_optimize控制每次刪除的分詞數量,默認爲2000,用戶能夠調整該參數來控制刪除幅度。

全文索引的一些限制

一、如今只支持myisam和innodb
二、不支持分區表
三、多列組合的全文檢索索引必須使用相同的字符集和字符序
四、象形文字不支持。須要ngram來分詞
五、創建全文索引的各個字段必須統一
六、match()裏的查找列,必須是在fulltext索引裏定義過的
七、against()必須爲字符串且爲常量
八、索引提示會更差
九、在innodb中,全部涉及到全文索引列的DML操做(update,insert,delete),只會在事務提交的時候,執行。中間可能要分詞,標記等
十、不能用 % 通配符
十一、不支持沒有單詞界定符(delimiter)的語言,如中文、日語、韓語等

參考資料:MySQL技術內幕 InnoDB存儲引擎 第2版

書單推薦:Java成神之路圖書

因爲近期不少粉絲總在問小編有沒有:JVM、Java後端、微服務、分佈式、大數據、區塊鏈、容器化編程、數據結構與算法、源碼閱讀、等技術棧,圖書資源推薦。如今網上已經有不少免費資源了,只是都沒有好好整理,尤爲是小白更不知道怎麼整理,或者怎找資料。

對了,我介紹的這些書籍,面試題,我順便幫你整理好了,你能夠在個人,原創微信公衆號『搜雲庫技術團隊』回覆『掘金』便可無套路,獲取哦!

公衆號
相關文章
相關標籤/搜索