Oracle 的 data block研讀(二)

xz43發表於2010-12-27
    上次,dump了DA他的block結構資訊出來,簡單看了一下,今天,把INDEX的block也dump出來,看看裡面的內容。
    B樹索引是一個典型的樹結構,其包含的元件主要是:

<!--[if !supportLists]--&gt1)     <!--[endif]--&gt葉子節點(Leaf node):包含條目直接指向表裡的資料行。

<!--[if !supportLists]--&gt2)     <!--[endif]--&gt分支節點(Branch node):包含的條目指向索引裡其他的分支節點或者是葉子節點。

<!--[if !supportLists]--&gt3)     <!--[endif]--&gt根節點(Root node):一個B樹索引只有一個根節點,它實際就是位於樹的最頂端的分支節點。

    首先,確定要dump的索引名稱,在已有系統中,找到一個平時用的比較多的表,看看上面的索引INDEX_ORAGNISE。
SQL> select data_object_id,object_id from dba_objects where object_name='INDEX_ORAGNISE';
DATA_OBJECT_ID  OBJECT_ID
-------------- ----------
         88217      88217

找到索引對應的OBJECT_ID,我們再用treedump來dump對應的OBJECT。
SQL> alter session set events 'immediate trace name treedump level 88217';
Session altered
SQL>
 
dump完成,我們再定位到user_dump_dest定義的目錄下,找得剛才dump出來的trace檔案。
SQL> show parameter user_dump_dest
NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
user_dump_dest                       string      /oracle/admin/orcl/udump
 
開啟trace檔案,裡面內容如下:
*** SESSION ID:(125.177) 2010-12-27 11:18:57.516
----- begin tree dump
branch: 0x140110c 20975884 (0: nrow: 51, level: 1)
   leaf: 0x140110d 20975885 (-1: nrow: 372 rrow: 372)
   leaf: 0x140110e 20975886 (0: nrow: 336 rrow: 336)
   leaf: 0x140110f 20975887 (1: nrow: 340 rrow: 340)
   leaf: 0x1401110 20975888 (2: nrow: 336 rrow: 336)
   leaf: 0x1401111 20975889 (3: nrow: 338 rrow: 338)
   leaf: 0x1401112 20975890 (4: nrow: 338 rrow: 338)
    。
    。
    。
   leaf: 0x1401141 20975937 (48: nrow: 438 rrow: 438)
   leaf: 0x1401142 20975938 (49: nrow: 29 rrow: 29)
----- end tree dump
 
表示共有52個索引塊,其中 branch(表示為根節點)1個,leaf(表示為葉子節點)51個。
####################################################################
結構說明:

leaf:表示該資料塊是leaf block
0x140110d:對應索引資料塊的十六進位地址
20975885:對應索引資料塊的十進位地址
-1: 表示索引資料塊的編號,編號起始是-1
nrow: 372 :表示該索引資料塊中總的行數,包含被刪除的行
rrow: 372:表示該索引資料塊中實際存在有效行數
####################################################################

通過檢視查詢索引的詳細資訊如下:
 
SQL> analyze index INDEX_ORAGNISE validate structure;
Index analyzed
SQL> SELECT NAME, BLOCKS, HEIGHT,LF_ROWS, LF_BLKS, BR_ROWS, BR_BLKS, BTREE_SPACE, USED_SPACE FROM INDEX_STATS WHERE NAME='INDEX_ORAGNISE';
NAME                               BLOCKS     HEIGHT    LF_ROWS    LF_BLKS    BR_ROWS    BR_BLKS BTREE_SPACE USED_SPACE
------------------------------ ---------- ---------- ---------- ---------- ---------- ---------- ----------- ----------
INDEX_ORAGNISE                     72          2      17290         51         50          1      415824     360985
SQL>
 
根據上面的trace資訊,得到檔案號和塊號。如果索引很小,那麼就可能沒有branch,直接從leaf取。如果僅僅看某個leaf的內容,也可以直接從leaf的dba得到檔案號和塊號。
 
