關於ORACLE點陣圖索引內部淺論

gaopengtttt發表於2015-06-11
我們都知道ORACLE點陣圖索引適用於欄位不同值很少的情況,同時修改DML會導致整個同樣的值
全部被鎖定,這嚴重影響了併發性,所以不建議OLTP系統大量使用點陣圖索引。
但是具體點陣圖索引的內部是如何排列和組織的呢?如下將進行探討,由於水平有限可能有一定錯誤。
首先認識BITMAP索引的組織方式,首先看一下ORACLE給出的這張圖






可以看到本圖中實際上有4種顏色藍色、綠色、紅色、黃色,BITMAP 索引也是B-TREE的格式,但是其頁節點儲存
的是鍵值+ROWID範圍+點陣圖鍵的方式,這樣一來可以很明顯的感知到BITMAP所以在鍵值很少的時候其空間比較B-TREE
索引是小很多的,而在點陣圖鍵方面使用一連串的0和1來表示是否存在相應的值,這樣一來ORACLE就能快速的進行是否有
值的定位。


接下來我們進行DUMP,先建立測試表


建立這樣一張表
SQL> select id,count(*) from testt_bit group by id;
         ID   COUNT(*)
----------- ----------
          1     100000
          2     100000
          3     100000
同時在分佈上是連續的,1是1-100000行,2是100001-200000行,3是剩下的
在ID列上建立BIT MAP索引
create bitmap index TEST_BIT_IND on TESTT_BIT (ID)


首先進行BITMAP索引的結構DUMP如下:
SQL> oradebug setmypid
Statement processed.
SQL> oradebug tracefile_name
/ora11g/diag/rdbms/test/test/trace/test_ora_5604.trc
SQL> alter session set events 'immediate trace name treedump level 76306';


*** 2015-03-18 00:40:35.111
----- begin tree dump
branch: 0x1000333 16778035 (0: nrow: 8, level: 1)
   leaf: 0x1000334 16778036 (-1: nrow: 2 rrow: 2)
   leaf: 0x1000335 16778037 (0: nrow: 2 rrow: 2)
   leaf: 0x1000336 16778038 (1: nrow: 2 rrow: 2)
   leaf: 0x1000337 16778039 (2: nrow: 2 rrow: 2)
   leaf: 0x1000338 16778040 (3: nrow: 2 rrow: 2)
   leaf: 0x1000339 16778041 (4: nrow: 2 rrow: 2)
   leaf: 0x100033a 16778042 (5: nrow: 2 rrow: 2)
   leaf: 0x100033b 16778043 (6: nrow: 1 rrow: 1)
----- end tree dump
這裡清楚的看到了這個點陣圖索引的層次,首先它只有2層,1個根節點8個頁節點,每個葉節點包含2個索引條目(最後一個節點除外)


接下來我們DUMP根節點:
根據DBA(block adress)16778035進行換算
SQL>  select dbms_utility.data_block_address_file(16778035),
  2   dbms_utility.data_block_address_block(16778035) from dual;
DBMS_UTILITY.DATA_BLOCK_ADDRES DBMS_UTILITY.DATA_BLOCK_ADDRES
------------------------------ ------------------------------
                             4                            819
然後alter system dump datafile 4 block 819進行DUMP


header address 47810796042828=0x2b7bd183ba4c
kdxcolev 1
KDXCOLEV Flags = - - -
kdxcolok 0
kdxcoopc 0x80: opcode=0: iot flags=--- is converted=Y
kdxconco 4
kdxcosdc 0
kdxconro 7
kdxcofbo 42=0x2a
kdxcofeo 7972=0x1f24
kdxcoavs 7930
kdxbrlmc 16778036=0x1000334
kdxbrsno 0
kdxbrbksz 8056 
kdxbr2urrc 0
關於這一部分引用一個文件(作者不詳)
 其中的kdxcolev表示索引層級號,這裡由於我們轉儲的是根節點,所以其層級號為1。對葉子節點來說該值為0;
 kdxcolok表示該索引上是否正在發生修改塊結構的事務;kdxcoopc表示內部操作程式碼;kdxconco表示索引條目中列的數量;
 kdxcosdc表示索引結構發生變化的數量,當你修改表裡的某個索引鍵值時,該值增加;kdxconro表示當前索引節點中索引條目的數量,
 但是注意,不包括kdxbrlmc指標;kdxcofbo表示當前索引節點中可用空間的起始點相對當前塊的位移量;
 kdxcofeo表示當前索引節點中可用空間的最尾端的相對當前塊的位移量;kdxcoavs表示當前索引塊中的可用空間總量,
 也就是用kdxcofeo減去kdxcofbo得到的。kdxbrlmc表示分支節點的地址,該分支節點存放了索引鍵值小於row#0(在轉儲文件後半部分顯示)
 所含有的最小值的所有節點資訊;kdxbrsno表示最後一個被修改的索引條目號,這裡看到是0,表示該索引是新建的索引;
 kdxbrbksz表示可用資料塊的空間大小。實際從這裡已經可以看到,即便是PCTFREE設定為0,也不能用足8192位元組。


