oracle資料庫特種恢復技術(二)—塊內篇

jinqibingl發表於2012-10-04

oracle資料庫特種恢復技術(二)—塊內篇  

oracle資料庫特種恢復技術(二)—塊內篇
作者:謝浩
Oracle資料庫特種恢復技術—原理篇連結:http://www.itpub.net/thread-1507760-1-1.html
oracle資料庫特種恢復技術—塊內篇連結:http://www.itpub.net/thread-1507766-1-1.html
oracle資料庫特種恢復技術—轉換篇連結:http://www.itpub.net/thread-1507774-1-1.html


繼續上一次的內容。
明確了大體思路後,需要進入資料檔案內部解析出資料行。結合使用bbed、dump、ue進行觀察。分析oracle塊內物理結構時可採用的方法是:首先在dump檔案中找到所關心的變數,然後在dd輸出中對照查詢該變數的值並記錄偏移量,最後在bbed輸出中檢視該偏移量對應的資料結構進行驗證。Oracle資料庫中的V$TYPE_SIZE檢視記錄了塊內各個變數及結構體的長度和簡單描述,熟悉後可以簡單的透過對變數名縮寫的估計找到對應的dump檔案中的變數。
首先觀察資料庫1號檔案的377資料塊,也就是sys.bootstrap$表的segment head block。
Bbed輸出:

BBED> setblock 377

        BLOCK#          377
BBED> map
File: /home/oracle/vie.pdf (1)

Block: 377                                  Dba:0x00400179

------------------------------------------------------------
Unlimited Data Segment Header
struct kcbh, 20 bytes                      a0      
struct ktech, 72 bytes                     a20      
struct ktemh, 16 bytes                     a92      
struct ktetb[1], 8 bytes                   a108     
struct ktshc, 8 bytes                      a4148   
struct ktsfs_seg[1], 20 bytes              a4156   
struct ktsfs_txn[16], 320 bytes            a4176   
ub4 tailchk                                a8188   
dump輸出:

*** 2011-10-1122:29:03.808

*** SERVICENAME:(SYS$USERS) 2011-10-11 22:29:03.797

*** SESSIONID:(124.22380) 2011-10-11 22:29:03.797

Start dump datablocks tsn: 0 file#: 1 minblk 377 maxblk 377

buffer tsn: 0rdba: 0x00400179 (1/377)

scn:0x0000.0000014a seq: 0x01 flg: 0x04 tail: 0x014a1001

frmt: 0x02chkval: 0xe733 type: 0x10=DATA SEGMENT HEADER - UNLIMITED

Hex dump ofblock: st=0, typ_found=1

Dump of memoryfrom 0x000000000F1DB600 to 0x000000000F1DD600

00F1DB6000000A210 00400179 0000014A 04010000  [....y.a.J......]

…………省略…………..

00F1DD5F000000000 00000000 00000000 014A1001  [....... .....J.]

  Extent Control Header

-----------------------------------------------------------------

  Extent Header:: spare1: 0      spare2: 0      #extents: 1      #blocks: 7     

                  last map  0x00000000 #maps: 0      offset: 4128  

      Highwater::  0x0040017d ext#: 0      blk#: 3      ext size: 7     

  #blocks in seg. hdr's freelists: 1     
  #blocks below: 3     

  mapblk 0x00000000  offset: 0     

                   Unlocked

     Map Header:: next  0x00000000 #extents: 1    obj#: 56     flag: 0x40000000

  Extent Map

-----------------------------------------------------------------

   0x0040017a length: 7     

  
  nfl = 1, nfb = 1 typ = 1 nxf = 0 ccnt = 3
  SEG LST:: flg: USED   lhd: 0x0040017c ltl: 0x0040017c

End dump datablocks tsn: 0 file#: 1 minblk 377 maxblk 377


dd輸出:

00000000h: 10A2 00 00 79 01 40 00 4A 01 00 00 00 00 01 04 ; .?.y.a.J.. ....

00000010h: 33E7 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; 3?.. ..... ....

00000020h: 0000 00 00 01 00 00 00 07 00 00 00 20 10 00 00 ; .. ..... ... ...

00000030h: 0000 00 00 03 00 00 00 07 00 00 00 7D 01 40 00 ; .. ..... ...}.a.

00000040h: 0000 00 00 00 00 00 00 01 00 00 00 03 00 00 00 ; .. ..... .......

00000050h: 0000 00 00 00 00 00 00 00 00 00 00 01 00 00 00 ; .. ..... .......

00000060h: 0000 00 00 38 00 00 00 00 00 00 40 7A 01 40 00 ; ....8.. ...az.a.

00000070h: 0700 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; .. ..... .......

00000080h: 0000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; .. ..... .......

00000090h: 0000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; .. ..... .......

000000a0h: 0000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; .. ..... .......

…………………省略……………………..

377塊的block type為0x10,表示該塊是Data segment header block (unlimited extents, no freelist groups),就是mssm下的segment header,而assm的segment header的block type為0x24,表示PAGETABLEEXTENT MAP BLOCK(bbed工具目前不支援assm相關的segment header和三級點陣圖塊)。

