位圖(bitmap)索引是另外一種索引類型它的組織形式與B樹索引相同也是一棵平衡樹與B樹索引的區別在於葉子節點裡存放索引條目的方式不同從前面我們知道B樹索引的葉子節點裡對於表裡的每個數據行如果被索引列的值不為空的則會為該記錄行在葉子節點裡維護一個對應的索引條目
而位圖索引則不是這樣其葉子節點裡存放的索引條目如下圖所示
假設某個表T裡所有的記錄在列C上只具有三個值和在表T的C列上創建位圖索引以後則葉子節點的內容如圖所示可以看到位圖索引只有三個索引條目也就是每個C列的值對應一個索引條目位圖索引條目上還包含表裡第一條記錄所對應的ROWID以及最後一條記錄所對應的ROWID索引條目的最後一部分則是由多個bit位所組成的bitmap每個bit位就對應一條記錄
位圖索引的結構
當發出where c=這樣的SQL語句時oracle會去搜索所在的索引條目然後掃描該索引條目中的bitmap裡所有的bit位第一個bit位為則說明第一條記錄上的C值為於是返回第一條記錄所在的ROWID(根據該索引條目裡記錄的start ROWID加上行號得到該記錄所在的ROWID)第二個bit位為則說明第二條記錄上的C值不為依此類推另外如果索引列為空也會在位圖索引裡記錄也就是將對應的bit位設置為即可
如果索引列上不同值的個數比較少的時候比如對於性別列(男或女)等則使用位圖索引會比較好因為它對空間的占用非常少(因為都是用bit位來表示表裡的數據行)從而在掃描索引的時候掃描的索引塊的個數也比較少可以試想一下如果在列的不同值非常多的列上比如主鍵列上創建位圖索引則產生的索引條目就等於表裡記錄的條數同時每個索引條目裡的bitmap裡只有一個其它都是這樣還不如B樹索引的效率高
如果被索引的列經常被更新的話則不適合使用位圖索引因為當更新位圖所在的列時由於要在不同的索引條目之間修改bit位比如將第一條記錄從變為則必須將所在的索引條目的第一個bit位改為再將所在的索引條目的第一個bit位改為因此在更新索引條目的過程中會鎖定位圖索引裡多個索引條目也就是同時只能有一個用戶能夠更新表T從而降低了並發性
位圖索引比較適合用在數據倉庫系統裡不適合用在OLTP系統裡
SQL> create table t_bitmap_test as
select rownum as idtrunc(dbms_randomvalue()) as bitcol
from dba_objects where rownum<=;
SQL> select * from t_bitmap_test;
ID BITCOL
SQL> connect hr/hr
已連接
SQL> alter session set events trace name context forever level ;
會話已更改
SQL> create bitmap index idx_t_bitmap_test on t_bitmap_test(bitcol);
索引已創建
SQL> alter session set events trace name context off;
會話已更改
SQL> select object_id from user_objects where object_name=IDX_T_BITMAP_TEST;
OBJECT_ID
SQL> alter session set events immediate trace name TREEDUMP level ;
會話已更改
事件用來跟蹤創建bitmap索引的過程而TREEDUMP則用來轉儲索引的樹狀結構打開轉儲出來的文件
*** SESSION ID:() ::
qerbiARwo: bitmap size is
qerbiIPI default pctfree=
qerbiIPI length=
qerbiAllocate pfree= space=
qerbiStart first start
qerbiRop: rid=cce new=Y key: (): c
qerbiCmpSz notfound pctfree=
qerbiCmpSz adjblksize= length=
qerbiRop keysize= maxbm=
kdibcoinit(): srid=cce
qerbiRop: rid=cce new=Y key: (): c
kdibcoinit(): srid=cce
qerbiRop: rid=cce new=Y key: (): c
kdibcoinit(c): srid=cce
qerbiRop: rid=cce new=N key: (): c
qerbiRop: rid=cce new=N key: (): c
qerbiRop: rid=cce new=N key: (): c
qerbiRop: rid=cce new=N key: (): c
qerbiRop: rid=cce new=N key: (): c
qerbiRop: rid=cce new=N key: (): c
qerbiRop: rid=cce new=N key: (): c
qerbiRop: rid=ccea new=N key: (): c
qerbiRop: rid=cceb new=N key: (): c
qerbiRop: rid=ccec new=N key: (): c
qerbiRop: rid=cced new=N key: (): c
qerbiRop: rid=ccee new=N key: (): c
qerbiRop: rid=ccef new=N key: (): c
qerbiRop: rid=cce new=N key: (): c
qerbiRop: rid=cce new=N key: (): c
qerbiRop: rid=cce new=N key: (): c
qerbiRop: rid=cce new=N key: (): c
kdibcoend(): erid=ccestatus=
qerbiCon: key: (): c
srid=cce erid=cce bitmap: (): ca
kdibcoend(): erid=ccestatus=
qerbiCon: key: (): c
srid=cce erid=cce bitmap: (): ca c
kdibcoend(c): erid=ccestatus=
qerbiCon: key: (): c
srid=cce erid=cce bitmap: (): ca
這一段是創建bitmap索引的過程我們先把被索引的列的值換算成十六進制
SQL> select dump()dump()dump() from dual;
DUMP() DUMP() DUMP()
Typ= Len=: Typ= Len=: Typ= Len=:
對應的十六進制則是也就是上面轉儲部分顯示的key部分的鍵值
可
以看到oracle在創建bitmap索引時先從第一條記錄開始掃描取出第一條記錄的鍵值(bitcol=)也就是qerbiRop:
rid=cce new=Y key: (): c
new=Y說明這是一個新的鍵值因此會對應到一個索引條目掃描第二條記錄時發現bitcol=該鍵值也是一個新的鍵值因此產生一個新
的索引條目對應qerbiRop: rid=cce new=Y key: (): c
掃描到第三條記錄時發現bitcol=該鍵值也是一個新的鍵值因此產生第三個索引條目對應qerbiRop:
rid=cce new=Y key: (): c
接下來掃描到的記錄所對應的bitcol的值都是中的一個因此都不會產生新的索引條目因此它們的new都為N
然後掃描完表裡的所有記錄以後開始創建bitmap索引條目也就是下面的部分
qerbiCon: key: (): c
srid=cce erid=cce bitmap: (): ca
kdibcoend(): erid=ccestatus=
qerbiCon: key: (): c
srid=cce erid=cce bitmap: (): ca c
kdibcoend(c): erid=ccestatus=
qerbiCon: key: (): c
srid=cce erid=cce bitmap: (): ca
這裡的srid表示start rowiderid表示end rowid
可以看到總共產生了個索引條目其key分別為
這
個索引條目的start rowid和end
rowid的格式分兩部分中間用點隔開點左邊的表示文件號(從左邊第一個字節開始的個字節表示)和數據塊號(從左邊第五個字節開始的個字節表
示)點右邊表示數據塊裡的行號這裡的顯示可以看到這條記錄都位於相同的數據塊裡這裡的c表示文件號
SQL> select syspkg_number_transf_hex_to_dec(c)/ file# FROM dual;
FILE#
SQL> select syspkg_number_transf_hex_to_dec(ce) as blk# FROM dual;
BLK#
因此這條記錄在號數據文件的號數據塊裡我們可以使用dbms_rowid來驗證
SQL> select distinct dbms_rowidrowid_relative_fno(rowid) as file#
dbms_rowidrowid_block_number(rowid) as block#
from t_bitmap_test;
FILE# BLOCK#
可以看到完全符合
每個索引條目的bitmap : ()部分表示的也就是前面說到的bit位了由組成
按照前面bitmap的理論這條記錄所對應的三個索引條目的bitmap的樣子應該為
Key_value start_rowid end_rowid 理論上的bitmap 轉儲文件的bitmap
cce cce ca
cce cce ca c
cce cce ca
轉儲文件裡的bitmap如何對應到bit位呢 ?首先第一個字節的ca可以不考慮暫時不知道第一個字節是什麼意思只考慮後三個字節比如對於key_value=來說對應的二進制為
SQL> col c format a
SQL> col c format a
SQL> col c format a
SQL> select syspkg_number_transf_hex_to_bin() as c
syspkg_number_transf_hex_to_bin() as c
syspkg_number_transf_hex_to_bin() as c from dual;
C C C
其中不足位的前面用補齊因此C=C=C=
在二進制裡每個應該倒過來從右到左排列因此為
C C C
然後將它們組織為一個由多個bit位組成的bitmap的話仍然從右到左依次取出每個bit位於是我們有我們可以把這個bit串與理論上的bitmap比較一下
很明顯除了最後多出來的個以外其余部分完全一致而最後多出的並不影響這個索引條目的使用
類似的我們可以使用相同的方法來依次驗證另外兩個鍵值都是符合理論值的
數據都在一個數據塊裡的情況比較容易理解如果被索引的數據分布在多個數據塊裡呢?
經常會有人問到只記錄start rowid和end rowid如果被索引的記錄分布在多個數據塊裡那麼oracle如何根據start rowed來找到bitmap裡的bit=所對應的記錄的rowid呢?
創建一個表
SQL> create table t_bitmap_(id numberbitcol char());
insert into t_bitmap_ values(A);
insert into t_bitmap_ values(A);
insert into t_bitmap_ values(A);
insert into t_bitmap_ values(B);
insert into t_bitmap_ values(A);
insert into t_bitmap_ values(A);
insert into t_bitmap_ values(B);
insert into t_bitmap_ values(A);
insert into t_bitmap_ values(A);
insert into t_bitmap_ values(A);
insert into t_bitmap_ values(B);
insert into t_bitmap_ values(B);
insert into t_bitmap_ values(B);
insert into t_bitmap_ values(B);
insert into t_bitmap_ values(B);
insert into t_bitmap_ values(B);
COMMIT;
獲得這條記錄所在的數據塊號
SQL> select distinct dbms_rowidrowid_relative_fno(rowid) as file#
dbms_rowidrowid_block_number(rowid) as block#
from t_bitmap_;
FILE# BLOCK#
可以知道這條記錄分布在個數據塊裡
然後跟蹤對於bitcol列上的bitmap索引的創建過程
alter session set events trace name context forever level ;
create bitmap index idx_t_bitmap_ on t_bitmap_(bitcol);
alter session set events trace name context off;
從轉儲出來的文件可以看到最終形成了兩個索引條目
……
qerbiCon: key: ():
……
srid=cd erid=cd bitmap: (): c c f b f
……
qerbiCon: key: ():
……
srid=cd erid=cd bitmap: (): f f c a c
*** ::
很明顯這裡的兩個索引條目的start rowed和end rowed是不相同的
鍵值為A的索引條目為
srid=cd erid=cd bitmap: (): c c f b f
其數據塊從d到d也就是
SQL> select syspkg_number_transf_hex_to_dec(d) as s_blk#
syspkg_number_transf_hex_to_dec(d) as e_blk#
from dual;
S_BLK# E_BLK#
而鍵值B的索引條目為
srid=cd erid=cd bitmap: (): f f c a c
其數據塊從d到d也就是
SQL> select syspkg_number_transf_hex_to_dec(d) as s_blk#
syspkg_number_transf_hex_to_dec(d) as e_blk#
from dual;
S_BLK# E_BLK#
這個時候
SQL> select substr(bitcol) as bitcoldbms_rowidrowid_block_number(rowid) as block# from t_bitmap_;
BI BLOCK#
B
A
A
A
B
B
B
B
B
A
A
A
B
A
A
B
這時oracle放了很多的bit來對應這條記錄但是oracle如何根據這些bit位來找對應的rowid就猜不出了
From:http://tw.wingwit.com/Article/program/Oracle/201311/17926.html