(3.8)存儲引擎--索引的遍歷與維護
一、遍歷
索引樹的每個節點都是一個頁面。
索引樹有三種類型的節點:根節點、中間節點、葉子節點。
(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、頁空間充足
在已存在數據的表上,創建或重建索引時,可指定填充因子,即在索引樹的每個節點上預留一定的空間,供表中後續增加的數據使用。但如果在創建表的時候就創建了索引,並指定了填充因子,這時的填充因子是無用的,數據庫系統不會刻意去保留頁面的空間。
索引頁面有剩余空間的情況如下圖:
圖1
參考圖1,此時向索引樹中插入一條索引鍵值為31的記錄,步驟如下:
(1)執行索引鍵值=31的查找操作,確定該新記錄應該插入到葉子節點L2中。
(2)檢查L2上是否有足夠的空間來存放當前記錄,這裏假設有足夠的空間;
(3)將記錄45向後移動,插入索引鍵值為31的新記錄。插入之後,10、30、31、45還是順序的,如下圖:
圖2
2.2、頁空間不足
參加圖2,此時再插入一條索引鍵值為32的記錄,步驟如下:
(1)執行索引鍵值=32的查找操作,確定該新記錄應該插入到葉子節點L2中;
(2)檢查L2上是否有足夠的空間來存放當前記錄,這時發現沒有足夠的頁空間,此時需要進行頁面分裂;
(3)向數據庫系統申請一個新的頁面L4,將L2的一半數據移到L4中,並重新鏈接葉子的左右節點,如下圖:
圖3
(4)此時,上層節點也需要生成一個新的葉子節點的指針。這裏的上層節點即根節點,如果上層節點沒有剩余空間的話,同樣也需要進行分裂,這裏有剩余空間,如下圖:
圖4
(5)因為當前記錄的鍵值範圍位於頁分裂的後一半中,將索引鍵值為32的新記錄插入到L4中,如果鍵值範圍位於前一半,則插入到L2中。如果L4的空間不夠存放鍵值為32的新記錄,則L4會繼續進行頁分裂,這裏假設空間足夠,插入結束,如下圖:
圖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)堆表中的數據行被刪除。
轉自:http://blog.51cto.com/qianzhang/1217600
(3.8)存儲引擎--索引的遍歷與維護