結合觀察三種工具的輸出。bbed中的“structktetb[1], 8 bytes  a108” 對應的就是oracle dump輸出的extent map,該結構體記錄了segment中的extent的首地址。這裡有一點特別的地方,在進行分析的過程中發現,存放在assm表空間中的segment的header block中的extent map包涵了三級點陣圖的塊,就是說assm管理的下的extent map覆蓋整個segment佔用的塊,而存放在mssm表空間中的segment的header block中的extent map,僅包涵segment中存放資料的塊,沒有包涵段頭塊本身。由於sys.bootstrap$表處於system表空間中,因此採用的是mssm的管理方式。

Assm與mssm下的extent map格式是相同的,每個extent的資訊佔用8位元組儲存,前4位元組記錄該extent中的第一個塊的dba(data block address),後4位元組記錄該extent包涵的塊的個數,使用bbed對其進行觀察:

BBED> pktetb

structktetb[0], 8 bytes                    a108     

   ub4 ktetbdba                             a108     0x0040017a

   ub4 ktetbnbk                             a112      0x00000007
與dump檔案中的extent map吻合。
在mssm管理下的extent map是在塊108偏移量的位置,那麼這個位置是否是固定的,我沒有找到相關權威資料,因此進行如下分析:首先從不同的庫,取不同資料量的表的segment header block,發現偏移量相同,然後用bbed分析ktetb結構體前面的幾個結構體變數:
BBED> map /v
File: /home/oracle/vie.pdf (1)

Block: 377                                  Dba:0x00400179

------------------------------------------------------------
Unlimited Data Segment Header
struct kcbh, 20 bytes                      a0      
    ub1 type_kcbh                           a0      
    ub1 frmt_kcbh                           a1      
    ub1 spare1_kcbh                         a2      
    ub1 spare2_kcbh                         a3      
    ub4 rdba_kcbh                           a4      
    ub4 bas_kcbh                            a8      
    ub2 wrp_kcbh                            a12      
    ub1 seq_kcbh                            a14      
    ub1 flg_kcbh                            a15      
    ub2 chkval_kcbh                         a16      
    ub2 spare3_kcbh                         a18      
struct ktech, 72 bytes                     a20      
    ub4 spare1_ktech                        a20      
    word tsn_ktech                          a24      
    ub4 lastmap_ktech                       a28      
    ub4 mapcount_ktech                      a32      
    ub4 extents_ktech                       a36      
    ub4 blocks_ktech                        a40      
    ub2 mapend_ktech                        a44      
    struct hwmark_ktech, 32 bytes           a48      
    struct locker_ktech, 8 bytes            a80      
    ub4 flag_ktech                          a88      
struct ktemh, 16 bytes                     a92      

    ub4 count_ktemh                         a92     

    ub4 next_ktemh                          a96      
    ub4 obj_ktemh                           a100     
    ub4 flag_ktemh                          a104   
可見前三個結構體都不是陣列型別,所以目前判斷ktetb的偏移量應該是在資料庫版本一定的情況下是固定的存放在塊頭偏移108的位置。
繼續進行觀察。將extent map中的dba“0x0040017a”進行轉換,其指向1號檔案的378號資料塊。按377塊extent map中所記錄,sys.bootstrap$表儲存於1號檔案378號資料塊開始的連續7個資料塊中,且ub4 next_ktemh a96的值為0x00000000,因此所有資料都儲存在這7個資料塊中。
繼續深入觀察378號資料塊就能得到sys.bootstrap$表的真實資料:
bbed輸出:

BBED> setblock 378

        BLOCK#          378
BBED> map
File: /home/oracle/vie.pdf (1)

Block: 378                                  Dba:0x0040017a

------------------------------------------------------------
KTB Data Block (Table/Cluster)
struct kcbh, 20 bytes                      a0      
struct ktbbh, 48 bytes                     a20      
struct kdbh, 14 bytes                      a68      
struct kdbt[1], 4 bytes                    a82      
sb2 kdbr[24]                               a86      
ub1 freespace[1158]                        a134     
ub1 rowdata[6896]                          a1292   
ub4 tailchk                                a8188   

oracledump輸出:

Start dump data blocks tsn: 0 file#: 1minblk 378 maxblk 378

buffer tsn: 0 rdba: 0x0040017a (1/378)

scn: 0x0000.0000015d seq: 0x01 flg:0x06 tail: 0x015d0601

frmt: 0x02 chkval: 0x13d5 type:0x06=trans data

Hex dump of block: st=0, typ_found=1

Dump of memory from 0x000000001E849600to 0x000000001E84B600

01E849600 0000A206 0040017A 0000015D06010000  [....z.a.].. ....]

01E849610 000013D5 00000001 00000038000000C7  [.. .....8.. ....]

