MySQL索引實現原理分析

    目前大部分數據庫系統及文件系統都採用B-Tree(B樹)或其變種B+Tree(B+樹)作爲索引結構。B+Tree是數據庫系統實現索引的首選數據結構。在MySQL中,索引屬於存儲引擎級別的概念,不同存儲引擎對索引的實現方式是不同的,本文主要討論MyISAM和InnoDB兩個存儲引擎的索引實現方式。MyISAM索引實現MyISAM引擎使用B+Tree作爲索引結構,葉節點的data域存放的是數據記錄的地址。

        索引是一個排序的列表,在這個列表中存儲着索引的值和包含這個值的數據所在行的物理地址或者數據的內容,在數據十分龐大的時候,索引可以大大加快查詢的速度,這是因爲使用索引後可以不用掃描全表來定位某行的數據,而是先通過索引表找到該行數據對應的物理地址然後訪問相應的數據。   

索引的優缺點:

    優勢:可以快速檢索,減少I/O次數,加快檢索速度;根據索引分組和排序,可以加快分組和排序;

    劣勢:索引本身也是表,因此會佔用存儲空間,一般來說,索引表佔用的空間的數據表的1.5倍(一般創建索引只是表的一個字段或者幾個字段,索引表佔用的表空間應該不會太大);索引表的維護和創建需要時間成本,這個成本隨着數據量增大而增大;構建索引會降低數據表的修改操作(刪除,添加,修改)的效率,因爲在修改數據表的同時還需要修改索引表;



MyISAM 索引實現 

MyISAM 引擎使用 B+Tree 作爲索引結構,葉節點的 data 域存放的是數據記錄的地址。下圖是 MyISAM 索引的原理圖:
MySQL索引實現原理分析

這裏設表一共有三列,假設我們以 Col1 爲主鍵,則圖 8 是一個 MyISAM 表的主索引(Primary key)示意。可以看出 MyISAM 的索引文件僅僅保存數據記錄的地址。

輔助索引 

在 MyISAM 中,主索引和輔助索引(Secondary key)在結構上沒有任何區別,只是主索引要求 key 是唯一的,而輔助索引的 key 可以重複。如果我們在 Col2 上建立一個輔助索引,則此索引的結構如下圖所示


MySQL索引實現原理分析

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

MyISAM 的索引方式也叫做“非聚集索引”,之所以這麼稱呼是爲了與 InnoDB的聚集索引區分。


InnoDB 索引實現 

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

1.第一個重大區別是 InnoDB 的數據文件本身就是索引文件(生成的索引文件的葉子節點上的直接存放在key:主鍵相對應的數據內容)。從上文知道,MyISAM 索引文件和數據文件是分離的,索引文件僅保存數據記錄的地址

而在InnoDB 中,表數據文件本身就是按 B+Tree 組織的一個索引結構,這棵樹的葉點data 域保存了完整的數據記錄。這個索引的 key 是數據表的主鍵,因此 InnoDB 表數據文件本身就是主索引。


MySQL索引實現原理分析

上圖是 InnoDB 主索引(同時也是數據文件)的示意圖,可以看到葉節點包含了完整的數據記錄。這種索引叫做聚集索引。因爲 InnoDB 的數據文件本身要按主鍵聚集,

 1 .InnoDB 要求表必須有主鍵(MyISAM 可以沒有),如果沒有顯式指定,則 MySQL系統會自動選擇一個可以唯一標識數據記錄的列作爲主鍵,如果不存在這種列,則MySQL 自動爲 InnoDB 表生成一個隱含字段作爲主鍵,類型爲長整形。

 同時,請儘量在 InnoDB 上採用自增字段做表的主鍵。因爲 InnoDB 數據文件本身是一棵B+Tree,非單調的主鍵會造成在插入新記錄時數據文件爲了維持 B+Tree 的特性而頻繁的分裂調整,十分低效,而使用自增字段作爲主鍵則是一個很好的選擇。如果表使用自增主鍵,那麼每次插入新的記錄,記錄就會順序添加到當前索引節點的後續位置,當一頁寫滿,就會自動開闢一個新的頁。如下圖所示:


MySQL索引實現原理分析

