位图索引的存储原理

如题所述

第1个回答  2016-05-28

位图索引对数据表的列的每一个键值分别存储为一个位图,Oracle对于不同的版本,不同的操作方式,数据生成均有差别.
对于8i,9i,
下面分3种方式来讨论数据的插入:
a.一次插入一行,插入多行后,一次提交;
b.每插入一行,提交一次;
c.批量插入方式,一次提交;
对于第一种方式,观察位图索引的变化情况.
a.假设插入8行相同键值的数据,如果以每行方式插入,然后一次提交,则会生成8个位图
SQL> Insert Into H病人挂号记录(Id,No,号别,执行人) Values(1,'G000001',1,'张1');
1 row inserted
SQL> /
1 row inserted
SQL> /
1 row inserted
SQL> /
1 row inserted
SQL> /
1 row inserted
SQL> /
1 row inserted
SQL> /
1 row inserted
SQL> /
1 row inserted
SQL> commit;
Commit complete
SQL> alter system dump datafile 1 block 40028;
System altered
row#0[7847] flag: -----, lock: 0
col 0; len 3; (3): d5 c5 31 --键值'张1'
col 1; len 6; (6): 00 40 9c 54 00 00 --rowid的起始位置
col 2; len 6; (6): 00 40 9c 54 00 07 --rowid的终止位置
col 3; len 2; (2): c8 ff --位图编码
row#1[7802] flag: -----, lock: 0
col 0; len 3; (3): d5 c5 31
col 1; len 6; (6): 00 40 9c 54 00 08
col 2; len 6; (6): 00 40 9c 54 00 0f
col 3; len 2; (2): c8 03
row#2[7780] flag: -----, lock: 0
col 0; len 3; (3): d5 c5 32
col 1; len 6; (6): 00 40 9c 54 00 08
col 2; len 6; (6): 00 40 9c 54 00 0f
col 3; len 1; (1): 02
row#3[7758] flag: -----, lock: 0
col 0; len 3; (3): d5 c5 33
col 1; len 6; (6): 00 40 9c 54 00 08
col 2; len 6; (6): 00 40 9c 54 00 0f
col 3; len 1; (1): 03
row#4[7736] flag: -----, lock: 2
col 0; len 3; (3): d5 c5 34
col 1; len 6; (6): 00 40 9c 54 00 08
col 2; len 6; (6): 00 40 9c 54 00 0f
col 3; len 1; (1): 04
row#5[7714] flag: -----, lock: 2
col 0; len 3; (3): d5 c5 35
col 1; len 6; (6): 00 40 9c 54 00 08
col 2; len 6; (6): 00 40 9c 54 00 0f
col 3; len 1; (1): 05
----- end of leaf block dump -----
但是,下次再插入一行相同键值的数据时,会自动合并这8行位图为一行位图,并生成一个新的索引位图行存放刚插入行的索引:
SQL> Insert Into H病人挂号记录(Id,No,号别,执行人) Values(1,'G000001',1,'张1');
1 row inserted
SQL> commit;
Commit complete
SQL> alter system dump datafile 1 block 40028;
System altered
row#0[7847] flag: -----, lock: 2
col 0; len 3; (3): d5 c5 31
col 1; len 6; (6): 00 40 9c 54 00 00
col 2; len 6; (6): 00 40 9c 54 00 07
col 3; len 2; (2): c8 ff
row#1[7825] flag: -----, lock: 2
col 0; len 3; (3): d5 c5 31
col 1; len 6; (6): 00 40 9c 54 00 08
col 2; len 6; (6): 00 40 9c 54 00 0f
col 3; len 1; (1): 00
----- end of leaf block dump -----
b.数据每行提交方式,与上面的情况相似,但有一点不一样,每提交一行,拷贝原来的位图,生成新的位图,并标记原来的位图为已删除,
标记为已删除的位图,只有索引块需要分配新的位图时,才会清除标记为已删除的位图,重用这些空间.
在8i,9i上实验的结果,与ITPUB的<Oracle 数据库性能优化>一书378页一致.
如果1000条相同键值的数据插入,将生成125个包括8条记录的位图行.
c.第三种方式,批量插入数据,insert into H病人挂号记录(Id,No,号别,执行人) select ***方式,
同一键值,只生成一次位图,只有一个位图.
SQL> Insert Into H病人挂号记录(Id,No,号别,执行人)
Select 1,'G000001',1,'张1' From dual
Union All
Select 2,'G000002',1,'张1' From dual
Union All
Select 3,'G000003',1,'张1' From dual
Union All
Select 4,'G000004',1,'张1' From dual
Union All
Select 5,'G000005',1,'张1' From dual
Union All
Select 6,'G000006',1,'张1' From dual
Union All
Select 7,'G000006',1,'张1' From dual
Union All
Select 8,'G000006',1,'张1' From dual
Union All
Select 9,'G000006',1,'张1' From dual;
SQL> commit;
Commit complete
SQL> alter system dump datafile 1 block 40028;
System altered
row#0[8006] flag: -----, lock: 2
col 0; len 3; (3): d5 c5 31
col 1; len 6; (6): 00 40 9c 54 00 00
col 2; len 6; (6): 00 40 9c 54 00 0f
col 3; len 3; (3): c9 ff 01
row#1[8030] flag: ---D-, lock: 2
col 0; NULL
col 1; NULL
col 2; NULL
col 3; NULL
----- end of leaf block dump -----
所以,位图索引最好采用批量插入方式,这样,每个键值只生成一个位图.而单行数据插入方式,每个键值将每8行数据生成一个位图.
10G的情况,则简单得多.
上面3种方式,相同键值的插入,位图的生成是一样的,只有一个位图,并且,每次提交时,并不会删除以前的位图,而是直接修改对应键值的位图.
每次插入一行数据,插入9行后提交
row#0[7763] flag: ------, lock: 2, len=29
col 0; len 3; (3): d5 c5 31
col 1; len 6; (6): 00 00 00 00 00 00
col 2; len 6; (6): 00 40 ef f2 00 0f
col 3; len 8; (8): f9 e4 d5 dc bc 01 ff 01
----- end of leaf block dump -----
再批量插入9行数据并提交
row#0[7733] flag: ------, lock: 2, len=30
col 0; len 3; (3): d5 c5 31
col 1; len 6; (6): 00 00 00 00 00 00
col 2; len 6; (6): 00 40 ef f2 00 17
col 3; len 9; (9): fa e4 d5 dc bc 01 ff ff 03
----- end of leaf block dump -----
可以看出,10G对位图索引的存储进行了优化,一个键值在索引块中只有一个位图
注意,其中有些结论并不是完全正确的,可以自己实验证明,另外,该文涉及的实验没有标明Oracle版本,不同的版本,结果有差异.

相似回答