01E849620 00000000 00020001 0000000000220000  [.. ..... ,...".],

01E849630 00000002 00400182 000C000200002018  [.. ...a.. ... ..]

……………………………省略部分內容………………………

Block header dump:  0x0040017a

Object id on Block? Y

seg/obj: 0x38 csc: 0x00.c7  itc: 1  flg: - typ: 1 - DATA

    fsl: 0  fnx: 0x0 ver: 0x01

Itl          Xid                  Uba         Flag Lck        Scn/Fsc

0x01  0x0000.022.00000002 0x00400182.0002.0c  --U-   24 fsc 0x0000.0000015d

data_block_dump,data header at0x1e849644

===============

tsiz: 0x1fb8

hsiz: 0x42

pbl: 0x1e849644

bdba: 0x0040017a

    76543210

flag=--------

ntab=1

nrow=24

frre=-1

fsbo=0x42

fseo=0x4c8

avsp=0x486

tosp=0x486

0xe:pti[0]       nrow=24 ffs=0

0x12:pri[0]     offs=0x1fa3

0x14:pri[1]     offs=0x1f1a

0x16:pri[2]     offs=0x1d95

0x18:pri[3]     offs=0x1ccd

0x1a:pri[4]     offs=0x1b4e

0x1c:pri[5]     offs=0x1a7a

0x1e:pri[6]     offs=0x19ad

0x20:pri[7]     offs=0x1749

0x22:pri[8]     offs=0x167b

0x24:pri[9]     offs=0x15b3

0x26:pri[10]   offs=0x14d6

0x28:pri[11]   offs=0x140a

0x2a:pri[12]   offs=0x12ef

0x2c:pri[13]   offs=0x1205

0x2e:pri[14]   offs=0x110e

0x30:pri[15]   offs=0xf38

0x32:pri[16]   offs=0xe68

0x34:pri[17]   offs=0xd91

0x36:pri[18]   offs=0xc79

0x38:pri[19]   offs=0x969

0x3a:pri[20]   offs=0x89c

0x3c:pri[21]   offs=0x65e

0x3e:pri[22]   offs=0x58e

0x40:pri[23]   offs=0x4c8

block_row_dump:

tab 0, row 0, a0x1fa3

tl: 21 fb: --H-FL-- lb: 0x1  cc: 3

col 0: [ 3]  3e 64 66

col 1: [ 3]  3e 64 66

col 2: [ 9]  38 2e 30 2e 30 2e 30 2e30

tab 0, row 1, a0x1f1a

tl: 137 fb: --H-FL-- lb: 0x1  cc: 3

col 0: [ 1]  80

col 1: [ 1]  80

col 2: [129]

43 52 45 41 54 45 20 52 4f 4c 4c 42 41 43 4b20 53 45 47 4d 45 4e 54 20 53

59 53 54 45 4d 20 53 54 4f 52 41 47 45 20 2820 20 49 4e 49 54 49 41 4c 20

31 31 32 4b 20 4e 45 58 54 20 31 30 32 34 4b20 4d 49 4e 45 58 54 45 4e 54

53 20 31 20 4d 41 58 45 58 54 45 4e 54 53 2033 32 37 36 35 20 4f 42 4a 4e

4f 20 30 20 45 58 54 45 4e 54 53 20 28 46 494c 45 20 31 20 42 4c 4f 43 4b

20 39 29 29

……………………………..省略………………………


dd輸出:
00000000h: 06 A2 00 00 7A 01 40 00 5D 01 00 00 00 00 01 06 ; .?.z.a.]......
00000010h: D5 13 00 00 01 00 00 00 38 00 00 00 C7 00 00 00 ; ?.. ...8...?..
00000020h: 00 00 00 00 01 00 02 00 00 00 00 00 00 00 22 00 ; ....... .....".
00000030h: 02 00 00 00 82 01 40 00 02 00 0C 00 18 20 00 00 ; ....?a..... ..
00000050h: 86 04 00 00 18 00 A3 1F 1A 1F 95 1D CD 1C 4E 1B ;?....?..??N.
00000060h: 7A 1A AD 19 49 17 7B 16 B3 15 D6 14 0A 14 EF 12 ;z.?I.{.??..?
00000070h: 05 12 0E 11 38 0F 68 0E 91 0D 79 0C 69 09 9C 08 ;....8.h.?y.i.?
00000080h: 5E 06 8E 05 C8 04 00 00 00 00 00 00 00 00 00 00 ; ^.??.........
00000090h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ....... .......
000000a0h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ....... .......
00000500h: 00 00 00 00 00 00 00 00 00 00 00 00 2C 01 03 02 ; ....... ...,...
…………………………..省略部分內容………………………….
00000510h: C1 08 02 C1 08 BC 43 52 45 41 54 45 20 49 4E 44 ; ?.?糃REATE IND
00000520h: 45 58 20 49 5F 54 53 23 20 4F 4E 20 43 4C 55 53 ; EXI_TS# ON CLUS
00000530h: 54 45 52 20 43 5F 54 53 23 20 50 43 54 46 52 45 ; TERC_TS# PCTFRE
00000540h: 45 20 31 30 20 49 4E 49 54 52 41 4E 53 20 32 20 ; E 10INITRANS 2
00000550h: 4D 41 58 54 52 41 4E 53 20 32 35 35 20 53 54 4F ;MAXTRANS 255 STO
00000560h: 52 41 47 45 20 28 20 20 49 4E 49 54 49 41 4C 20 ; RAGE(  INITIAL
00000570h: 36 34 4B 20 4E 45 58 54 20 31 30 32 34 4B 20 4D ; 64KNEXT 1024K M
00000580h: 49 4E 45 58 54 45 4E 54 53 20 31 20 4D 41 58 45 ;INEXTENTS 1 MAXE
00000590h: 58 54 45 4E 54 53 20 32 31 34 37 34 38 33 36 34 ; XTENTS214748364
000005a0h: 35 20 50 43 54 49 4E 43 52 45 41 53 45 20 30 20 ; 5PCTINCREASE 0
000005b0h: 4F 42 4A 4E 4F 20 37 20 45 58 54 45 4E 54 53 20 ; OBJNO 7EXTENTS
……………………………省略……………………………………
到這裡就是非常熟悉的資料塊的內容了。觀察dd輸出可見,由於系統字典全部為ascii碼字元,所以已經可以看到表中內容的明文了。

在378號塊中,從structkdbh, 14 bytes  a68開始記錄的就是塊內資料行的相關字典資訊,kdbh資料結構的偏移量為:20+24+ ktbbh. Ktbbhict*24。具體含義為kcbh結構體20位元組+ktbbh結構體中的固定長度部分24位元組+ktbbh中ktbbhitl陣列長度(每個itl槽佔24位元組,itl槽個數記錄在ktbbhict變數中)。注:這個偏移量公式只適用於mssm表空間,assm稍有區別,後詳述。

繼續觀察kdbh結構體:
BBED> p kdbh

struct kdbh, 14bytes                       a68      

   ub1 kdbhflag                             a68       0x00 (NONE)
   b1 kdbhntab                              a69       1
   b2 kdbhnrow                              a70       24
   sb2 kdbhfrre                             a72      -1
   sb2 kdbhfsbo                             a74       66
   sb2 kdbhfseo                             a76       1224
   b2 kdbhavsp                              a78       1158
   b2 kdbhtosp                              a80       1158
這裡有兩個和本文有關的變數;1、kdbhntab­­,塊裡存放資料屬於幾個表,普通表這裡是1,超過1是簇表。2、Kdbhnrow,塊裡存放的資料行數。
接下來是struct kdbt[1], 4 bytes   a130陣列,該陣列的偏移量為kdbh的偏移量+14(kdbh結構體的長度)。該塊中存放的資料所屬的每個表都要在kdbt陣列裡佔用4位元組(一個陣列元素)儲存相關資訊,對於普通表來說,資料塊記憶體儲的資料都屬於同一個表,因此這個陣列的長度肯定為1。
從sb2 kdbr[24]  a86開始記錄的就是塊記憶體放的資料行的行偏移量表,每個資料行在行偏移量表裡佔2位元組。該陣列本身的物理偏移量為:kdbh的偏移量+14(kdbh結構體的長度)+kdbh結構體中kdbhntab變數的值*4(每個表佔4位元組)。具體到378資料塊就是68+14+1*4=86。在dd輸出中找到offset 86的位置:

00000000h: 06 A200 00 7A 01 40 00 5D 01 00 00 00 00 01 06 ; .?.z.a.].. ....

00000010h: D5 1300 00 01 00 00 00 38 00 00 00 C7 00 00 00 ; ?.. ...8...?..

00000020h: 00 0000 00 01 00 02 00 00 00 00 00 00 00 22 00 ; .. ..... .....".

00000030h: 02 0000 00 82 01 40 00 02 00 0C 00 18 20 00 00 ; ....?a.. ... ..

00000040h: 5D 0100 00 00 01 18 00 FF FF 42 00 C8 04 86 04 ; ].. ....??B.??