row#0[8044] dba: 16778037=0x1000335
col 0; len 2; (2):  c1 02
col 1; len 3; (3):  01 00 03
col 2; TERM
row#1[8031] dba: 16778038=0x1000336
col 0; len 2; (2):  c1 02
col 1; len 4; (4):  01 00 03 f8
col 2; TERM
row#2[8019] dba: 16778039=0x1000337
col 0; len 2; (2):  c1 03
col 1; len 3; (3):  01 00 04
col 2; TERM
row#3[8006] dba: 16778040=0x1000338
col 0; len 2; (2):  c1 03
col 1; len 4; (4):  01 00 04 bf
col 2; TERM
row#4[7998] dba: 16778041=0x1000339
col 0; len 2; (2):  c1 04
col 1; TERM
row#5[7985] dba: 16778042=0x100033a
col 0; len 2; (2):  c1 04
col 1; len 4; (4):  01 00 05 57
col 2; TERM
row#6[7972] dba: 16778043=0x100033b
col 0; len 2; (2):  c1 04
col 1; len 4; (4):  01 00 05 f8
col 2; TERM


這一部分是具體的關於各個葉節點的位置(起始位置的葉節點已經在kdxbrlmc 16778036=0x1000334給出)
其儲存方式為COL0 鍵值+COL1其對應的表中資料塊起始的DBA(可能包含BMB LEVEL1塊)
但是這裡
row#4[7998] dba: 16778041=0x1000339
col 0; len 2; (2):  c1 04
col 1; TERM
並未包含實際的表的DBA,為什麼未知



  Auxillary Map
  --------------------------------------------------------
   Extent 0     :  L1 dba:  0x01000130 Data dba:  0x01000133
   Extent 1     :  L1 dba:  0x01000130 Data dba:  0x01000138
   Extent 2     :  L1 dba:  0x01000140 Data dba:  0x01000141
   Extent 3     :  L1 dba:  0x01000140 Data dba:  0x01000148
   Extent 4     :  L1 dba:  0x01000150 Data dba:  0x01000151
   Extent 5     :  L1 dba:  0x01000150 Data dba:  0x01000158
   Extent 6     :  L1 dba:  0x01000160 Data dba:  0x01000161
   Extent 7     :  L1 dba:  0x01000160 Data dba:  0x01000168
   Extent 8     :  L1 dba:  0x01000170 Data dba:  0x01000171
   Extent 9     :  L1 dba:  0x01000170 Data dba:  0x01000178
   Extent 10    :  L1 dba:  0x01000300 Data dba:  0x01000301
   Extent 11    :  L1 dba:  0x01000300 Data dba:  0x01000308
   Extent 12    :  L1 dba:  0x01000310 Data dba:  0x01000311
   Extent 13    :  L1 dba:  0x01000310 Data dba:  0x01000318
   Extent 14    :  L1 dba:  0x01000320 Data dba:  0x01000321
   Extent 15    :  L1 dba:  0x01000320 Data dba:  0x01000328
   Extent 16    :  L1 dba:  0x01000380 Data dba:  0x01000382
   Extent 17    :  L1 dba:  0x01000400 Data dba:  0x01000402
   Extent 18    :  L1 dba:  0x01000480 Data dba:  0x01000482
   Extent 19    :  L1 dba:  0x01000500 Data dba:  0x01000502
   Extent 20    :  L1 dba:  0x01000580 Data dba:  0x01000582
比如:
row#2[8019] dba: 16778039=0x1000337
col 0; len 2; (2):  c1 03
col 1; len 3; (3):  01 00 04   
根據COL 1 01 00 04 實際是01000400,我們在BMB LEVEL3的dump中可以找到
  Extent 17    :  L1 dba:  0x01000400 Data dba:  0x01000402
實際上它是一個BMB LEVEL1塊,我們可以看他的資料塊實際上是0x01000402
可以進行DUMP這個資料塊是否是C1 03這個值
SQL> oradebug setmypid 
Statement processed.
SQL> oradebug tracefile_name
/ora11g/diag/rdbms/test/test/trace/test_ora_6108.trc
SQL>  alter system dump datafile 4 block 1026;
擷取第一行
tab 0, row 0, @0x1f89
tl: 15 fb: --H-FL-- lb: 0x1  cc: 2
col  0: [ 8]  67 61 6f 70 65 6e 67 32
col  1: [ 2]  c1 03
tab 0, row 1, @0x1f7a
可以看到這裡的col  1確實為C1 03


接下來取出其中一個塊進行分析
這裡為了更方便的論述,我將資料ID的分佈變為123123這樣的分佈
而非連續的分佈,這樣更能清晰看到點陣圖在分佈中的變化。如果為連續
那麼會全部是是FF這樣的出現
根據DUMP的BITMAP的索引結構我取出其中一個塊進行分析如下:


Leaf block dump
===============
header address 47520285706852=0x2b382dbfca64
kdxcolev 0
KDXCOLEV Flags = - - -
kdxcolok 0
kdxcoopc 0x80: opcode=0: iot flags=--- is converted=Y
kdxconco 4
kdxcosdc 0
kdxconro 2
kdxcofbo 40=0x28
kdxcofeo 950=0x3b6
kdxcoavs 910
kdxlespl 0
kdxlende 0
kdxlenxt 20971653=0x1400085
kdxleprv 0=0x0
kdxledsz 0
kdxlebksz 8032