根結點檔案號:
SQL> select dbms_utility.data_block_address_file(20975884) "file",
  2         dbms_utility.data_block_address_block(20975884) "block"
  3    from dual;
      file      block
---------- ----------
         5       4364
 
葉節點檔案號:
SQL> select dbms_utility.data_block_address_file(20975886) "file",
  2         dbms_utility.data_block_address_block(20975886) "block"
  3    from dual;
      file      block
---------- ----------
         5       4366
 
再dump該block得到實際INDEX block的內容。
SQL> alter system dump datafile 5 block 4364;
System altered
SQL>
 
重新開啟剛才的trace檔案,可以看到如下內容:
Branch block dump  (開始根節點塊資訊)
=================
header address 381738060=0x16c0dc4c
kdxcolev 1
KDXCOLEV Flags = - - -
kdxcolok 0
kdxcoopc 0x80: pcode=0: iot flags=--- is converted=Y
kdxconco 2
kdxcosdc 0
kdxconro 50
kdxcofbo 128=0x80
kdxcofeo 7364=0x1cc4
kdxcoavs 7236
kdxbrlmc 20975885=0x140110d
kdxbrsno 0
kdxbrbksz 8056
kdxbr2urrc 3
row#0[8040] dba: 20975886=0x140110e
col 0; len 10; (10):  31 33 30 31 30 34 30 30 31 34
col 1; TERM
row#1[8027] dba: 20975887=0x140110f
col 0; len 7; (7):  31 33 30 34 33 32 30
col 1; TERM
row#2[8014] dba: 20975888=0x1401110
col 0; len 7; (7):  31 33 30 39 30 30 31
col 1; TERM
row#3[8004] dba: 20975889=0x1401111
col 0; len 4; (4):  31 34 30 33
col 1; TERM
row#4[7988] dba: 20975890=0x1401112
col 0; len 10; (10):  31 34 32 34 30 30 30 32 30 32
col 1; TERM

     對於分支節點塊(包括根節點塊)來說,其所包含的索引條目都是按照順序排列的(預設是升序排列,也可以在建立索引時指定為降序排列)。每個索引條目(也可以叫做每條記錄)都具有兩個欄位。第一個欄位表示當前該分支節點塊下面所連結的索引塊中所包含的最小鍵值;第二個欄位為四個位元組,表示所連結的索引塊的地址,該地址指向下面一個索引塊。在一個分支節點塊中所能容納的記錄行數由資料塊大小以及索引鍵值的長度決定。比如從上可以看到,對於根節點塊來說,包含五條記錄,它們指向五個分支節點塊。其中col 0對應的數值分別表示分支節點塊所連結的鍵值的最小值。而如dba:dba: 20975887=0x140110f則表示所指向的五個分支節點塊的地址,包括十進位制與十六進位制地址。而col 1沒看出有什麼用,還希望有高手指點。

根據以上內容中,branch 20975886 得到更詳細內容

SQL> select dbms_utility.data_block_address_file(20975886) "file",
  2         dbms_utility.data_block_address_block(20975886) "block"
  3    from dual;
      file      block
---------- ----------
         5       4366
 