00000050h: 86 0400 00 18 00 A3 1F 1A1F 95 1D CD 1C 4E 1B ; ?....?..??N.

00000060h: 7A 1AAD 19 49 17 7B 16 B3 15 D6 14 0A 14 EF 12 ; z.?I.{.??..?

00000070h: 05 120E 11 38 0F 68 0E 91 0D 79 0C 69 09 9C 08 ; ....8.h.?y.i.?

00000080h: 5E 068E 05 C8 04 00 00 00 00 00 00 00 00 00 00 ; ^.??.. .......

00000090h: 00 0000 00 00 00 00 00 00 00 00 00 00 00 00 00 ; .. ..... .......

000000a0h: 00 0000 00 00 00 00 00 00 00 00 00 00 00 00 00 ; .. ..... .......

上面紅色白底標註的就是378塊offset 86位置。我進行分析所用資料庫安裝在linux作業系統上,linux作業系統採用Little Endian儲存模式,因此一個變數中的各個位元組之間的順序和正常閱讀順序是顛倒的,因此該位置的值是1F A3,與dump檔案中行列表中的第一行相吻合。
0x1FA3轉換為10進製為8099,前面提到,該偏移量是在kdbh開始計算,因此要加上68,899+68=8167,觀察dd輸出中8167位置:
……………………………省略前面部分…………………………….

00001f90h: 49 4E49 54 49 41 4C 20 31 31 32 4B 20 4E 45 58 ; INITIAL 112K NEX

00001fa0h: 54 2031 30 32 34 4B 20 4D 49 4E 45 58 54 45 4E ; T 1024K MINEXTEN

00001fb0h: 54 5320 31 20 4D 41 58 45 58 54 45 4E 54 53 20 ; TS 1 MAXEXTENTS

00001fc0h: 33 3237 36 35 20 4F 42 4A 4E 4F 20 30 20 45 58 ; 32765 OBJNO 0 EX

00001fd0h: 54 454E 54 53 20 28 46 49 4C 45 20 31 20 42 4C ; TENTS (FILE 1 BL

00001fe0h: 4F 434B 20 39 29 29 2C 01 03 03 3E 64 66 03 3E ; OCK9)),...>df.>

