您好,登錄后才能下訂單哦!
索引與表一樣,也屬于段(segment)的一種。里面存放了用戶的數據,跟表一樣需要占用磁盤空間。只不過,在索引里的數據存放形式與表里的數據存放形式非常的不一樣。在理解索引時,可以想象一本書,其中書的內容就相當于表里的數據,而書前面的目錄就相當于該表的索引。同時,通常情況下,索引所占用的磁盤空間要比表要小的多,其主要作用是為了加快對數據的搜索速度。
但是,索引作為一種可選的數據結構,你可以選擇為某個表里的創建索引,也可以不創建。這是因為一旦創建了索引,就意味著oracle對表進行DML(包括INSERT、UPDATE、DELETE)時,必須處理額外的工作量(也就是對索引結構的維護)以及存儲方面的開銷。所以創建索引時,需要考慮創建索引所帶來的查詢性能方面的提高,與引起的額外的開銷相比,是否值得。
B樹索引是一個典型的樹結構,始終是平衡的,也就是說 從Root節點到 Leaf 節點的任何一個路徑都是等距離的。其包含的組件主要是:
葉子節點(Leaf node):包含條目直接指向表里的數據行。
分支節點(Branch node):包含的條目指向索引里其他的分支節點或者是葉子節點。
根節點(Branch node):一個B樹索引只有一個根節點,它實際就是位于樹的最頂端的分支節點。
對于分支節點塊(包括根節點塊)來說,其所包含的索引條目都是按照順序排列的(可以指定reverse,倒序)。每個索引條目(也可以叫做每條記錄)都具有兩個字段。第一個字段表示當前該分支節點塊下面所鏈接的索引塊中所包含的最小鍵值(按B+tree最小鍵值復制原則);第二個字段為四個字節,表示所鏈接的索引塊的地址,該地址指向下面一個索引塊。在一個分支節點塊中所能容納的記錄行數由數據塊大小以及索引鍵值的長度決定。
對于葉子節點塊來說,其所包含的索引條目與分支節點一樣,都是按照順序排列的(缺省是升序排列,也可以在創建索引時指定為降序排列)。每個索引條目(也可以叫做每條記錄)也具有兩個字段。第一個字段表示索引的鍵值,對于單列索引來說是一個值;而對于多列索引來說則是多個值組合在一起的。第二個字段表示鍵值所對應的記錄行的ROWID,該ROWID是記錄行在表里的物理地址。在葉子節點中,每個索引條目都會在數據塊中占一行空間。每一行用2到3個字節作為行頭,行頭用來存放標記以及鎖定類型等信息。同時,在第一個表示索引的鍵值的字段中,每一個索引列都有1個字節表示數據長度,后面則是該列具體的值。
下面分別把分支節點的索引結構和葉子節點的索引信息dump出來
創建測試數據
[sql]
sys@ORCL> select * from v$version where rownum=1;
BANNER
----------------------------------------------------------------
Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Prod
sys@ORCL> drop table tt purge;
drop table tt purge
*
ERROR at line 1:
ORA-00942: table or view does not exist
sys@ORCL> create table tt as select * from dba_objects;
Table created.
sys@ORCL> select count(*) from tt;
COUNT(*)
----------
50356
sys@ORCL> insert into tt select * from tt;
50356 rows created.
sys@ORCL> commit;
Commit complete.
sys@ORCL> select count(*) from tt;
COUNT(*)
----------
100712
sys@ORCL> create index btree_tt on tt(object_name);
Index created.
查看索引的Blevel、height(blevel:節點的深度。root位于第0層,以此類推。height=blevel+1)
[sql]
sys@ORCL> select index_name,blevel from dba_indexes where index_name='BTREE_TT';
INDEX_NAME BLEVEL
------------------------------ ----------
BTREE_TT 2
sys@ORCL> analyze index btree_tt validate structure;
Index analyzed. sys@ORCL> select name,height from index_stats where name='BTREE_TT';
NAME HEIGHT
------------------------------ ----------
BTREE_TT 3
獲得btree_tt的對象號,進行索引結構的dump
[sql]
sys@ORCL> select object_id from dba_objects where owner='SYS' and object_name='BTREE_TT';
OBJECT_ID
----------
52614
sys@ORCL> oradebug setmypid
Statement processed.
sys@ORCL> alter session set events 'immediate trace name treedump level 52614';
Session altered.
sys@ORCL> oradebug tracefile_name
/u01/app/oracle/admin/orcl/udump/orcl_ora_5234.trc
查看treedump trc文件
[sql]
----- begin tree dump
branch: 0x40efaa 4255658 (0: nrow: 2, level: 2)
branch: 0x40f603 4257283 (-1: nrow: 247, level: 1)
leaf: 0x40efab 4255659 (-1: nrow: 182 rrow: 182)
leaf: 0x40efac 4255660 (0: nrow: 182 rrow: 182)
leaf: 0x40efad 4255661 (1: nrow: 186 rrow: 186)
leaf: 0x40efae 4255662 (2: nrow: 189 rrow: 189)
leaf: 0x40efaf 4255663 (3: nrow: 186 rrow: 186)
leaf: 0x40efb0 4255664 (4: nrow: 190 rrow: 190)
leaf: 0x40efb1 4255665 (5: nrow: 185 rrow: 185)
leaf: 0x40efb2 4255666 (6: nrow: 179 rrow: 179)
leaf: 0x40efb3 4255667 (7: nrow: 187 rrow: 187)
leaf: 0x40efb4 4255668 (8: nrow: 181 rrow: 181)
............................................
............................................
branch: 0x40f6fb 4257531 (0: nrow: 248, level: 1)
leaf: 0x40f602 4257282 (-1: nrow: 228 rrow: 228)
leaf: 0x40f604 4257284 (0: nrow: 226 rrow: 226)
leaf: 0x40f605 4257285 (1: nrow: 224 rrow: 224)
leaf: 0x40f606 4257286 (2: nrow: 223 rrow: 223)
leaf: 0x40f607 4257287 (3: nrow: 217 rrow: 217)
leaf: 0x40f608 4257288 (4: nrow: 253 rrow: 253)
leaf: 0x40f609 4257289 (5: nrow: 232 rrow: 232)
............................................
............................................
leaf: 0x40f6f8 4257528 (244: nrow: 191 rrow: 191)
leaf: 0x40f6f9 4257529 (245: nrow: 181 rrow: 181)
leaf: 0x40f6fa 4257530 (246: nrow: 99 rrow: 99)
----- end tree dump
解釋trc文件
每一行第一列表示:節點類型,branch是分支節點(包括了根節點),而leaf則是葉子節點
第二列表示:節點地址,16進制
第三列表示:節點地址,10進制
第四列表示:相對于前一個節點的位置:根節點從0算起,其他分支節點和葉子節點從1開始算
第五列表示:(nrow)當前節點所含索引條目的數量(包括delete的條目)
第六列表示:(level)分支節點的層級,在oracle的索引中,層級號是倒過來的,也就是說假設某個索引有N層,則根節點的層級號為N,而根節點下一層的分支節點的層級號為N-1
第七列表示:(rrow)有效的索引條目的數量,因為索引條目如果被刪除,不會立即被清除出索引塊中。所以nrow減rrow的數量就表示已經被刪除的索引條目數量
上面這種方式以樹狀形式轉儲整個索引。同時,我們可以轉儲一個索引節點來看看其中存放了些什么。
下面轉儲根節點的索引塊內容。
從trc文件可知:根節點branch: 0x40efaa 4255658 (0: nrow: 2, level: 2)
[sql]
sys@ORCL> select dbms_utility.data_block_address_file(4255658 ) fno,
2 dbms_utility.data_block_address_block(4255658 ) bno
3 from dual;
FNO BNO
---------- ----------
1 61354
sys@ORCL> alter system dump datafile 1 block 61354;
System altered.
sys@ORCL> oradebug tracefile_name
/u01/app/oracle/admin/orcl/udump/orcl_ora_5234.trc
查看root節點的trc內容
[sql]
header address 230057028=0xdb66444
kdxcolev 2
KDXCOLEV Flags = - - -
kdxcolok 0
kdxcoopc 0x80: opcode=0: iot flags=--- is converted=Y
kdxconco 2
kdxcosdc 0
kdxconro 1
kdxcofbo 30=0x1e
kdxcofeo 8026=0x1f5a
kdxcoavs 7996
kdxbrlmc 4257283=0x40f603
kdxbrsno 0
kdxbrbksz 8056
kdxbr2urrc 0
row#0[8026] dba: 4257531=0x40f6fb
col 0; len 18; (18): 41 4c 4c 5f 43 4f 4c 5f 50 52 49 56 53 5f 4d 41 44 45
col 1; len 6; (6): 00 40 ee c5 00 2c
----- end of branch block dump -----
kdxcolev 表示:索引層級號,我們這個例子中,根節點的level是2,葉子該是0
kdxcolok 表示:該索引上是否有DML活動事務
kdxconco 表示:索引條目中列的數量
kdxcosdc 表示:索引結構發生變化的數量,當你修改某個索引鍵值時,該值加1
kdxconro 表示:當前索引節點中索引條目的數量
kdxcofbo 表示:當前索引節點從第幾個字節開始記錄
kdxcofeo 表示:當前索引節點可用空間的最尾端在哪個字節
kdxcoavs 表示:當前索引節點可用空間總量。也就是kdxcofeo - kdxcofbo 的值
kdxbrlmc 表示:分支節點的位置
kdxbrsno 表示:最后一個被修改的索引條目號,這里為0,表明是新建索引
kdxbrbksz 表示:可用數據塊大小,從這里我們可以知道,即便pctfree為0,對于8k數據塊,我們也不能完全用完
[sql]
row#0[8026] dba: 4257531=0x40f6fb
col 0; len 18; (18): 41 4c 4c 5f 43 4f 4c 5f 50 52 49 56 53 5f 4d 41 44 45
col 1; len 6; (6): 00 40 ee c5 00 2c
這部分內容就是根節點里面記錄的索引條目,總共1行(在B+樹的定義里,如果按最小關鍵碼復寫原則,則樹中每個非葉子節點中有m棵子樹必有m-1個關鍵碼)。每個索引條目都指向一個分支節點,其中,col 1表示所鏈接的分支節點的地址,如果根節點下沒有其他的分支節點,則col 1為TERM;col 0表示該分支節點所鏈接的最小鍵值。注意一點,這里的col 0; len 18; (18):--列的行號,從0開始,緊接著的就是列的長度以及列的值,那么這個值稱之為separator key,這個separator key 可以區分真實的索引值,所以從這里我們也知道 branch block不會存儲完整的索引值,只要能區分就行。也就是說,Oracle在 Branch block中只記錄 索引鍵值的前綴,而不是所有值,是因為這樣可以節約空間,從而能夠存儲更多的索引條目。同時,我們也能理解了為什么 查詢使用 like '%xxx' 這種方法不會走Btree 索引,因為Branch block 存儲的是前綴。
下面轉儲葉子節點塊的內容
隨便選一葉:leaf: 0x40f6fa 4257530 (246: nrow: 99 rrow: 99)
[sql]
sys@ORCL> select dbms_utility.data_block_address_file(4257530) fno,
2 dbms_utility.data_block_address_block(4257530) bno
3 from dual;
FNO BNO
---------- ----------
1 63226
sys@ORCL> oradebug setmypid
Statement processed.
sys@ORCL> alter system dump datafile 1 block 63226;
sys@ORCL> oradebug tracefile_name
/u01/app/oracle/admin/orcl/udump/orcl_ora_6177.trc
葉子節點的部分內容摘入如下:
[sql]
Block header dump: 0x0040f6fa
Object id on Block? Y
seg/obj: 0xcd86 csc: 0x00.a3506 itc: 2 flg: - typ: 2 - INDEX
fsl: 0 fnx: 0x0 ver: 0x01
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x0000.000.00000000 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000
0x02 0xffff.000.00000000 0x00000000.0000.00 C--- 0 scn 0x0000.000a3506
Leaf block dump
===============
header address 221234268=0xd2fc45c
kdxcolev 0
KDXCOLEV Flags = - - -
kdxcolok 0
kdxcoopc 0x80: opcode=0: iot flags=--- is converted=Y
kdxconco 2
kdxcosdc 0
kdxconro 99
kdxcofbo 234=0xea
kdxcofeo 4692=0x1254
kdxcoavs 4458
kdxlespl 0
kdxlende 0
kdxlenxt 0=0x0
kdxleprv 4257529=0x40f6f9
kdxledsz 0
kdxlebksz 8032
row#0[7992] flag: ------, lock: 0, len=40
col 0; len 30; (30):
73 75 6e 2f 74 6f 6f 6c 73 2f 74 72 65 65 2f 53 77 69 74 63 68 53 74 61 74
65 6d 65 6e 74
col 1; len 6; (6): 00 40 f3 25 00 0a
row#1[7953] flag: ------, lock: 0, len=39
col 0; len 29; (29):
73 75 6e 2f 74 6f 6f 6c 73 2f 74 72 65 65 2f 54 68 69 73 45 78 70 72 65 73
73 69 6f 6e
col 1; len 6; (6): 00 40 f0 74 00 31
row#2[7914] flag: ------, lock: 0, len=39
col 0; len 29; (29):
73 75 6e 2f 74 6f 6f 6c 73 2f 74 72 65 65 2f 54 68 69 73 45 78 70 72 65 73
73 69 6f 6e
col 1; len 6; (6): 00 40 f0 74 00 32
............................
............................
row#97[4727] flag: ------, lock: 0, len=35
col 0; len 25; (25):
79 43 62 43 72 53 75 62 53 61 6d 70 6c 69 6e 67 54 79 70 65 31 37 30 5f 54
col 1; len 6; (6): 00 40 f1 f1 00 0c
row#98[4692] flag: ------, lock: 0, len=35
col 0; len 25; (25):
79 43 62 43 72 53 75 62 53 61 6d 70 6c 69 6e 67 54 79 70 65 31 37 30 5f 54
col 1; len 6; (6): 00 40 f4 a2 00 10
----- end of leaf block dump -----
和分支節點不同的值解析如下:
kdxlespl 表示:當葉子節點被拆分時,未提交的事務數量
kdxlende 表示:被刪除的索引條目數量
kdxlenxt 表示:當前葉子節點的下一個葉子節點的地址
kdxlprv 表示:當前葉子節點的上一個葉子節點的地址
kdxledsz 表示:被刪除的空間
轉儲文件中接下來的部分就是索引條目部分。lock: 0 表示ITL中的鎖信息 0表示沒有被鎖 ;len :表示索引值長度 ;flag 表示 標記,如刪除標記等。col 表示列號,從0開始 那么接下來就是索引的鍵值 以及 rowid中后三部分(相對文件號、塊號、行號)即:col 0 是鍵值, col 1 是rowid。
也就是說,Leaf節點主要存儲了完整的索引鍵值,以及相關索引鍵值的部分rowid(這個rowid去掉了data object number部分),同時leaf 節點還存儲了2個指針(DBA),他們分別指向上一個leaf節點以及下一個leaf節點.這樣葉子節點便是雙向鏈表的結構。我們看到前面對B樹索引的體系結構的描述,可以知道其為一個樹狀的立體結構。但對應到數據文件里的排列當然還是一個平面的形式,也就是像下面這樣。因此,當oracle需要訪問某個索引塊的時候,勢必會在這個結構上跳躍的移動。
/根/分支/分支/葉子/…/葉子/分支/葉子/葉子/…/葉子/分支/葉子/葉子/…/葉子/分支/.....
當oracle需要獲得一個索引塊時,首先從根節點開始,根據所要查找的鍵值,從而知道其所在的下一層的分支節點,然后訪問下一層的分支節點,再次同樣根據鍵值訪問再下一層的分支節點,如此這般,最終訪問到最底層的葉子節點。可以看出,其獲得物理I/O塊時,是一個接著一個,按照順序,串行進行的。在獲得最終物理塊的過程中,我們不能同時讀取多個塊,因為我們在沒有獲得當前塊的時候是不知道接下來應該訪問哪個塊的。因此,在索引上訪問數據塊時,會對應到db file sequential read等待事件,其根源在于我們是按照順序從一個索引塊跳到另一個索引塊,從而找到最終的索引塊的。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。