由淺至深講解Oracle數據庫 B-tree索引
B-tree索引:
·索引會隨著時間的增加而變的不平衡;
·刪除的索引空間不會被重用;
·隨著索引層數的增加,索引將會變得無效并需要重建;
·聚簇因子差,索引需要重建;
·為了提高性能,索引需要經常重建;
索引基礎
·一個更新由一個刪除和一個插入組成;
·頁塊由索引條目(row header(2/3B)|length(1B)|indexed data value(nB)|length(1B)|RowID(6B))和相應的rowid組成;
·每個頁塊包含兩個指針分別前面的頁塊和后面頁塊;
Treedump
alter session set events ‘immediate trace name treedump level index_object_id’;
----- begin tree dump
branch: 0x424362 4342626 (0: nrow: 2, level: 1)
leaf: 0x424363 4342627 (-1: nrow: 540 rrow: 540)
leaf: 0x424364 4342628 (0: nrow: 461 rrow: 461)
----- end tree dump
以上dump包含的信息如下:
塊類型:branch(分支塊);leaf(頁塊);
塊地址:0x424362 4342626;
nrow:索引條目的數量;
rrow:當前塊中的索引條目數量;
level:分支塊等級(頁塊隱示為0);
Block Dump
alter system dump datafile X block X;
alter system dump datafile X block min X1 block max X2
Start dump data blocks tsn: 0 file#: 1 minblk 148538 maxblk 148538
buffer tsn: 0 rdba: 0x0042443a (1/148538)
scn: 0x0000.00162a95 seq: 0x01 flg: 0x04 tail: 0x2a950601
frmt: 0x02 chkval: 0x8b5c type: 0x06=trans data
Block header dump: 0x0042443a
Object id on Block? Y
seg/obj: 0xd1fe csc: 0x00.162a95 itc: 2 flg: O typ: 2 - INDEX
fsl: 0 fnx: 0x42443b ver: 0x01
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x0005.02a.00000332 0x008005cb.020e.01 CB-- 0 scn 0x0000.00162a92
0x02 0x0008.011.00000346 0x008002e6.0163.03 C--- 0 scn 0x0000.00162a93
該dump包含的信息如下:
rdba:分支塊的相對數據庫塊地址(文件號/塊號);
scn:塊最后改變的SCN號;
type:塊類型;
seq:塊改變的數量;
seg/obj: 16進制對象ID;
typ:段類型;
Itl:相關的事務槽(頁塊默認為2),包括槽ID,事務ID,撤銷塊地址,標記,鎖信息,和事務SCN;
通過rba確定數據文件號和塊號:
select DBMS_UTILITY.DATA_BLOCK_ADDRESS_FILE(rba),
DBMS_UTILITY.DATA_BLOCK_ADDRESS_BLOCK(rba)
from dual;
通用的索引塊頭
header address 153168988=0x9212c5c
kdxcolev 0
KDXCOLEV Flags = - - -
kdxcolok 1
kdxcoopc 0x89: opcode=9: iot flags=--- is converted=Y
kdxconco 2
kdxcosdc 2
kdxconro 254
kdxcofbo 544=0x220
kdxcofeo 4482=0x1182
kdxcoavs 3938
kdxcolev:索引級別(0代表頁塊);
kdxcolok:標示結構塊事塊是否發生;
kdxcoopc:內部操作碼;
kdxconco:索引列數量,包括ROWID;
kdxcosdc:塊中索引結構改變的數量;
kdxconro:索引條目的數量,不包括kdxbrlmc指針;
kdxcofbo:塊中空閑空間的開始位置;
kdxcofeo:塊中空閑空間的結束位置;
kdxcoavs:塊中的可用空間數量(kdxcofbo-kdxcofeo);
分支頭區域
kdxbrlmc 8388627=0x800013
kdxbrsno 92
kdxbrbksz 8060
kdxbrlmc:如果索引值小于第一個值(row#0),則為該索引值所在的塊地址;
kdxbrsno:最后更改的索引條目;
kdxbrbksz:可使用的塊空間;
葉塊頭區域
kdxlespl 0
kdxlende 127
kdxlenxt 4342843=0x42443b
kdxleprv 4342845=0x42443d
kdxledsz 0
kdxlebksz 8036
kdxlespl:塊拆分時被清除的未提交數據的字節數;
kdxlende:被刪除的條目數;
kdxlenxt:下一個頁塊的RBA;
kdxleprv:上一個頁塊的RBA;
kdxlebksz:可使用的塊空間(默認小于分支的可用空間);
分支條目
row#0[7898] dba: 4342821=0x424425
col 0; len 3; (3): c2 61 03
col 1; TERM
row#1[7214] dba: 4342873=0x424459
col 0; len 4; (4): c3 04 02 17
col 1; TERM
行號,[塊中的起始位置] dba;
列號,列長度,列值;
brach中的每個entry有2個columns:
一個是child blocks中的最大值,另一個是指向的下一層block的address'
但是某些時候可能會有一些比較奇怪的結果:
row#0[7025] dba: 4342908=0x42447c
col 0; len 1024; (1024):
41 20 20 20 …20
col 1; len 4; (4): 00 42 44 73
----- end of branch block dump -----
葉條目
row#38[5014] flag: ----S-, lock: 2, len=14
col 0; len 4; (4): c3 04 61 55
col 1; len 6; (6): 00 42 43 db 00 a1
row#39[5028] flag: ---DS-, lock: 2, len=14
行號[在塊中的開始位置] 各種標記(鎖信息,刪除信息);
索引列號,長度,值。其中6個字節的為ROWID號,將其轉換為二進制,算法結果為:
前10 bit代表了file_id
中22 bit代表了block_id
后16 bit代表了row_id;
通過文件號和塊號算出的結果為創建該索引的表的塊。
奇怪的是,為什么索引中的rowid不能直接找到obj_id?
因為索引段對應的數據段在 一開始就知道,因為是先知道數據段才找到索引段,然后
根據索引段內容去搜索數據段內容,所以索引段中 rowid 不必包含 data_object_id 信息。
如果索引是建立在非分區表上,或者是分區表上的 LOCAL 索引,使用的是6 bytes的 Restricted ROWID。如果索引是建立在分區表上的 GLOBAL index,則使用 10bytes 的 Extended ROWID,這樣可以區分索引指向哪個分區表。
更新/重用索引條目
當更新了索引條目后,DUMP如下:
kdxconco 2
kdxcosdc 0
kdxconro 2
kdxcofbo 40=0x28
kdxcofeo 8006=0x1f46
kdxcoavs 7966
kdxlespl 0
kdxlende 1
kdxlenxt 0=0x0
kdxleprv 0=0x0
kdxledsz 0
kdxlebksz 8036
row#0[8021] flag: ---D-, lock: 2 => deleted index entry
col 0; len 5; (5): 42 4f 57 49 45
col 1; len 6; (6): 00 80 05 0a 00 00
row#1[8006] flag: -----, lock: 2
col 0; len 5; (5): 5a 49 47 47 59 => new index entry
col 1; len 6; (6): 00 80 05 0a 00 00
更新后,將包含一個刪除的條目,一個新的條目。在隨后的插入中,如果新插入的索引條目能夠放到被刪除的索引條目的位置上,就會直接重用這個條目。根據索引值來決定。
所謂重用,是對row 的重用,而不是對row所在物理存儲(或說物理位置)的重用。索引是按照indexed value對row進行排序的。有新的row被插入,首先按照value排序,將他放在合適的row list中,如果他的位置正好原來有個row被刪掉了,則重用這個row在row list中的位置。至于物理存儲上,則可能根據版本不同會有不同。在10.2中,我做的測試并沒有向下開辟空間。
結論:
·到葉塊中的任何插入都將移除所有被刪除的條目;
·刪除的空間在隨后的寫中被清除;
·刪除的空間在延遲塊清除中被清除;
·全空塊被放在空閑列表,可以重用;
索引統計
·dba_indexes
·dbms_stats
·index_stats
-- analyze index index_name validate structure;
--分析資源,鎖;
·v$segment_statistics
statistics_level = typical (or all)
注意事項:
blevel (dba_indexes) vs. height (index_stats)
blocks allocated,但未必使用;
lf_rows_len包含行負載(單列索引12個字節)
pct_used索引結構中當前使用的空間:(used_space/btree_space)*100
絕大多數索引統計包含刪除的條目:
non-deleted rows = lf_rows – del_lf_rows
pct_used by non-deleted rows = ((used_space – del_lf_rows_len) / btree_space) * 100