00001ff0h: 6466 09 38 2E 30 2E 30 2E 30 2E 30 01 06 5D 01 ; df.8.0.0.0.0..].


以上紅色白底部分就是8167偏移量位置對應的資料,塊最後4個位元組“01 06 5D 01”是塊的校驗資訊,不屬於資料行內容。從這裡可以看出,oracle在將資料寫入資料塊時是從下往上進行寫入的,這是在程式設計中經常用到的一種資料儲存設計方式,在一個固定大小的儲存空間中儲存不同的資料結構,那麼就採用一種資料從上向下增長,另一種資料從下向上增長,這樣可以最大限度的利用儲存空間。
進一步觀察這段資料:

2C01 03 03 3E 64 66 03 3E 64 66 09 38 2E 30 2E 30 2E 30 2E 30

這一行字元,就是該表第一行的全部資料。第一個位元組的2C表示該行的row flag,採用的是符號位的儲存方式,即該位元組的8個位元位分別代表塊的一種屬性,該位元位為1表示該屬性對於該塊成立。2C對應為00101100與dump檔案中該行的flag相吻合:
tab 0, row 1, a0x1f1a
tl: 137 fb: --H-FL-- lb:0x1  cc: 3
col 0: [ 1]  80
col 1: [ 1]  80
col 2: [129]
4352 45 41 54 45 20 52 4f 4c 4c 42 41 43 4b 20 53 45 47 4d 45 4e 54 20 53
……………………………省略………………………………..
2C即--H-FL--中的H、F、L分別表示head data piece、first data piece、last data piece,這裡表示該行資料的以上三個piece全部儲存在當前的這個位置,沒有行連結現象。如果有行連結,則資料行的三個piece會分別儲存到不同的塊內。Flag的其他位以後做詳細分析。

2C 0103 03 3E 64 66 03 3E 64 66 09 38 2E 30 2E 30 2E 30 2E 30

       第二個位元組的01表示行上的鎖的狀態。

2C 01 0303 3E 64 66 03 3E 64 66 09 38 2E 30 2E 30 2E 30 2E 30

       第三個位元組的03表示行內欄位的數量,這裡要注意的是flag、lock資訊、column count三中資訊不一定固定為三個位元組,在簇表等其他情況下,可能是3位元組或者9位元組或者19位元組等等不同長度,需要針對segment型別分別分析。

2C 01 03 033E 64 66 03 3E 64 66 09 38 2E 30 2E 30 2E 30 2E 30

       第四個位元組的03表示之後這個欄位(行記憶體儲的第一個欄位)佔用3個位元組儲存。提取後面三個位元組的內容“3E 64 66”,與sys.bootstrap$表中第一行的第一個欄位的值“-1”吻合:
SQL> select dump(-1,16) from dual;
DUMP(-1,16)
---------------------
Typ=2 Len=3: 3e,64,66
2C 01 03 03 3E 64 66 03 3E 64 66 09 38 2E 30 2E30 2E 30 2E 30
第八個欄位的03表示第二個欄位佔用三個位元組。
2C 01 03 03 3E 64 66 03 3E 64 66 09 38 2E 30 2E 30 2E30 2E 30
第十二個位元組的09表示第三個欄位佔用9個位元組。
這裡所舉例的bootstrap$表的所有欄位都是非空的,如果一行中非末尾欄位為空,oracle加入一個值為0xff的位元組,如果行最後一個欄位為空,那麼該欄位不記錄,就是說如果一行的最後一個位元組為空,那麼行頭記錄的欄位個數會比結尾欄位不為空的行少。
如果一個欄位的長度大於256位元組,oracle將佔用三個位元組來記錄該欄位長度,且這三個位元組的第一個位元組為“0xfe”。
從這裡也可以看出,oracle在一行資料中的各個欄位是按照佇列的方式存放的,且佇列的各個元素不固定長度,所以,當oracle需要讀取某行資料的某個欄位的值時,需要遍歷該欄位前的所有欄位。因此,在oracle表的設計階段,根據業務,將使用頻繁的列儘量放在表的前面,可以減小資料庫記憶體操作的壓力,提升sql執行速度。
再分析索引簇的儲存方式。以SYS.SEG$表為例,sys.bootstrap$中該表的定義為:

CREATE TABLE SEG$("FILE#" NUMBER NOTNULL,"BLOCK#" NUMBER NOT NULL,"TYPE#" NUMBER NOTNULL,"TS#" NUMBER NOT NULL,"BLOCKS" NUMBER NOTNULL,"EXTENTS" NUMBER NOT NULL,"INIEXTS" NUMBER NOTNULL,"MINEXTS" NUMBER NOT NULL,"MAXEXTS" NUMBER NOTNULL,"EXTSIZE" NUMBER NOT NULL,"EXTPCT" NUMBER NOTNULL,"USER#" NUMBER NOT NULL,"LISTS"NUMBER,"GROUPS" NUMBER,"BITMAPRANGES" NUMBER NOTNULL,"CACHEHINT" NUMBER NOT NULL,"SCANHINT" NUMBER NOTNULL,"HWMINCR" NUMBER NOT NULL,"SPARE1"NUMBER,"SPARE2" NUMBER) STORAGE ( OBJNO 14 TABNO 2) CLUSTER C_FILE#_BLOCK#(TS#,FILE#,BLOCK#)

