一、引言
對資料庫索引的關注從未淡出我的們的討論,那麼資料庫索引是什麼樣的?叢集索引與非叢集索引有什麼不同?希望本文對各位同仁有一定的協助。有不少存疑的地方,誠心希望各位不吝賜教指正,共同進步。
二、B-Tree
我們常見的資料庫系統,其索引使用的資料結構多是B-Tree或者B+Tree。例如,MsSql使用的是B+Tree,Oracle及Sysbase使用的是B-Tree。所以在最開始,簡單地介紹一下B-Tree。
B-Tree不同於Binary Tree二叉樹,最多有兩個子樹),一棵M階的B-Tree滿足以下條件:
1)每個結點至多有M個孩子;
2)除根結點和葉結點外,其它每個結點至少有M/2個孩子;
3)根結點至少有兩個孩子除非該樹僅包含一個結點);
4)所有葉結點在同一層,葉結點不包含任何關鍵字資訊;
5)有K個關鍵字的非葉結點恰好包含K+1個孩子;
另外,對於一個結點,其內部的關鍵字是從小到大排序的。以下是B-TreeM=4)的範例:
對於每個結點,主要包含一個關鍵字數組Key[],一個指標數組指向兒子)Son[]。在B-Tree內,尋找的流程是:使用順序尋找數組長度較短時)或折半尋找方法尋找Key[]數組,若找到關鍵字K,則返回該結點的地址及K在Key[]中的位置;否則,可確定K在某個Key[i]和Key[i+1]之間,則從Son[i]所指的子結點繼續尋找,直到在某結點中尋找成功;或直至找到葉結點且葉結點中的尋找仍不成功時,尋找過程失敗。
接著,我們使用以片示範如何產生B-TreeM=4,依次插入1~6):
從圖可見,當我們插入關鍵字4時,由於原結點已經滿了,故進行分裂,基本按一半的原則進行分裂,然後取出中間的關鍵字2,升級這裡是成為根結點)。其它的依類推,就是這樣一個大概的過程。
三、資料庫索引
1.什麼是索引
在資料庫中,索引的含義與日常意義上的“索引”一詞並無多大區別想想小時候查字典),它是用於提高資料庫表資料訪問速度的資料庫物件。
A)索引可以避免全表掃描。多數查詢可以僅掃描少量索引頁及資料頁,而不是遍曆所有資料頁。
B)對於非叢集索引,有些查詢甚至可以不訪問資料頁。
C)叢集索引可以避免資料插入操作集中於表的最後一個資料頁。
D)一些情況下,索引還可用於避免排序操作。
當然,眾所周知,雖然索引可以提高查詢速度,但是它們也會導致資料庫系統更新資料的效能下降,因為大部分資料更新需要同時更新索引。
2.索引的儲存
一條索引記錄中包含的基本資料包括:索引值即你定義索引時指定的所有欄位的值)+邏輯指標指向資料頁或者另一索引頁)。
當你為一張空表建立索引時,資料庫系統將為你分配一個索引頁,該索引頁在你插入資料前一直是空的。此頁此時既是根結點,也是葉結點。每當你往表中插入一行資料,資料庫系統即向此根結點中插入一行索引記錄。當根結點滿時,資料庫系統大抵按以下步驟進行分裂:
A)建立兩個兒子結點
B)將原根結點中的資料近似地拆成兩半,分別寫入新的兩個兒子結點
C)根結點中加上指向兩個兒子結點的指標
通常狀況下,由於索引記錄僅包含索引欄位值以及4-9位元組的指標),索引實體比真實的資料行要小許多,索引頁相較資料頁來說要密集許多。一個索引頁可以儲存數量更多的索引記錄,這意味著在索引中尋找時在I/O上占很大的優勢,理解這一點有助於從本質上瞭解使用索引的優勢。
3.索引的類型
A)叢集索引,表資料按照索引的順序來儲存的。對於叢集索引,葉子結點即儲存了真實的資料行,不再有另外單獨的資料頁。
B)非叢集索引,表資料存放區順序與索引順序無關。對於非叢集索引,葉結點包含索引欄位值及指向資料頁資料行的邏輯指標,該層緊鄰資料頁,其行數量與資料表行資料量一致。
在一張表上只能建立一個叢集索引,因為真實資料的物理順序只可能是一種。如果一張表沒有叢集索引,那麼它被稱為“堆集”Heap)。這樣的表中的資料行沒有特定的順序,所有的新行將被添加的表的末尾位置。
4.叢集索引
在叢集索引中,葉結點也即資料結點,所有資料行的儲存順序與索引的儲存順序一致。
1)叢集索引與查詢操作
如,我們在名字欄位上建立叢集索引,當需要在根據此欄位尋找特定的記錄時,資料庫系統會根據特定的系統資料表尋找的此索引的根,然後根據指標尋找下一個,直到找到。例如我們要查詢“Green”,由於它介於[Bennet,Karsen],據此我們找到了索引頁1007,在該頁中“Green”介於[Greane, Hunter]間,據此我們找到葉結點1133也即資料結點),並最終在此頁中找以了目標資料行。
此次查詢的IO包括3個索引頁的查詢其中最後一次實際上是在資料頁中查詢)。這裡的尋找可能是從磁碟讀取(Physical Read)或是從緩衝中讀取(Logical Read),如果此表訪問頻率較高,那麼索引樹中較高層的索引很可能在緩衝中被找到。所以真正的IO可能小於上面的情況。
2)叢集索引與插入操作
最簡單的情況下,插入操作根據索引找到對應的資料頁,然後通過挪動已有的記錄為新資料騰出空間,最後插入資料。
如果資料頁已滿,則需要拆分資料頁頁面分割是一種耗費資源的操作,一般資料庫系統中會有相應的機制要盡量減少頁面分割的次數,通常是通過為每頁預留空間來實現):
A)在該使用的資料區段extent)上分配新的資料頁,如果資料區段已滿,則需要分配新段。
B)調整索引指標,這需要將相應的索引頁讀入記憶體並加鎖。
C)大約有一半的資料行被歸入新的資料頁中。
D)如果表還有非叢集索引,則需要更新這些索引指向新的資料頁。
特殊情況:
A)如果新插入的一條記錄包含很大的資料,可能會分配兩個新資料頁,其中之一用來儲存新記錄,另一儲存從原頁中拆分出來的資料。
B)通常資料庫系統中會將重複的資料記錄儲存於相同的頁中。
C)類似於自增列為叢集索引的,資料庫系統可能並不拆分資料頁,頁只是簡單的新添資料頁。
3)叢集索引與刪除操作
刪除行將導致其下方的資料行向上移動以填充刪除記錄造成的空白。
如果刪除的行是該資料頁中的最後一行,那麼該資料頁將被回收,相應的索引頁中的記錄將被刪除。如果回收的資料頁位於跟該表的其它資料頁相同的段上,那麼它可能在隨後的時間內被利用。如果該資料頁是該段的唯一一個資料頁,則該段也被回收。
對於資料的刪除操作,可能導致索引頁中僅有一條記錄,這時,該記錄可能會被移至鄰近的索引頁中,原索引頁將被回收,即所謂的“索引合并”。
5.非叢集索引
非叢集索引與叢集索引相比:
A)葉子結點並非資料結點
B)葉子結點為每一真正的資料行儲存一個“鍵-指標”對
C)葉子結點中還儲存了一個指標位移量,根據頁指標及指標位移量可以定位到具體的資料行。
D)類似的,在除葉結點外的其它索引結點,儲存的也是類似的內容,只不過它是指向下一級的索引頁的。
叢集索引是一種稀疏索引,資料頁上一級的索引頁儲存的是頁指標,而不是行指標。而對於非叢集索引,則是密集索引,在資料頁的上一級索引頁它為每一個資料行儲存一條索引記錄。