row#0[4492] flag: ------, lock: 0, len=3540
col 0; len 2; (2):  c1 02
col 1; len 6; (6):  01 00 03 43 00 00
col 2; len 6; (6):  01 00 03 7c 00 3f
col 3; len 3519; (3519): 
 cf 49 92 24 49 92 24 49 92 cf 24 49 92 24 49 92 24 49 cf 92 24 49 92 24 49
 .....
 49 92 24 49 92 24 49 92 02 ff 1e 49 92 24 49 92 24 49 92
row#1[950] flag: ------, lock: 0, len=3542
col 0; len 2; (2):  c1 02
col 1; len 6; (6):  01 00 03 7c 00 40
col 2; len 6; (6):  01 00 06 36 00 7f
col 3; len 3521; (3521): 
 cf 24 49 92 24 49 92 24 49 cf 92 24 49 92 24 49 92 24 cf 49 92 24 49 92 24
 .....
 92 02 ff 1e 49 92 24 49 92 24 49 92 cf 24 49 92 24 49 92 24 49
 
由於在點陣圖索引中每一個鍵值被壓縮為鍵值+ROWID範圍+點陣圖鍵的方式,這裡對於row#0
可以看到
col 0; len 2; (2):  c1 02為鍵值
col 1; len 6; (6):  01 00 03 43 00 00
col 2; len 6; (6):  01 00 03 7c 00 3f
為ROWID的範圍
col 3; len 3519 就是他的點陣圖鍵,由於點陣圖鍵非常長,我們主要取出
cf 49 92 24 49 92 24 49 92 
這個片段進行分析
首先cf應該是一個標示位(作用未知)
剩下的
49 92 24 49 92 24 49 92 我們進行分析,實際上這裡每一個FF代表了一個位元組,一個位元組8位FF代表是的11111111
SQL> select to_number('49','xxxxxxxxxxxxxx') from dual;
TO_NUMBER('49','XXXXXXXXXXXXXX
------------------------------
                            73


SQL> select to_number('92','xxxxxxxxxxxxxx') from dual;
TO_NUMBER('92','XXXXXXXXXXXXXX
------------------------------
                           146


SQL> select to_number('24','xxxxxxxxxxxxxx') from dual;
TO_NUMBER('24','XXXXXXXXXXXXXX
------------------------------
                            36
實際上他們的十進位制為73 146 36 73 146 36 73 146
我們轉換為2進位制然後進行取反同時不足不滿8位的如下:
10010010(73) 01001001(146) 00100100(36) 10010010(73) 01001001(146) 00100100(36) 10010010(73) 01001001(146)
那麼組合下來如下:
1001001001001001001001001001001001001001001001001001001001001001
其中每一個點陣圖BIT代表一個ROWID他們是連續的,根據起始方位ROWID是能推算出來的。
這樣可以清晰的看到表中欄位1的取值(實際上c1 02=1)點陣圖如上,他們是交替出現和我表中資料一樣如下:
SQL> select dbms_rowid.rowid_block_number(rowid),dbms_rowid.rowid_row_number(rowid),TESTt_bi2.*  from TESTT_BI2 where dbms_rowid.rowid_block_number(rowid)=835;
DBMS_ROWID.ROWID_BLOCK_NUMBER( DBMS_ROWID.ROWID_ROW_NUMBER(RO NAME                          ID
------------------------------ ------------------------------ -------------------- -----------
                           835                              0 gaopeng                        1
                           835                              1 gaopeng                        2
                           835                              2 gaopeng                        3
                           835                              3 gaopeng                        1
                           835                              4 gaopeng                        2
                           835                              5 gaopeng                        3
                           835                              6 gaopeng                        1
                           835                              7 gaopeng                        2
                           835                              8 gaopeng                        3
                           835                              9 gaopeng                        1
                           835                             10 gaopeng                        2
                           835                             11 gaopeng                        3
                           835                             12 gaopeng                        1
                           835                             13 gaopeng                        2
                           835                             14 gaopeng                        3
                           835                             15 gaopeng                        1
                           835                             16 gaopeng                        2
                           835                             17 gaopeng                        3
..........
這段如果理解一下就是
如果SELECT * FROM TEABLE WHERE ID=1
那麼這時候點陣圖中取值為1的都是滿足條件的,將會被取出(根據ROWID)


關於閱讀這部分資訊參考
What is 6D DB B6? 
6D = 1101101 
DB = 11011011 
B6 = 10110110
Read from least significant bit (right to left) and left pad with zeros if not eight bits. 
The resulting map is 
10110110 11011011 01101101
An important point is to read the bitmap from left to right in hexadecimal in two-byte 
chunks. Read each of those chunks in binary right (least significant bit) to left. If there 
are not eight bits, then these would have been in effect, leading zeros, and are treated 
as such. The underlined zero above demonstrates this. 


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

相關文章