透過紅色字型白底部分,可見該表屬於C_FILE#_BLOCK#簇,而C_FILE#_BLOCK#簇在sys.bootstrap$中的定義為:

CREATE CLUSTERC_FILE#_BLOCK#("TS#" NUMBER,"SEGFILE#"NUMBER,"SEGBLOCK#" NUMBER) PCTFREE 10 PCTUSED 40 INITRANS 2 MAXTRANS255 STORAGE (  INITIAL 24K NEXT 1024KMINEXTENTS 1 MAXEXTENTS 2147483645 PCTINCREASE 0 OBJNO 8 EXTENTS (FILE 1 BLOCK73)) SIZE 225

可見,該簇的segment header block存放在file 1的73號block中。其中extent map的第一行指向file 1的74號block。對該block進行觀察:
Bbed輸出:

BBED> setblock 74

        BLOCK#          74
BBED> map
File: /home/oracle/vie.pdf (1)

Block: 74                                   Dba:0x0040004a

------------------------------------------------------------
KTB Data Block (Table/Cluster)
struct kcbh, 20 bytes                      a0      
struct ktbbh, 72 bytes                     a20      
struct kdbh, 14 bytes                      a92      
struct kdbt[3], 12 bytes                   a106     
sb2 kdbr[70]                               a118     
ub1 freespace[5084]                        a258     
ub1 rowdata[2846]                          a5342   
ub4 tailchk                                a8188   
oracle dump輸出:
       …………………………..省略………………….
Block header dump:  0x0040004a
Object id on Block? Y
seg/obj: 0x8  csc: 0x00.dc780c  itc: 2 flg: -  typ: 1 - DATA
     fsl: 0  fnx: 0x0 ver: 0x01
Itl           Xid                  Uba         Flag Lck        Scn/Fsc
0x01  0x0000.047.00000002 0x0040000e.0000.01  C---    0 scn 0x0000.000000f1
0x02  0x0007.024.000018d8 0x0080190f.0c3c.05  --U-    1 fsc 0x0000.00dc780e
data_block_dump,data header at 0x1cf7365c
===============
tsiz: 0x1fa0
hsiz: 0xa6
pbl: 0x1cf7365c
bdba: 0x0040004a
     76543210
flag=--------
ntab=3
nrow=70
frre=-1
fsbo=0xa6
fseo=0x1482
avsp=0x1410
tosp=0x1410
0xe:pti[0]       nrow=35 ffs=0
0x12:pti[1]     nrow=0   offs=35
0x16:pti[2]     nrow=35 ffs=35
0x1a:pri[0]     offs=0x1f84
0x1c:pri[1]     offs=0x1f68
0x1e:pri[2]     offs=0x1f4c
0x20:pri[3]     offs=0x1f31
0x22:pri[4]     offs=0x1f15
…………………….省略……………………….
dd輸出:
00000000h: 06 A2 00 00 4A 00 40 00 FE 98 ED 00 00 00 01 04 ; .?.J.a.??....
00000010h: 6C D7 00 00 01 00 00 00 08 00 00 00 0C 78 DC 00 ; l?.........x?
00000020h: 00 00 00 00 02 00 02 00 00 00 00 00 00 00 47 00 ; ....... .....G.
00000050h: 3C 0C 05 00 01 20 00 00 0E 78 DC 00 00 03 46 00 ;<.... ...x="">00000060h: FF FF A6 00 82 14 10 14 10 14 00 00 23 00 23 00 ; ????.....#.#.
00000070h: 00 00 23 00 23 00 84 1F 68 1F 4C 1F 31 1F 15 1F ;..#.#.?h.L.1...
00000080h: F9 1E DD 1E C1 1E A5 1E 89 1E 6D 1E 52 1E 36 1E ;?????m.R.6.
00000090h: 1A 1E FE 1D E3 1D C7 1D AB 1D 8F 1D 73 1D 57 1D ; ..?????s.W.
000000a0h: 3C 1D 21 1D 05 1D E9 1C CD 1C B1 1C 96 1C 7A 1C ;<.>000000b0h: 5E 1C 42 1C 26 1C 0B 1C EF 1B D3 1B 9F 1B 6B 1B ;^.B.&...???k.
000000c0h: 37 1B 03 1B CE 1A 9A 1A 66 1A 32 1A FE 19 CA 19 ;7...??f.2.??
000000d0h: 96 19 62 19 2E 19 FA 18 C6 18 91 18 5D 18 29 18 ;?b...???].).
000000e0h: F4 17 C0 17 82 14 58 17 24 17 F0 16 BC 16 87 16 ;???X.$.???
000000f0h: 53 16 24 16 F0 15 BB 15 87 15 53 15 1F 15 EB 14 ;S.$.???S...?
透過以下查詢,確定C_FILE#_BLOCK#中存放著三個segment的資料,分別是
SYS.UET$、SYS.SEG$和C_FILE#_BLOCK#本身。
SQL> select * from sys.bootstrap$ a where a.sql_text like'%C_FILE#_BLOCK#%';
     LINE#       OBJ# SQL_TEXT
