数据结构与索引-- B+树索引
B+樹索引
-
上一節中我們討論的都是B+樹的數據結構的由來以及他的一些操作,B+樹索引在本質就是B+樹在數據庫中的一個實現,但是B+索引在數據庫中有一個特點就是他的高扇出性,因此在數據庫中,B+樹的高度一般是2~3層,也就是對于查找某一個鍵值的行記錄,最多只需要2 ~ 3次IO,我們假設一般的磁盤每秒可以做100次IO,那么2 ~3次就差不多0.02 秒 ~0.03秒
- 扇入數(引入)就是引入了多少別的模塊引到自己模塊來,像光線匯聚。
- 扇出(輸出)就是自己模塊被多少個其他模塊拿來使用,像瀑布鋪灑。
- 用如下圖解釋
-
數據庫中的B+樹索引分為聚集索引(clustered index)和輔助索引(secondary index),但是不管是聚集索引還是輔助索引,內部結構都是B+樹,即高度平衡的,葉節點存放著所有的數據,聚集索引與非聚集索引不同的是,葉子節點存放的是否是一整行的信息
聚集索引
-
InnoDB存儲引擎表是索引組織表,即表中數據依照主鍵順序存放。而聚集索引就是按照每張表的主鍵構造一顆B+樹,并且葉子節點存放整張表的行為記錄數據,因此也讓聚集索引的葉節點成為數據頁。聚集索引的這個特性決定了索引組織表中數據也是索引的一部分。和B+樹數據結構一樣,每一個數據頁都通過一個雙向鏈表來進行連接
-
由于實際的數據頁只能按照一顆B+樹進行排序,因此每張表只擁有一個聚集索引。在許多情況下,查詢優化器非常傾向于采用聚集索引,因此,聚集索引能夠讓我們在索引的葉子節點上直接找到數據。此外,由于定義了數據的邏輯順序,聚集索引能夠特別快的訪問針對范圍的查詢。查詢優化器能夠快速發現某一段范圍的數據頁需要進一步掃描篩選數據。
-
我們用如下的表以及數據來說明,我們以認為的方式讓每一個頁只能存放兩個行為記錄:
- 我們如上表的定義,插入的數據使得目前每個頁只能存放兩個行記錄,并且在構造的B+樹上,數據頁上存放的是完整的行記錄,而在非數據頁的索引頁中,存放的僅僅是鍵值以及指向數據頁的偏移量,而不是一個完整的行記錄,因此我們構造這顆二叉樹大致如下圖:
-
很多數據庫文檔中說過,聚集索引按照順序物理的存儲數據。如上圖可以看到會有這種感覺。但是,如果聚集索引必須按照特定順序存放物理記錄的話,則維護成本會非常高。索引,聚集索引的存儲并不是物理上連續的,相反,邏輯上才是連續的。有兩點:
- 一個是我們說過頁面通過雙向鏈表鏈接,頁按照主鍵順序排列
- 每個頁中的記錄也是通過雙向鏈表進行維護,物理存儲上可以同樣不按主鍵存儲。
-
聚集索引的一個好處是,他對于主鍵的排序查找和范圍查找速度非常快。葉節點的數據就是我們要查詢的數據,如果我們要查詢一張注冊用戶的表,查詢最后注冊的10位用戶,由于B+樹索引是雙向的,我們可以快速找到最后一個數據頁,并去除10條記錄,我們用explain分析如下:
- 另一個是范圍查找(range query),如果查找主鍵某個范圍內的數據,通過葉節點的上層中間節點就可以得到頁的范圍,之后直接讀取數據頁即可,如下:
- 上圖中的rows代表是一個預估值,,如果實際查詢這條sql的數據條數發現更多17384條。
輔助索引
-
對應輔助索引,也就是我們說的***非聚集索引***,葉節點不包含行的全部數據。葉子節點除了包含鍵值以外,每個葉子節點中的索引行中還包含了一個書簽(bookmark),該書簽用來告訴InnoDB存儲引擎,哪里可以找到與索引對應的行數據。因為InooDB存儲引擎表是索引組織表,因此,InnoDB存儲引擎的輔助索引的書簽就是相應數據的聚集索引的鍵值。如下圖表示InnoDB存儲引擎中輔助索引與聚集索引的關系。
-
輔助索引的存在并不影響數據在聚集索引中的組織關系,因此每張表可以有多個輔助索引,當通過輔助索引來查找數據時候,InnoDB存儲引擎會遍歷輔助索引并通過葉子節點上的指針獲得指向主鍵索引的主鍵,讓后通過主鍵索引再來找一次最終找到一個完整的記錄。
-
案例
- 如果在一顆高度為3(根算0 層)的輔助索引中查找數據,那么需要對這可輔助索引遍歷3次找到指定主鍵
- 如果聚集索引樹的的高度同樣是3,那么還需要對聚集索引在進行三次查找,才能最終找到一個完整的行數據所在的頁(InnoDB最小存儲單元是頁)
- 因此一共需要6次邏輯IO來訪問最終的一個數據頁。
-
此處也行會有疑問,為啥不直接將負責索引葉子上的 書簽指針之間指向行數據所在的頁呢,這樣就可以和聚集索引一樣,通過遍歷一個B+樹的索引就可以找到我們需要的數據頁所在的位置,少了一次IO操作。
- 的確在某些情況,例如只讀的時候,書簽是行標識的方式的非聚集索引 比 書簽是主鍵方式的非聚集索引要快。
- 但是這是考慮在OLTP(Online Transaction Processing, 在線事務處理)應用的情況下,表可能還需要提供其他服務,例如,insert,update,delete等DML操作。當進行這種操作時候,書簽為行的標識符方式的非聚集索引可能就需要不斷的更新行標識符所指向的數據頁面的位置,這時候的開銷可能就大于書簽為主鍵的非聚集索引了
-
又有另外一個疑問,那么DML操作也會影響主鍵索引,這個效率也會降低嗎:
- DML操作的確會影響,但是主鍵是不能修改的,對于聚集索引來說 DML操作只會存在增加節點或者刪除節點的情況,不會變更行的地址,而只是修改節點的指針數據而已
- B+樹的節點操作我們在之前章節 數據結構與算法–B樹原理及實現 中有過詳細的解釋。
B+樹索引的管理
- 索引的創建和刪除通過兩種方法,一種ALTER TABLE,一種CREATE/DROP index,具體預發略
- 索引可以索引整個列的數據,也可以索引一個列的開頭N行數據,例如建立前100 行的索引如下:
-
MySQL數據庫存在一個普遍問題,所有對所有的添加,刪除操作,MySQL數據庫都是先創建一個新的臨時表,然后把數據導入臨時表,刪除原表,在吧臨時表重命名為原來的表,因此對于一張大表,添加,刪除索引非長久時間,
-
InnoDB存儲引擎從InnoDB Plugin開始,支持一種稱為快速索引的創建方法,但是只限定于輔助索引,對于主鍵的創建和刪除還是需要重建一張表。對于輔助索引的創建,InnoDB存儲引擎會對表加上一個s鎖。創建過程,不需要重建表,因此速度快,但是創建過程中上了S鎖,因此創建過程中該表只進行讀。
-
刪除的話更簡單,只需要InnoDB存儲引擎內部視圖更新,將輔助索引空間標記可用即可,并刪除MySQL內部視圖上對改表索引定義。
-
可用用如下方法查看索引:
-
上圖中,有3個索引,一個主鍵索引,b列的非聚集索引,a,b的復合索引,如下圖:
-
具體每個列的含義:
- Table:索引所在表名
- Non_unique:非唯一索引,可以看到primaryKey是0,因為必須是唯一索引
- Key_name:索引名稱,我們可以通過這個名稱來DROP INDEX
- Sql_in_index:索引中該列的位置,如看到聯合索引idx_a_b就比較直觀的標識復合索引,位置有兩個1,2
- Column_name:索引列
- Collation:列以什么方式存儲在索引中。可以上‘A’或者NULL。B+樹總是A,即排序的。如果使用了Heap存儲引擎,并且建立了Heap索引,這里就會顯示NULL了,因為Hash根據Hash桶來存放索引數據,而不是對數據進行排序
- Cardinality:非常重要的值,表示索引中唯一值的數目的估計值。Cardinality表示的行數如果非常小,說明我們可能不需要這個索引,比如以上案例,只有四條數據,完全沒有必要索引的。
- Sub_part:是否是列的部分被索引。如果看idx_b這個索引,這里顯示100,表示我們只索引b列的前100個字符,如果索引整個列,該字段值是NULL
- Packed:關鍵字如何被壓縮。如果沒有被壓縮,則為NULL
- Null:是否索引的列含義NULL值,可以看到idx_b這里是yes,因為我們定義b列的時候允許NULL
- Index_type:索引類型。InnoDB存儲引擎只支持B+樹索引,所以這里顯示的都是BTREE
- Comment:注釋
-
其中Cardinality值比較關鍵,查詢優化器 會根據這個值來判斷是否使用這個索引。但是這個值并不是實時更新,并非每次索引的更新都會更新改值,這樣代價太大。因此這個值是不太準確的,只是一個估計值。上面顯示的Cardinality為2,但是顯然表中有四條數據,這個應該是4的。如果需要更新Cardinality信息,可以使用命令如下:
- 不過這條命令在每個系統上可能得到的結果不一樣,因為AnalyzeTable 現在還存在問題,可能會影響得到最后的結果,另一個問題是MySql對Cardinality計數的問題
- 某些情況下可能會發生即使你建立了索引,但是查詢這個字段確沒有用到,或者,explain兩條基本一樣的語句,但是最終出來的結果一個用索引,一個不用索引,這個都和查詢優化器對Cardinality的數量判斷做的優化有關系。
上一篇:數據結構與索引-- mysql InnoDB存儲引擎索引
下一篇:數據結構與索引-- mySql索引詭異事件
總結
以上是生活随笔為你收集整理的数据结构与索引-- B+树索引的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 数据结构与索引-- mysql Inno
- 下一篇: 数据结构与索引-- mySql索引诡异事