Leaf block dump
===============
header address 381738084=0x16c0dc64
kdxcolev 0
KDXCOLEV Flags = - - -
kdxcolok 0
kdxcoopc 0x80: pcode=0: iot flags=--- is converted=Y
kdxconco 2
kdxcosdc 0
kdxconro 336
kdxcofbo 708=0x2c4
kdxcofeo 1530=0x5fa
kdxcoavs 822
kdxlespl 0
kdxlende 0
kdxlenxt 20975887=0x140110f -- 下個節點RBA地址
kdxleprv 20975885=0x140110d -- 上一個節點RBA地址
kdxledsz 0
kdxlebksz 8032
row#0[8012] flag: ------, lock: 0, len=20
col 0; len 10; (10):  31 33 30 31 30 34 30 30 31 34
col 1; len 6; (6):  01 40 10 70 00 48
row#1[7992] flag: ------, lock: 0, len=20
col 0; len 10; (10):  31 33 30 31 30 34 30 30 31 35
col 1; len 6; (6):  01 40 10 4d 00 19
row#2[7972] flag: ------, lock: 0, len=20
col 0; len 10; (10):  31 33 30 31 30 34 30 30 31 36
col 1; len 6; (6):  01 40 11 08 00 0f
row#3[7952] flag: ------, lock: 0, len=20
col 0; len 10; (10):  31 33 30 31 30 34 30 30 31 37
col 1; len 6; (6):  01 40 10 4d 00 26
row#4[7932] flag: ------, lock: 0, len=20
col 0; len 10; (10):  31 33 30 31 30 34 30 30 31 38
col 1; len 6; (6):  01 40 10 6c 00 39
row#5[7912] flag: ------, lock: 0, len=20
col 0; len 10; (10):  31 33 30 31 30 34 30 30 31 39
col 1; len 6; (6):  01 40 10 4d 00 27
row#6[7892] flag: ------, lock: 0, len=20
col 0; len 10; (10):  31 33 30 31 30 34 30 30 32 30
col 1; len 6; (6):  01 40 10 80 00 16
###########################################################################
結構說明:

header address 381738084=0x16c0dc64
kdxcolev 0          (index level, 0表示是leaf block)
KDXCOLEV Flags = - - -
kdxcolok 0
kdxcoopc 0x80: pcode=0: iot flags=--- is converted=Y 內部操作程式碼
kdxconco 2 索引列數量
kdxcosdc 0 索引結構改變次數
kdxconro 336 索引記錄數量BR_ROWS
kdxcofbo 708=0x2c4 空閒空間開始偏移量
kdxcofeo 1530=0x5fa  空閒空間結束偏移量
kdxcoavs 822  所提供的空閒空間
kdxlespl 0   在資料塊被cleaned out時,還未進行commit的數量
kdxlende 0 被刪除索引記錄的數量
kdxlenxt 20975887=0x140110f 下一個連結資料塊的地址
kdxleprv 20975885=0x140110d 上一個連結資料塊地址
kdxledsz 0  被刪除的空間大小
kdxlebksz 8032 使用的資料塊空間

kdxcolev: index level (0 represents leaf blocks)
kdxcolok: denotes whether structural block transaction is occurring
kdxcoopc: internal operation code
kdxconco: index column count
kdxcosdc: count of index structural changes involving block
kdxconro: number of index entries (does not include kdxbrlmc pointer)
kdxcofbo: offset to beginning of free space within block
kdxcofeo: offset to the end of free space (ie. first portion of block containing index data)
kdxcoavs: available space in block (effectively area between the two fields above)
 
 
結構說明:
row#0[8012] flag: ------, lock: 0, len=20
col 0; len 10; (10):  31 33 30 31 30 34 30 30 31 34
col 1; len 6; (6):  01 40 10 70 00 48
col 0: 為keyvalue.
col 1: 為Rowid

關於rowid的換算:
索引中的rowid由48個bit構成,前10個bit構成檔案號,中間22個bit構成塊號,最後16個bit 構成塊中的行號。
(關於rowid解讀,參考:我的ROWID解讀)

關於整數key值的換算:
先去掉c2,因為c2只表示最高位的位置,後面才是真正資料,06轉換為十進位制為6,減去1為5,3b轉換為十進位制為48+11=59,

減去1為 58,兩個結果合併到一起,為258,這就是其儲存的真正資料,簡單表示一下:

   1. c2 03 3b -> 03 3b
   2. 03 -> 0*16+3=3->3-1=2
   3. 3b -> 3*16+14=59 -> 59-1=58
   4. 結果合併得到258

關於字串的換算:直接由十六進位制得到各個ascii碼,然後對應到相應字元。

