淺談索引系列之基本原理
注意:本文談論的索引僅限最常用的標準B*Tree索引,點陣圖索引,聚簇索引不在本文討論範圍內,以下原理並不適用。
關於索引前段時間已經寫了淺談索引序列之是否可以儲存NULL值?淺談索引系列之聚簇因子(clustering_factor)兩篇博文,之所以再返回來寫基本原理是因為發現自己對索引的基本原理了解的並不是很深入,翻閱了相關資料後整理了幾個索引的重要知識點如下,和大家共享。
1.B*Tree索引中索引條目是按照鍵值排序儲存的。
2.B*Tree索引儲存空間可以進行預先估算。
3.葉子節點之間相互指向,在結合1,因此索引條目按照鍵值構成了一個雙向有序連結串列
下面透過具體的例子進行說明:
對索引進行treedump,為了便於閱讀關於treedump方法請參考淺談索引序列之是否可以儲存NULL值?博文,取trace日誌中任一葉子節點進行檢視
leaf: 0x1002ff8 16789496 (3: nrow: 479 rrow: 479)
開啟dump出來的trace檔案,限於篇幅只擷取部分,可以清晰的看到雖然表是按照OBJECT_NAME的依次插入記錄的,但是索引條目依然按照OBJECT_ID的順序儲存在塊中。
row#0[8019] flag: ------, lock: 0, len=13
col 0; len 3; (3): c2 15 58
col 1; len 6; (6): 01 00 2f bb 00 19
row#1[8006] flag: ------, lock: 0, len=13
col 0; len 3; (3): c2 15 59
col 1; len 6; (6): 01 00 2f c2 00 43
row#2[7993] flag: ------, lock: 0, len=13
col 0; len 3; (3): c2 15 5a
col 1; len 6; (6): 01 00 2f bb 00 17
row#3[7980] flag: ------, lock: 0, len=13
col 0; len 3; (3): c2 15 5b
col 1; len 6; (6): 01 00 2f c2 00 47
row#4[7967] flag: ------, lock: 0, len=13
col 0; len 3; (3): c2 15 5c
col 1; len 6; (6): 01 00 2f bb 00 1b
row#5[7954] flag: ------, lock: 0, len=13
col 0; len 3; (3): c2 15 5d
col 1; len 6; (6): 01 00 2f c2 00 49
關於索引前段時間已經寫了淺談索引序列之是否可以儲存NULL值?淺談索引系列之聚簇因子(clustering_factor)兩篇博文,之所以再返回來寫基本原理是因為發現自己對索引的基本原理了解的並不是很深入,翻閱了相關資料後整理了幾個索引的重要知識點如下,和大家共享。
1.B*Tree索引中索引條目是按照鍵值排序儲存的。
2.B*Tree索引儲存空間可以進行預先估算。
3.葉子節點之間相互指向,在結合1,因此索引條目按照鍵值構成了一個雙向有序連結串列
下面透過具體的例子進行說明:
索引鍵值順序
建立測試表和索引點選(此處)摺疊或開啟
- drop table test;
-
create table test as select * from all_objects order by OBJECT_NAME;
- create index test_pk on test(object_id);
leaf: 0x1002ff8 16789496 (3: nrow: 479 rrow: 479)
點選(此處)摺疊或開啟
-
SQL> SELECT dbms_utility.data_block_address_file(16789496),dbms_utility.data_block_address_block(16789496) FROM dual;
-
-
DBMS_UTILITY.DATA_BLOCK_ADDRESS_FILE(16789496) DBMS_UTILITY.DATA_BLOCK_ADDRESS_BLOCK(16789496)
-
---------------------------------------------- -----------------------------------------------
- 4 12280
- SQL> alter system dump datafile 4 block 12280;
row#0[8019] flag: ------, lock: 0, len=13
col 0; len 3; (3): c2 15 58
col 1; len 6; (6): 01 00 2f bb 00 19
row#1[8006] flag: ------, lock: 0, len=13
col 0; len 3; (3): c2 15 59
col 1; len 6; (6): 01 00 2f c2 00 43
row#2[7993] flag: ------, lock: 0, len=13
col 0; len 3; (3): c2 15 5a
col 1; len 6; (6): 01 00 2f bb 00 17
row#3[7980] flag: ------, lock: 0, len=13
col 0; len 3; (3): c2 15 5b
col 1; len 6; (6): 01 00 2f c2 00 47
row#4[7967] flag: ------, lock: 0, len=13
col 0; len 3; (3): c2 15 5c
col 1; len 6; (6): 01 00 2f bb 00 1b
row#5[7954] flag: ------, lock: 0, len=13
col 0; len 3; (3): c2 15 5d
col 1; len 6; (6): 01 00 2f c2 00 49
...
那麼我們看看再次插入OBJECT_ID=2088的記錄效果怎麼樣?
row#0[8019] flag: ------, lock: 0, len=13
col 0; len 3; (3): c2 15 58
col 1; len 6; (6): 01 00 2f bb 00 19
row#1[8006] flag: ------, lock: 0, len=13
col 0; len 3; (3): c2 15 59
col 1; len 6; (6): 01 00 2f c2 00 43
row#2[1797] flag: ------, lock: 2, len=13
col 0; len 3; (3): c2 15 59
col 1; len 6; (6): 01 00 30 6b 00 00
row#3[7993] flag: ------, lock: 0, len=13
col 0; len 3; (3): c2 15 5a
col 1; len 6; (6): 01 00 2f bb 00 17
row#4[7980] flag: ------, lock: 0, len=13
col 0; len 3; (3): c2 15 5b
col 1; len 6; (6): 01 00 2f c2 00 47
row#5[7967] flag: ------, lock: 0, len=13
col 0; len 3; (3): c2 15 5c
col 1; len 6; (6): 01 00 2f bb 00 1b
1.首先計算出每個葉子節點索引條目佔用的位元組數byte_per_lnode,7372.8/byte_per_lnode進而計算出每個葉子節點儲存多少索引條目entries_per_lnode,表的總行數total_num/entries_per_lnode得到葉子節點的總個數lnodes,因此葉子節點佔用的總空間為:lnodes*8k
2.計算出每個分支節點索引條目佔用的位元組數byte_per_bnode,7372.8/byte_per_bnode進而計算出每個分支節點儲存多少索引條目entries_per_bnode,用步驟1中lnodes/byte_per_bnode得到分支節點的總個數bnodes,因此最底層分支節點佔用的總空間為bnodes*8k
3.檢查分支節點總個數是否超過了byte_per_bnode,如果大於byte_per_bnode說明至少還有兩層分支節點,需要再重複步驟2的情況。否則計算空間完成。
因此索引佔用的儲存空間為:lnodes*8k+bnodes*8k,注意儲存空間的大小僅為估算,加上一小部分管理空間,實際佔用空間可能會比預算空間大一些。
那麼我們看看再次插入OBJECT_ID=2088的記錄效果怎麼樣?
點選(此處)摺疊或開啟
- SQL> insert into test select * from dba_objects where object_id=2088;
- 1 row created.
- SQL> commit;
- Commit complete.
- SQL> alter system dump datafile 4 block 12280;
- System altered.
col 0; len 3; (3): c2 15 58
col 1; len 6; (6): 01 00 2f bb 00 19
row#1[8006] flag: ------, lock: 0, len=13
col 0; len 3; (3): c2 15 59
col 1; len 6; (6): 01 00 2f c2 00 43
row#2[1797] flag: ------, lock: 2, len=13
col 0; len 3; (3): c2 15 59
col 1; len 6; (6): 01 00 30 6b 00 00
row#3[7993] flag: ------, lock: 0, len=13
col 0; len 3; (3): c2 15 5a
col 1; len 6; (6): 01 00 2f bb 00 17
row#4[7980] flag: ------, lock: 0, len=13
col 0; len 3; (3): c2 15 5b
col 1; len 6; (6): 01 00 2f c2 00 47
row#5[7967] flag: ------, lock: 0, len=13
col 0; len 3; (3): c2 15 5c
col 1; len 6; (6): 01 00 2f bb 00 1b
發現了什麼?索引中新增了一鍵值為c2 15 59(2088)的索引條目。無論記錄在表中是怎麼雜亂無章儲存的還是按照有序的,一旦建立索引後,索引條目都是按照索引列為順序來進行儲存的,這裡就引出了聚簇因子概念,具體聚簇因子的概念在這裡不再贅述。另外執行計劃中INDEX FULL SCAN也是基於索引鍵值有序這個原理。
葉子節點索引條目的資料結構如下:
分支節點索引條目的資料結構如下:
對於預設段,其預設的PCTFREE為10%,也就是說最多隻能使用其中的90%,因此對於一個大小為8K的塊來說僅有8192*90%=7372.8位元組可用。儲存空間估算
生產環境任何操作都可能存在隱患,導致業務無法正常對外服務。建立索引也不例外,如何預先評估索引佔用的空間,防止表空間被佔滿可能是每位DBA必備的技能,下面將結合索引條目的結構來說明一下索引的佔用情況。葉子節點索引條目的資料結構如下:
分支節點索引條目的資料結構如下:
1.首先計算出每個葉子節點索引條目佔用的位元組數byte_per_lnode,7372.8/byte_per_lnode進而計算出每個葉子節點儲存多少索引條目entries_per_lnode,表的總行數total_num/entries_per_lnode得到葉子節點的總個數lnodes,因此葉子節點佔用的總空間為:lnodes*8k
2.計算出每個分支節點索引條目佔用的位元組數byte_per_bnode,7372.8/byte_per_bnode進而計算出每個分支節點儲存多少索引條目entries_per_bnode,用步驟1中lnodes/byte_per_bnode得到分支節點的總個數bnodes,因此最底層分支節點佔用的總空間為bnodes*8k
3.檢查分支節點總個數是否超過了byte_per_bnode,如果大於byte_per_bnode說明至少還有兩層分支節點,需要再重複步驟2的情況。否則計算空間完成。
因此索引佔用的儲存空間為:lnodes*8k+bnodes*8k,注意儲存空間的大小僅為估算,加上一小部分管理空間,實際佔用空間可能會比預算空間大一些。
雙向有序連結串列
在trace出索引葉子塊的資訊後,有如下關鍵字
kdxlenxt 16789497=0x1002ff9
kdxleprv 16789495=0x1002ff7
其中kdxlenxt為下一個葉子節點的地址,kdxleprv為上一個葉子節點的地址。每個葉子節點都有此資訊標記了上一個以及下一個葉子節點的資訊,因此葉子節點構成了一個索引鍵值有序的雙向有序連結串列,這種有序結構某些情況下避免了再次排序,節省了oracle的硬體資源,提高了效率。
kdxlenxt 16789497=0x1002ff9
kdxleprv 16789495=0x1002ff7
其中kdxlenxt為下一個葉子節點的地址,kdxleprv為上一個葉子節點的地址。每個葉子節點都有此資訊標記了上一個以及下一個葉子節點的資訊,因此葉子節點構成了一個索引鍵值有序的雙向有序連結串列,這種有序結構某些情況下避免了再次排序,節省了oracle的硬體資源,提高了效率。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/29827284/viewspace-2134526/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 淺談索引系列之索引重建索引
- 淺談索引系列之本地索引與全域性索引索引
- 淺談索引系列之聚簇因子(clustering_factor)索引
- 淺談分散式儲存之SSD基本原理分散式
- 淺談Mysql索引MySql索引
- 淺談sql索引SQL索引
- MySQL淺談(索引、鎖)MySql索引
- 淺談索引序列之是否可以儲存NULL值?索引Null
- 淺談聚簇索引與非聚簇索引索引
- 淺談oracle中重建索引 (ZT)Oracle索引
- 淺談MySQL的B樹索引與索引優化MySql索引優化
- FAQ系列|MySQL索引之聚集索引MySql索引
- 淺談正規表示式背後的基本原理
- 淺談.NET下的多執行緒和平行計算系列文章索引執行緒索引
- 淺談HTTP之URLHTTP
- 淺談OpenGL之DSA
- Spring5.0原始碼學習系列之淺談BeanFactory建立Spring原始碼Bean
- 淺談Nginx之反向代理Nginx
- 淺談 Laravel 之探秘 SoftDeletesLaraveldelete
- 淺談軟體開發定律系列之布魯克斯定律薦
- Oracle索引梳理系列(六)- Oracle索引種類之函式索引Oracle索引函式
- 深入理解MySQL系列之索引MySql索引
- Java基礎之淺談介面Java
- Java基礎之淺談集合Java
- 淺談 Laravel 之探祕 SoftDeletesLaraveldelete
- 關於ORACLE組合索引內部儲存淺談Oracle索引
- K8s 系列(四) - 淺談 InformerK8SORM
- 淺談java之設計模式(1)Java設計模式
- Java基礎之淺談泛型Java泛型
- Gradle技巧之語法淺談Gradle
- 淺談WPF之MVVM工具包MVVM
- 淺談WPF之屬性系統
- 淺談MySql整型索引和字串索引失效或隱式轉換問題汊叄MySql索引字串
- 淺入淺出 MySQL 索引MySql索引
- 淺淺談ReduxRedux
- [原創]淺談勝新系列軟體的破解
- JS核心系列:淺談 call apply 與 bindJSAPP
- JS核心系列:淺談 原型物件和原型鏈JS原型物件