這樣就會形成一個緊湊的索引結構,近似順序填滿。由於每次插入時也不需要移動已有數據,因此效率很高,也不會增加很多開銷在維護索引上。

 2.第二個與 MyISAM 索引的不同是 InnoDB 的輔助索引 data 域存儲相應記錄主鍵的值而不是地址。換句話說,InnoDB 的所有輔助索引都引用主鍵作爲 data 域。
例如,圖 11 爲定義在 Col3 上的一個輔助索引:


MySQL索引實現原理分析
 

聚集索引這種實現方式使得按主鍵的搜索十分高效,但是輔助索引搜索需要檢索兩遍索引:首先檢索輔助索引獲得主鍵,然後用主鍵到主索引中檢索獲得記錄。

 引申:爲什麼不建議使用過長的字段作爲主鍵?

 因爲所有輔助索引都引用主索引,過長的主索引會令輔助索引變得過大。


局部性原理與磁盤預讀:

由於存儲介質的特性,磁盤本身存取就比主存慢很多,再加上機械運動耗費,磁盤的存取速度往往是主存的幾百分分之一,因此爲了提高效率,要儘量減少磁盤I/O。爲了達到這個目的,磁盤往往不是嚴格按需讀取,而是每次都會預讀,即使只需要一個字節,磁盤也會從這個位置開始,順序向後讀取一定長度的數據放入內存預讀可以提高I/O效率。預讀的長度一般爲頁(page:計算機管理存儲器的邏輯塊-通常爲4k)的整倍數. 主存和磁盤以頁爲單位交換數據。當程序要讀取的數據不在主存中時,會觸發一個缺頁異常,此時系統會向磁盤發出讀盤信號,磁盤會找到數據的起始位置並向後連續讀取一頁或幾頁載入內存中。


B-/+Tree索引的性能優勢: 一般使用磁盤I/O次數評價索引優劣。

1.結合操作系統存儲結構優化處理: mysql巧妙運用操作系統存儲結構(一個節點分配到一個存儲頁中->儘量減少IO次數) & 磁盤預讀(緩存預讀->加速預讀馬上要用到的數據).

2.B+Tree 單個節點能放多個子節點相同IO次數,檢索出更多信息。

3.B+TREE 只在葉子節點存儲數據 & 所有葉子結點包含一個鏈指針 & 其他內層非葉子節點只存儲索引數據。只利用索引快速定位數據索引範圍,先定位索引再通過索引高效快速定位數據。

每個節點中的key個數越多,那麼樹的高度越小,需要I/O的次數越少,因此一般來說B+Tree比BTree更快,因爲B+Tree的非葉節點中不存儲data,就可以存儲更多的key

詳解:Mysql設計利用了磁盤預讀原理,將一個B+Tree節點大小設爲一個頁大小,在新建節點時直接申請一個頁的空間,這樣就能保證一個節點物理上存儲在一個頁裏,加之計算機存儲分配都是按頁對齊的,這樣就實現了每個Node節點只需要一次I/O操作。

B-Tree索引、B+Tree索引: 單個節點能放多個子節點,查詢IO次數相同(mysql查詢IO次數最多3-5次-所以需要每個節點需要存儲很多數據)

B+TREE 只在葉子節點存儲數據 & 所有葉子結點包含一個鏈指針 & 其他內層非葉子節點只存儲索引數據。只利用索引快速定位數據索引範圍,先定位索引再通過索引高效快速定位數據。

B+Tree更適合外存索引,原因和內節點出度d有關。從上面分析可以看到,d越大索引的性能越好,而出度的上限取決於節點內key和data的大小:

B+Tree內節點去掉了data域,因此可以擁有更大的出度,擁有更好的性能。只利用索引快速定位數據索引範圍,先定位索引再通過索引高效快速定位數據。

dmax=floor(pagesize/(keysize+datasize+pointsize))


InnoDB表空間管理

InnoDB物理存儲文件結構說明:

    InnoDB以表空間Tablespace(idb文件)結構進行組織,每個Tablespace 包含多個Segment段,每個段(分爲2種段:葉子節點Segment&非葉子節點Segment), 一個Segment段包含多個Extent,一個Extent佔用1M空間包含64個Page(每個Page 16k),InnoDB B-Tree 一個邏輯節點就分配一個物理Page,一個節點一次IO操作。,一個Page裏包含很多有序數據Row行數據,Row行數據中包含Filed屬性數據等信息。