---------- ------------------------------------------------------------------------------------------
        13        13 CREATE TABLE UET$("SEGFILE#" NUMBER NOTNULL,"SEGBLOCK#" NUMBER NOT NULL,"EXT#"
        14         14 CREATE TABLE SEG$("FILE#"NUMBER NOT NULL,"BLOCK#" NUMBER NOT NULL,"TYPE#" NUMBE
         8          8 CREATE CLUSTERC_FILE#_BLOCK#("TS#" NUMBER,"SEGFILE#"NUMBER,"SEGBLOCK#" NUMBER)
         9          9 CREATE INDEX I_FILE#_BLOCK# ONCLUSTER C_FILE#_BLOCK# PCTFREE 10 INITRANS 2 MAXT
在bbed中觀察kdbh結構體:
BBED> p kdbh
struct kdbh, 14 bytes                       a92      
   ub1 kdbhflag                             a92      0x00 (NONE)
   b1 kdbhntab                              a93       3
   b2 kdbhnrow                              a94       70
   sb2 kdbhfrre                             a96      -1
   sb2 kdbhfsbo                             a98       166
   sb2 kdbhfseo                             a100      5250
   b2 kdbhavsp                              a102      5136
   b2 kdbhtosp                              a104      5136
kdbh結構體中的kdbhntab變數為3,與分析相符。
按前面分析,kdbt陣列的長度是該塊記憶體儲的資料所屬的物件的個數,每個物件佔用4位元組儲存,該陣列記憶體儲的,實際上是每個不同物件在行偏移量列表裡所對應的行的範圍:
BBED> p kdbt
struct kdbt[0], 4 bytes                     a106     
   b2 kdbtoffs                              a106      0
   b2 kdbtnrow                              a108      35
struct kdbt[1], 4 bytes                     a110     
   b2 kdbtoffs                              a110      35
   b2 kdbtnrow                              a112      0
struct kdbt[2], 4 bytes                     a114     
   b2 kdbtoffs                              a114      35
   b2 kdbtnrow                              a116      35
這裡可以看到74號塊記憶體總共放了70行資料,紅色部分表示陣列的第一個元素,表示第一個物件從第0行開始,共佔用35行。
陣列的第二個元素表示第二個物件從第35行開始,共佔用了0行(其實就是SYS.UET$表,該表內沒有資料)。
陣列元素的第三個元素表示第三個物件從第35行開始,共佔用了35行。
從sb2 kdbr[70]       a118開始,存放著塊內資料行的具體偏移量列表,按照上面的分析,計算kdbr結構體的偏移量為:20+24+2*24(該塊內有兩個itl槽)+14+3*4(該塊內有來自三個物件的資料)=118,與bbed中的輸出相符:
BBED> map

File: /home/oracle/vie.pdf(1)

Block: 74                                   Dba:0x0040004a

------------------------------------------------------------

KTB Data Block(Table/Cluster)

struct kcbh, 20 bytes                      a0      
struct ktbbh, 72 bytes                     a20      
struct kdbh, 14 bytes                      a92      
struct kdbt[3], 12 bytes                   a106     
sb2 kdbr[70]                               a118     
ub1 freespace[5084]                        a258     
ub1 rowdata[2846]                          a5342   
ub4 tailchk                                a8188
進一步觀察資料行內部結構。第一行偏移量為0x1f84換算為十進位制為8068,如先前分析,還要加上kdbh的偏移量92,8068+92=8160。bbed中8160位置的資料如下:

00001fe0h: AC 00 03 01 00 01 00 00 40 00 4A 00 00 00 40 00; ?.. ...a.J...a.

00001ff0h: 4A 00 00 01 80 02 C1 02 03 C2 04 26 0106 FE 98 ;

塊最後4個位元組是校驗資訊,紅色部分就是第一行資料,逐個位元組對其進行分析:
AC 00 03 0100 01 00 00 40 00 4A 00 00 00 40 00 4A 00 00 01 80 02 C1 02 03 C2 04 26
第一個位元組AC轉換為2進位制10101100,與dump中相符:
tab 0, row 0, a0x1f84
tl: 28 fb: K-H-FL-- lb: 0x0  cc: 3
curc: 1 comc: 1 pk: 0x0040004a.0 nk: 0x0040004a.0
col  0: [ 1]  80
col  1: [ 2]  c1 02
col  2: [ 3]  c2 04 26
這裡的flag位元組,比之前單個表的flag多了一位“K”,表示本行資料存放的是cluster的key。
AC 00 03 01 00 01 00 00 40 004A 00 00 00 40 00 4A 00 00 01 80 02 C1 02 03 C2 04 26

第二個位元組表示該行的鎖資訊。
AC 00 03 01 00 01 00 00 40 00 4A 00 00 00 40 00 4A 00 00 01 8002 C1 02 03 C2 04 26

第三個位元組表示該行內共存放著3個欄位。
AC 00 03 01 00 01 00 00 40 00 4A 0000 00 40 00 4A 00 00 01 80 02 C1 02 03 C2 04 26
從第四個位元組開始的總共16個位元組,記錄了簇的相關資訊,具體為
AC 00 03 01 00 01 00 00 40 004A 00 00 00 40 00 4A 00 00 01 80 02 C1 02 03 C2 04 26
第四、五兩個位元組對應dump檔案中的curc的值:

tl: 28 fb: K-H-FL-- lb: 0x0 cc: 3

curc: 1 comc: 1 pk:0x0040004a.0 nk: 0x0040004a.0

col  0: [ 1]  80
col  1: [ 2]  c1 02
col  2: [ 3]  c2 04 26
tab 0, row 1, a0x1f68

AC 00 03 01 00 01 00 00 40 00 4A 00 00 00 40 00 4A 0000 01 80 02 C1 02 03 C2 04 26

第六、七兩個位元組對應dump檔案中的comc的值:

tl: 28 fb: K-H-FL-- lb: 0x0 cc: 3

curc: 1 comc: 1 pk: 0x0040004a.0 nk: 0x0040004a.0
col  0: [ 1]  80
col  1: [ 2]  c1 02
col  2: [ 3]  c2 04 26
tab 0, row 1, a0x1f68

AC 00 03 01 00 01 00 00 40 00 4A 00 00 00 40 004A 00 00 01 80 02 C1 02 03 C2 04 26

八到十三位元組記錄了簇中具有該key值的前一行的儲存位置,對應dump檔案中的pk的值,其儲存格式為,data block address.row index,表示具有這個key的值的上一行資料的儲存位置,這裡看到這個值是指向該行自身的,說明本行資料是簇中該key對應的第一行資料:
       tl:28 fb: K-H-FL-- lb: 0x0  cc: 3

curc: 1 comc: 1 pk:0x0040004a.0 nk: 0x0040004a.0

col  0: [ 1] 80

col  1: [ 2]  c1 02
col  2: [ 3]  c2 04 26
tab 0, row 1, a0x1f68

AC 00 03 01 00 01 00 00 40 00 4A 00 00 00 40 00 4A 0000 01 80 02 C1 02 03 C2 04 26

十四到十九位元組記錄了簇中具有該key值的後一行的儲存位置,對應dump檔案中的nk的值,其儲存格式為,data block address.row index,表示具有這個key的值的下一行資料的儲存位置,這裡看到這個值是指向該行自身的,說明本行資料是簇中該key對應的最後一行資料:

       tl: 28 fb: K-H-FL-- lb:0x0  cc: 3

curc: 1 comc: 1pk: 0x0040004a.0 nk: 0x0040004a.0

col  0: [ 1] 80

col  1: [ 2]  c1 02
col  2: [ 3]  c2 04 26
tab 0, row 1, a0x1f68
既是第一行又是最後一行表示這個鍵值對應的只有這一行資料。

AC 00 03 01 00 01 00 00 40 00 4A 00 00 00 40 00 4A 00 00 0180 02 C1 02 03 C2 04 26

下一個位元組01表示第一個欄位佔一個位元組儲存。

AC 00 03 01 00 01 00 00 40 00 4A 00 00 00 40 00 4A 00 00 01 8002 C1 02 03 C2 04 26

80是第一個欄位的值0的oracle內部表示方法。

AC 00 03 01 00 01 00 00 40 00 4A 00 00 00 40 00 4A 00 00 01 80 02C1 02 03 C2 04 26

下一個位元組02表示第二個欄位佔兩個位元組儲存。

AC 00 03 01 00 01 00 00 40 00 4A 00 00 00 40 00 4A 00 00 01 80 02 C102 03 C2 04 26

下兩個位元組C1 02是數字1的oracle內部表示方法。

AC 00 03 01 00 01 00 00 40 00 4A 00 00 00 40 00 4A 00 00 01 80 02 C102 03 C2 04 26

下一個位元組的03表示第三個欄位佔三個位元組儲存。

AC 00 03 01 00 01 00 00 40 00 4A 00 00 00 40 00 4A 00 00 01 80 02 C102 03 C2 04 26

最後三個位元組C2 04 26是數字337的oracle內部表示方法。
至此,單表及簇表透過segment header塊定為資料行的大體流程已經清晰,總結如下。
對於單表:

1、            在segment header block偏移量96的位置找到ktemh結構體的next_ktemh變數,該變數指向segment的下一個extent map。如果該變數的值為0x00000000,表示該segment的所有資料都儲存在一個segment中。

2、             在segment header block偏移量108的位置找到ktetb陣列,該陣列中每個節點佔8位元組,前4位元組為extent首地址,後4位元組為extent內塊的個數。

3、             透過extent map找到具體儲存資料的塊。

4、             在資料塊內找到kdbh資料結構,其偏移量為:20+24+ktbbh. Ktbbhict(塊內偏移量36位置,長度兩位元組)*24。

5、             找到kdbr陣列,其偏移量為:kdbh的偏移量+14(kdbh結構體的長度)+kdbh結構體中kdbhntab變數的值*4(每個表佔4位元組)。

6、             取出kdbr中行的偏移量,將該值加上kdbh的偏移量值,得到行物理偏移量。

7、             根據行物理偏移量,找到行資料起始位置,並取出其首位元組flag。

根據塊的flag判斷行頭的不同長度。如,普通無行連結資料行,flag為2C對應為二進位制00101100 --H-FL—則行內資料位於行頭偏移3位元組的位置。簇且無行連結資料行,flag為AC對應二進位制10101100 K-H-FL—則行內資料位於行頭偏移19位元組的位置。

8、             在行頭第三個欄位,取出行內欄位個數。

9、             在行頭後找到首個欄位的長度,並按該長度取出其後的欄位值。

10、          按步驟9遞迴取出行內全部欄位的值。


下一步就是要將取出的二進位制資料轉換為明文。

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

相關文章