MySQL全面瓦解22:索引的介紹和原理分析
阿新 • • 發佈:2021-02-18
索引的定義
MySQL官方對索引的定義為:索引(Index)是協助MySQL高效獲取資料的資料結構。 本質上,索引的目的是為了提高查詢效率,通過不斷地縮小想要獲取資料的範圍來篩選出最終想要的結果,同時把隨機的事件變成順序的事件,也就是說,有了這種索引機制,我們可以總是用同一種查詢方式來鎖定資料。 可以類比銀行的保險櫃,比如你要找歸屬你的保險櫃子。如果沒有索引,你需要拿著鑰匙,一個個的保險櫃的試過去才能找到屬於你的保險櫃。但是如果有了索引,而且保險櫃能夠以物理分割槽的方式存在在對應的區域,同時你可以根據鑰匙上的編號(A1003-10-17),找到保險櫃所在 A1003的存放房間,找到存放室保險櫃的第10排,再找到第17個位置,找到屬於你的保險櫃,這個定位就快很多了。在沒有索引的情況下,要想完成這個事情還是比較困難的。索引的原理
二叉查詢樹
平衡二叉樹(AVL Tree)
平衡多路查詢樹(B-Tree)
我們知道,磁碟這種儲存裝置是以磁碟塊(block)為基本單位的,而B-樹也是基於這種儲存方式設計的平衡查詢樹。 所以當我們從系統磁碟讀取資料時,以磁碟塊(block)為基本單位對映到記憶體中,位於同一個磁碟塊中的資料會被一次性讀取出來,而不是隻取需要的資料。InnoDB儲存引擎中有頁(Page)的概念,頁是其磁碟管理的最小單位。InnoDB儲存引擎中預設每個頁的大小為16KB,可通過引數innodb_page_size將頁的大小設定為4K、8K、16K,我們可以在命令視窗輸入以下指令碼檢視:1 mysql> show variables like 'innodb_page_size'; 2 +------------------+-------+ 3 | Variable_name | Value | 4 +------------------+-------+ 5 | innodb_page_size | 16384 | 6 +------------------+-------+ 7 1 row in set而系統一個磁碟塊的儲存空間往往沒有這麼大,因此InnoDB每次申請磁碟空間時都會是若干地址連續磁碟塊來達到頁的大小16KB。 InnoDB在把磁碟資料讀入到磁碟時會以頁為基本單位,在查詢資料時如果一個頁中的每條資料都能有助於定位資料記錄的位置, 這將會減少磁碟I/O次數,提高查詢效率。 B-Tree結構的資料可以讓系統高效的找到資料所在的磁碟塊。為了描述B-Tree,首先定義一條記錄為一個二元組[key, data] ,key為記錄的鍵值,對應表中的主鍵值,data為一行記錄中除主鍵外的資料。對於不同的記錄,key值互不相同。 一棵m階的B-Tree有如下特性: 1. 每個節點最多有m個孩子。 2. 除了根節點和葉子節點外,其它每個節點至少有Ceil(m/2)個孩子。 3. 若根節點不是葉子節點,則至少有2個孩子 4. 所有葉子節點都在同一層,且不包含其它關鍵字資訊 5. 每個非終端節點包含n個關鍵字資訊(P0,P1,…Pn, k1,…kn) 6. 關鍵字的個數n滿足:ceil(m/2)-1 <= n <= m-1 7. ki(i=1,…n)為關鍵字,且關鍵字升序排序。 8. Pi(i=1,…n)為指向子樹根節點的指標。P(i-1)指向的子樹的所有節點關鍵字均小於ki,但都大於k(i-1) B-Tree中的每個節點根據實際情況可以包含大量的關鍵字資訊和分支,如下圖所示為一個3階的B-Tree: 每個節點佔用一個盤塊的磁碟空間,一個節點上有兩個升序排序的關鍵字和三個指向子樹根節點的指標,指標儲存的是子節點所在磁碟塊的地址。兩個鍵值資料劃分成的三個範圍域對應三個指標指向的子樹的資料的範圍域。以根節點為例,兩個鍵值資料為33和66,P1指標指向的子樹的資料範圍為小於33,P2指標指向的子樹的資料範圍為33~66之間,P3指標指向的子樹的資料範圍為大於66。 模擬查詢關鍵字55的過程: 1、根據根節點找到磁碟塊Disk1,讀入記憶體。第1次操作磁碟I/O。 2、比較鍵值55在區間(33,66),找到磁碟塊Disk1的指標P2。 3、根據P2指標找到磁碟塊Disk3,讀入記憶體。第2次操作磁碟I/O。 4、比較鍵值55在區間(39,62),找到磁碟塊Disk3的指標P2。 5、根據P2指標找到磁碟塊Disk8,讀入記憶體。第3次操作磁碟I/O。 6、在Disk8中的鍵值列表中找到關鍵字55。 通過上面的操作過程,發現需要3次磁碟I/O操作,和3次記憶體查詢操作。由於記憶體中的關鍵字是一個有序表結構,可以利用二分法查詢提高效率。而3次磁碟I/O操作是影響整個B-Tree查詢效率的決定因素。 B-Tree相對於AVLTree縮減了節點個數,使每次磁碟I/O取到記憶體的資料都發揮了作用,從而提高了查詢效率。
B+Tree
B+Tree是在B-Tree基礎上的一種優化,使其更適合實現外儲存索引結構,InnoDB儲存引擎就是用B+Tree實現其索引結構。 從上面的B-Tree結構圖中可以看到每個節點中不僅包含資料的key值,還有data值。而每一個頁的儲存空間是有限的,如果data資料較大時將會導致每個節點(即一個頁)能儲存的key的數量很小,當儲存的資料量很大時同樣會導致B-Tree的深度較大,增大查詢時的磁碟I/O次數,進而影響查詢效率。在B+Tree中,所有資料記錄節點都是按照鍵值大小順序存放在同一層的葉子節點上,而非葉子節點上只儲存key值資訊,這樣可以大大加大每個節點儲存的key值數量,降低B+Tree的高度,提高查詢效率。 B+Tree相比較於B-Tree的不同點: 1、非葉子節點只儲存鍵值資訊。 2、所有葉子節點之間都有一個鏈指標。 3、資料記錄都存放在葉子節點中。 將上面的B-Tree優化,由於B+Tree的非葉子節點只儲存鍵值資訊,假設每個磁碟塊能儲存4個鍵值及指標資訊,則變成B+Tree後其結構如下圖所示: 通常在B+Tree上有兩個頭指標,一個指向根節點,另一個指向關鍵字最小的葉子節點,而且所有葉子節點(即資料節點)之間是一種鏈式環結構。因此可以對B+Tree進行兩種查詢運算:一種是對於主鍵的範圍查詢和分頁查詢,另一種是從根節點開始,進行隨機查詢。 可能上面例子中只有22條資料記錄,看不出B+Tree的優點,下面做一個推算: InnoDB儲存引擎中頁的大小為16KB,一般表的主鍵型別為INT(佔用4個位元組)或BIGINT(佔用8個位元組),指標型別也一般為4或8個位元組,也就是說一個頁(B+Tree中的一個節點)中大概儲存16KB/(8B+8B)=1K個鍵值(因為是估值,為方便計算,這裡的K取值為〖10〗^3)。也就是說一個深度為3的B+Tree索引可以維護10^3 * 10^3 * 10^3 = 10億 條記錄。 實際情況中每個節點可能不能填充滿,因此在資料庫中,B+Tree的高度一般都在2~4層。mysql的InnoDB儲存引擎在設計時是將根節點常駐記憶體的,也就是說查詢某一鍵值的行記錄時最多隻需要1~3次磁碟I/O操作。 資料庫中的B+Tree索引可以分為聚集索引(clustered index)和輔助索引(secondary index)。上面的B+Tree示例圖在資料庫中的實現即為聚集索引,聚集索引的B+Tree中的葉子節點存放的是整張表的行記錄資料。輔助索引與聚集索引的區別在於輔助索引的葉子節點並不包含行記錄的全部資料,而是儲存相應行資料的聚集索引鍵,即主鍵。當通過輔助索引來查詢資料時,InnoDB儲存引擎會遍歷輔助索引找到主鍵,然後再通過主鍵在聚集索引中找到完整的行記錄資料。總結
根據上面,二叉查詢樹,紅黑樹等資料結構也可以用來實現索引,但是檔案系統及資料庫系統普遍採用B+Tree作為索引結構(目前MySQL的MYISAM 和 INNODB 都是採用B+Tree作為索引結構),這是因為B+Tree索引的設計是以計算機磁碟儲存結構為理論基礎的。 索引以索引檔案的形式儲存在磁碟上,當採用B+Tree查詢的時候,產生磁碟I/O消耗對效能的影響比其他方式小很多(評價一個數據結構作為索引的優劣最重要的指標就是在查詢過程中磁碟I/O操作次數的漸進複雜度)。 換句話說,索引的結構組織要儘量減少查詢過程中磁碟I/O的存取次數,而B+Tree無疑是較優的演算法。