• 表空間(ibd文件)

• 段(一個索引2段:葉子節點Segment & 非葉子節點Segment

• Extent(1MB):一個Extent(1M) 包含64個 Page(16k),一個Page裏包含很多有序行數據 , InnoDB B-Tree 一個邏輯節點就分配一個物理Page,一個節點一次IO操作。

• Page(16KB)

• Row

• Field

表插入數據擴展原理: 一次擴張一個Extent空間(1M),64個Page,按照順序結構向每個page中插入順序。

InnoDB邏輯組織結構:

InnoDB索引樹結構

每個索引一個B+樹, 一個B+樹節點 = 一個物理Page(16K)

• 數據按16KB切片爲Page 並編號, 編號可映射到物理文件偏移(16K * N), B+樹葉子節點前後形成雙向鏈表, 數據按主鍵索引聚簇, 二級索引葉節點存儲主鍵值, 通過葉節點主鍵值回表查找數據。

InnoDB索引原理: 

  採用聚簇索引- InnoDB數據&索引文件爲一個idb文件,表數據文件本身就是主索引,相鄰的索引臨近存儲。 葉節點data域保存了完整的數據記錄(數據[除主鍵id外其他列data]+主索引[索引key:表主鍵id])。 葉子節點直接存儲數據記錄,以主鍵id爲key,葉子節點中直接存儲數據記錄。(底層存儲結構: frm -表定義、 ibd: innoDB數據&索引文件)

注:由於InnoDB採用聚簇索引結構存儲,索引InnoDB的數據文件需要按照主鍵聚集,因此InnoDB要求表必須有主鍵(MyISAM可以沒有)。如果沒有指定mysql會自動選擇一個可以唯一表示數據記錄的列作爲主鍵,如果不存在這樣的列,mysql自動爲InnoDB表生成一個隱含字段(6個字節長整型)作爲主鍵。 InnoDB的所有 輔助索引 都引用 數據記錄的主鍵 作爲data域。


聚簇索引與非聚簇索引 

InnoDB 使用的是聚簇索引, 將主鍵組織到一棵 B+樹中, 而行數據就儲存在葉子節點上, 若使用"where id = 14"這樣的條件查找主鍵, 則按照 B+樹的檢索算法即可查找到對應的葉節點, 之後獲得行數據。 若對 Name 列進行條件搜索, 則需要兩個步驟:
第一步在輔助索引 B+樹中檢索 Name, 到達其葉子節點獲取對應的主鍵。
第二步使用主鍵在主索引 B+樹種再執行一次 B+樹檢索操作, 最終到達葉子節點即可獲取整行數據。

MyISM 使用的是非聚簇索引, 非聚簇索引的兩棵 B+樹看上去沒什麼不同, 節點
的結構完全一致只是存儲的內容不同而已, 主鍵索引 B+樹的節點存儲了主鍵, 輔助鍵索引B+樹存儲了輔助鍵。 表數據存儲在獨立的地方, 這兩顆 B+樹的葉子節點都使用一個地址指向真正的表數據, 對於表數據來說, 這兩個鍵沒有任何差別。 由於索引樹是獨立的, 通過輔助鍵檢索無需訪問主鍵的索引樹。

爲了更形象說明這兩種索引的區別, 我們假想一個表如下圖存儲了 4 行數據。 其中Id 作爲主索引, Name 作爲輔助索引。 圖示清晰的顯示了聚簇索引和非聚簇索引的差異


MySQL索引實現原理分析

 

聯合索引及最左原則

聯合索引存儲數據結構圖:

最左原則:

例如聯合索引有三個索引字段(A,B,C)

查詢條件:

(A,,)---會使用索引

(A,B,)---會使用索引

(A,B,C)---會使用索引

(,B,C)---不會使用索引

(,,C)---不會使用索引



索引類型中葉子data域存放內容:

索引類型

主/輔索引

Date域存儲的內容

索引種類

MyISAM

主索引

數據的地址

非聚合索引

輔索引

數據的地址

InnoDB

主索引

直接數據記錄

聚合索引,查詢效率較快,索引文件較大

輔索引

表的主鍵,在去主索引查全部信息

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