一、遍曆
索引樹的每個節點都是一個頁面。
索引樹有三種類型的節點:根節點、中間節點、葉子節點。
(1) 根節點與中間節點一樣,只包含下一層節點的入口值與入口指標,它們稱為索引節點;
(2) 葉子節點包含要遍曆的資料,對叢集索引而言資料就是表中資料行,對非叢集索引資料是指索引列值和行書籤。
索引的遍曆總是從根節點開始,即先根遍曆,分為兩種:索引掃描和索引尋找。
(1) 索引掃描,是指從索引樹的根節點開始,對葉子節點逐個掃描,直至命中所有滿足尋找條件的資料;
(2) 索引尋找,是指從索引樹的根節點開始,按尋找值在索引節點中根據路由資訊跳轉,直至葉子節點以命中資料。
B+樹的深度通常小於等於3,計算如下:
以叢集索引為例,簡單計算如下:10個INT列寬度總和為40B,假設叢集索引樹每一層為二叉,共三層,即2^0+2^1+2^2=1*(1-2^3)/(1-2)=7個頁面,4個葉子節點,每個頁面8060K可儲存8060000/40=201500行,乘以4=806000行,如果是三叉、四叉,那麼三層可儲存上千萬至億行的資料,當然在資料量達到這個等級時,通常我們會選擇表分區,那麼B樹深度就更不會突破三層了。
所以索引尋找的效率是很高的,在查詢中應該努力構造索引尋找,避免索引掃描。
二、插入
2.1、頁空間充足
在已存在資料的表上,建立或重建索引時,可指定填滿因數,即在索引樹的每個節點上預留一定的空間,供表中後續增加的資料使用。但如果在建立表的時候就建立了索引,並指定了填滿因數,這時的填滿因數是無用的,資料庫系統不會刻意去保留頁面的空間。
索引頁面有剩餘空間的情況如:
650) this.width=650;" src="http://www.bkjia.com/uploads/allimg/131228/223KJJ1-0.jpg" style="float:none;" title="6.1_query.jpg" data-pinit="registered" />
圖1
參考圖1,此時向索引樹中插入一條索引索引值為31的記錄,步驟如下:
1)執行索引索引值=31的尋找操作,確定該新記錄應該插入到葉子節點L2中。
2)檢查L2上是否有足夠的空間來存放目前記錄,這裡假設有足夠的空間;
3)將記錄45向後移動,插入索引索引值為31的新記錄。插入之後,10、30、31、45還是順序的,如:
650) this.width=650;" src="http://www.bkjia.com/uploads/allimg/131228/223KH0F-1.jpg" style="float:none;" title="6.2_insert1.jpg" data-pinit="registered" />
圖2
2.2、頁空間不足
參加圖2,此時再插入一條索引索引值為32的記錄,步驟如下:
1)執行索引索引值=32的尋找操作,確定該新記錄應該插入到葉子節點L2中;
2)檢查L2上是否有足夠的空間來存放目前記錄,這時發現沒有足夠的頁空間,此時需要進行頁面分裂;
3)向資料庫系統申請一個新的頁面L4,將L2的一半資料移到L4中,並重新連結葉子的左右節點,如:
650) this.width=650;" src="http://www.bkjia.com/uploads/allimg/131228/223KL1C-2.jpg" style="float:none;" title="6.3_insert2.jpg" data-pinit="registered" />
圖3
4)此時,上層節點也需要產生一個新的葉子節點的指標。這裡的上層節點即根節點,如果上層節點沒有剩餘空間的話,同樣也需要進行分裂,這裡有剩餘空間,如:
650) this.width=650;" src="http://www.bkjia.com/uploads/allimg/131228/223KG123-3.jpg" style="float:none;" title="6.4_insert3.jpg" data-pinit="registered" />
圖4
5)因為目前記錄的索引值範圍位於頁分裂的後一半中,將索引索引值為32的新記錄插入到L4中,如果索引值範圍位於前一半,則插入到L2中。如果L4的空間不夠存放索引值為32的新記錄,則L4會繼續進行頁分裂,這裡假設空間足夠,插入結束,如:
650) this.width=650;" src="http://www.bkjia.com/uploads/allimg/131228/223KI094-4.jpg" style="float:none;" title="6.5_insert4.jpg" data-pinit="registered" />
圖5
三、刪除
3.1、刪除葉子節點中的記錄
參考圖5,刪除索引索引值為32的記錄,步驟如下:
1)執行索引索引值=32的尋找操作,確定該記錄在L4中;
2)將索引索引值=32的記錄標記為虛影,但並不立即釋放空間,準刪除記錄可用於交易回復、多版本等;
3)如果此時L4上的準刪除記錄空間被申請使用,準刪除記錄就會被擦除;
4)如果資料頁面最後一條記錄也被刪除,資料頁面會被回收;
3.2、刪除非葉子節點中的記錄
1)索引節點中的指標被刪除時並不是準刪除記錄,但同樣也不釋放空間,直到有新的指標插入時,才會進行空間壓縮;
2)堆表中資料行被刪除後,頁空間不會被回收,即使是空閑分頁也還是標識為分配狀態,無法被其他對象使用;
註:從理論上講,在兄弟節點頁面空閑空間都小於50%時,應該將兄弟節點合并,即分裂的逆操作,但這樣可能帶來的後果是更頻繁的頁面合并、分裂,成本更大,所以在資料庫系統中通常不進行頁面合併作業,除非rebuild/reorganize索引。
四、更新
4.1、覆蓋更新
如果更新操作能夠在頁內進行原位索引值替換,那麼就進行覆蓋更新。
4.2、非覆蓋更新
無法進行覆蓋更新時,更新操作被分解為刪除和插入操作。
如果非覆蓋更新過程中,新的記錄比較長,則會在頁面分裂的過程中會帶來資料行的移動:
1)叢集索引的移動對非叢集索引沒有影響,因為非叢集索引中儲存的是叢集索引的索引值,分裂並不會改變索引值;
2)堆表中的資料頁分裂,會在原記錄處留下一個前轉指標,以告訴非叢集索引去哪裡找新的記錄;
所以資料行的移動對非叢集索引都不會帶來維護的成本,非叢集索引的維護成本來自書籤的變化:
1)叢集索引的索引值發生變化或被刪除;
2)堆表中的資料行被刪除。
本文出自 “SQL Server DBA” 部落格,請務必保留此出處http://qianzhang.blog.51cto.com/317608/1217600