由淺入深理解索引的實現
教科書上的b+tree是乙個簡化了的,方便於研究和教學的b+tree。然而在資料庫實現時,為了更好的效能或者降低實現的難度,都會在細節上進行一定的變化。下面以innodb為例,來說說這些變化。
04 - sparse index中的資料指標
在「由淺入深理解索引的實現
第一部分」中提到,sparse index中的每個鍵值都有乙個指標指向所在的資料頁。這樣每個b+tree都有指標指向資料頁。如圖fig.1所示:
fig.1
如果資料頁進行了拆分或合併操作,那麼所有的b+tree都需要修改相應的頁指標。特別是secondary b+tree(輔助索引對應的b+tree), 要對很多個不連續的頁進行修改。同時也需要對這些頁加鎖,這會降低併發性。
為了降低難度和增加更新(**和合併b+tree節點)的效能,innodb 將 secondary b+tree中的指標替換成了主鍵的鍵值。如圖fig.2所示:
fig.2
這樣就去除了secondary b+tree對資料頁的依賴,而資料就變成了clustered b+tree(簇索引對應的b+tree)獨佔的了。對資料頁的拆分及合併操作,僅影響clustered b+tree. 因此innodb的資料檔案中儲存的實際上就是多個孤立b+tree。
接下來看一下資料操作在b+tree上的基本實現。
- 用主鍵查詢
直接在clustered b+tree上查詢。
- 用輔助索引查詢
a. 在secondary b+tree上查詢到主鍵。
b. 用主鍵在clustered b+tree
可以看出,在使用主鍵值替換頁指標後,輔助索引的查詢效率降低了。
a. 盡量使用主鍵來查詢資料(索引遍歷操作除外).
b. 可以通過快取來彌補效能,因此所有的鍵列,都應該盡量的小。
- insert
a. 在clustered b+tree上插入資料
b. 在所有其他secondary b+tree上插入主鍵。
- delete
a. 在clustered b+tree上刪除資料。
b. 在所有其他secondary b+tree上刪除主鍵。
- update 非鍵列
a. 在clustered b+tree上更新資料。
- update 主鍵列
a. 在clustered b+tree刪除原有的記錄(只是標記為deleted,並不真正刪除)。
b. 在clustered b+tree插入新的記錄。
c. 在每乙個secondary b+tree上刪除原有的資料。(有疑問,看下一節。)
d. 在每乙個secondary b+tree上插入原有的資料。
- update 輔助索引的鍵值
a. 在clustered b+tree上更新資料。
b. 在每乙個secondary b+tree上刪除原有的主鍵。
c. 在每乙個secondary b+tree上插入原有的主鍵。
更新鍵列時,需要更新多個頁,效率比較低。
a. 盡量不用對主鍵列進行update操作。
b. 更新很多時,盡量少建索引。
05 – 非唯一鍵索引
教科書上的b+tree操作,通常都假設」鍵值是唯一的「。但是在實際的應用中secondary index是允許鍵值重複的。在極端的情況下,所有的鍵值都一樣,該如何來處理呢?
innodb 的 secondary b+tree中,主鍵也是此鍵的一部分。
secondary key = 使用者定義的key + 主鍵。如圖fig.3所示:
fig.3
因為主鍵是唯一的,secondary key也是唯一的。按理說,如果輔助索引是唯一的,就不需要這樣做。可是,innodb對所有的secondary b+tree都這樣建立。當然,在插入資料時,還是會根據使用者定義的key,來判斷唯一性。
06 – 對
標準的b+tree的每個節點有k個鍵值和k+1個指標,指向k+1個子節點。如圖fig.4:
fig.4(來自於wikipedia)
而在「由淺入深理解索引的實現(1)」中fig.9的b+tree上,每個節點有k個鍵值和k個指標。innodb的b+tree也是如此。如圖fig.5所示:
fig.5
這樣做的好處在於,鍵值和指標一一對應。我們可以將乙個對看作一條記錄。這樣就可以用資料塊的儲存格式來儲存索引塊。因為不需要為索引塊定義單獨的儲存格式,就降低了實現的難度。
- 插入最小值
當考慮在變形後的b+tree上進行insert操作時,發現了乙個有趣的問題。如果插入的資料的健值比b+tree的最小鍵值小時,就無法定位到乙個適當的資料塊上去(中的key代表了子節點上的鍵值是》=key的)。例如,在fig.5的b+tree中插入鍵值為0的資料時,無法定位到任何節點。
在標準的b+tree上,這樣的鍵值會被定位到最左側的節點上去。這個做法,對於fig.5中的b+tree也是合理的。innodb的做法是,將每一層(葉子層除外)的最左側節點的第一條記錄標記為最小記錄(min_rec).在進行定位操作時,任何鍵值都比標記為min_rec的鍵值大。因此0會被插入到最左側的記錄節點上。如fig.6所示:
fig.6
07 – 順序插入資料
fig.7是b-tree的插入和**過程,我們看看有沒有什麼問題?
fig.7(來自於wikipedia)
標準的b-tree**時,將一半的鍵值和資料移動到新的節點上去。原有節點和新節點都保留一半的空間,用於以後的插入操作。當按照鍵值的順序插入資料時,左側的節點不可能再有新的資料插入。因此,會浪費約一半的儲存空間。
解決這個問題的基本思路是:**順序插入的b-tree時,將原有的資料都保留在原有的節點上。建立乙個新的節點,用來儲存新的資料。順序插入時的**過程如fig.8所示:
fig.8
以上是以b-tree為例,b+tree的**過程類似。innodb的實現以這個思路為基礎,不過要複雜一些。因為順序插入是有方向性的,可能是從小到大,也可能是從大到小的插入資料。所以要區分不同的情況。如果要了解細節,可參考以下函式的**。
btr_page_split_and_insert();
btr_page_get_split_rec_to_right();
btr_page_get_split_rec_to_right();
innodb的**太複雜了,有時候也不敢肯定自己的理解是對的。因此寫了乙個小指令碼,來列印innodb數
據檔案中b+tree。這樣可以直觀的來觀察b+tree的結構,驗證自己的理解是否正確。
原文出處:
由淺入深理解 IOC 和 DI
必須滿足此原則的 才能算作好的可維護的 可實現面向抽象程式設計的語法 只有有了介面和抽象類的概念,多型性才能夠得到很好的支援。面向抽象程式設計的目的 實現可維護的 實現開閉原則。從上面的 可以看出以下幾點 從以上 示例可得出以下幾點 抽象的難點在於將new物件這個操作變得更加的抽象,而不是具體。物件...
理解索引 索引優化
最近有個需求,要修改現有儲存結構,涉及查詢條件和查詢效率的考量,看了幾篇索引和hbase相關的文章,回憶了相關知識,結合專案需求,說說自己的理解和總結。索引結構和資料定位過程 查詢過程和高階查詢 執行計畫詳細介紹 常見優化方法 聯合索引最左字首原則 復合索引遵守 最左字首 原則,查詢條件中,使用了復...
理解索引 索引優化
最近有個需求,要修改現有儲存結構,涉及查詢條件和查詢效率的考量,看了幾篇索引和hbase相關的文章,回憶了相關知識,結合專案需求,說說自己的理解和總結。索引結構和資料定位過程 查詢過程和高階查詢 執行計畫詳細介紹 常見優化方法 聯合索引最左字首原則 復合索引遵守 最左字首 原則,查詢條件中,使用了復...