這個換算沒有得到驗證???????
這種dump資訊要參考DSI

注意:上面紅色的字型標識該索引塊的鍵值中最小的鍵值,也就是下面第一個索引葉節點塊的第一個索引項對應的數值。
###########################################################################
 
     對於葉子節點塊來說,其所包含的索引條目與分支節點一樣,都是按照順序排列的(預設是升序排列,也可以在建立索引時指定為降序排列)。每個索引條目(也可以叫做每條記錄)也具有兩個欄位。第一個欄位表示索引的鍵值,對於單列索引來說是一個值;而對於多列索引來說則是多個值組合在一起的。第二個欄位表示鍵值所對應的記錄行的ROWID,該ROWID是記錄行在表裡的實體地址。如果索引是建立在非分割槽表上或者索引是分割槽表上的本地索引的話,則該ROWID佔用6個位元組;如果索引是建立在分割槽表上的全域性索引的話,則該ROWID佔用10個位元組。
 
     知道這些資訊以後,我們可以舉個例子來說明如何估算每個索引能夠包含多少條目,以及對於表來說,所產生的索引大約多大。對於每個索引塊來說,預設的PCTFREE10%,也就是說最多隻能使用其中的90%。同時9i以後,這90%中也不可能用盡,只能使用其中的87%左右。也就是說,8KB的資料塊中能夠實際用來存放索引資料的空間大約為64888192×90%×88%)個位元組。

     假設我們有一個非分割槽表,表名為warecountd,其資料行數為130萬行。該表中有一個列,列名為goodid,其型別為char8),那麼也就是說該goodid的長度為固定值:8。同時在該列上建立了一個B樹索引。

在葉子節點中,每個索引條目都會在資料塊中佔一行空間。每一行用23個位元組作為行頭,行頭用來存放標記以及鎖定型別等資訊。同時,在第一個表示索引的鍵值的欄位中,每一個索引列都有1個位元組表示資料長度,後面則是該列具體的值。那麼對於本例來說,在葉子節點中的一行所包含的資料大致如下圖二所示:

Oracle 的 data block研讀(二)

從上圖可以看到,在本例的葉子節點中,一個索引條目佔18個位元組。同時我們知道8KB的資料塊中真正可以用來存放索引條目的空間為6488位元組,那麼在本例中,一個資料塊中大約可以放3606488/18)個索引條目。而對於我們表中的130萬條記錄來說,則需要大約36111300000/360)個葉子節點塊。

      而對於分支節點裡的一個條目(一行)來說,由於它只需儲存所連結的其他索引塊的地址即可,而不需要儲存具體的資料行在哪裡,因此它所佔用的空間要比葉子節點要少。分支節點的一行中所存放的所連結的最小鍵值所需空間與上面所描述的葉子節點相同;而存放的索引塊的地址只需要4個位元組,比葉子節點中所存放的ROWID少了2個位元組,少的這2個位元組也就是ROWID中用來描述在資料塊中的行號所需的空間。因此,本例中在分支節點中的一行所包含的資料大致如下圖三所示:

Oracle 的 data block研讀(二)

從上圖可以看到,在本例的分支節點中,一個索引條目佔16個位元組。根據上面葉子節點相同的方式,我們可以知道一個分支索引塊可以存放大約4056488/16)個索引條目。而對於我們所需要的3611個葉子節點來說,則總共需要大約9個分支索引塊。

      這樣,我們就知道了我們的這個索引有2層,第一層為1個根節點,第二層為9個分支節點,而葉子節點數為3611個,所指向的表的行數為1300000行。但是要注意,在oracle的索引中,層級號是倒過來的,也就是說假設某個索引有N層,則根節點的層級號為N,而根節點下一層的分支節點的層級號為N-1,依此類推。對本例來說,9個分支節點所在的層級號為1,而根節點所在的層級號為2


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/9399028/viewspace-682646/,如需轉載,請註明出處,否則將追究法律責任。

相關文章