注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

无线时代辐射无穷

抓紧生宝宝,小心辐射

 
 
 

日志

 
 

位图索引深度探讨  

2009-11-10 14:16:43|  分类: oracle |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

一.什么是位图索引
我们目前大量使用的索引一般主要是B*Tree索引,在索引结构中存储着键值和键值的RowID,并且是一一对应的.
而位图索引主要针对大量相同值的列而创建(例如:类别,操作员,部门ID,库房ID等),
索引块的一个索引行中存储键值和起止Rowid,以及这些键值的位置编码,
位置编码中的每一位表示键值对应的数据行的有无.一个位图索引块可能指向的是几十甚至成百上千行数据的位置.


这种方式存储数据,相对于B*Tree索引,占用的空间非常小,创建和使用非常快.


当根据键值查询时,可以根据起始Rowid和位图状态,快速定位数据.
当根据键值做and,or或 in(x,y,..)查询时,直接用索引的位图进行或运算,快速得出结果行数据.
当select count(XX) 时,可以直接访问索引就快速得出统计数据.


创建语法很简单,就是在普通索引创建的语法中index前加关键字bitmap即可,例如:
create bitmap index H病人挂号记录_ix_执行人 on H病人挂号记录(执行人);


二.位图索引的特点
1.Bitmap索引的存储空间
相对于B*Tree索引,位图索引由于只存储键值的起止Rowid和位图,占用的空间非常少.
bitmap的空间占用主要根以下4个因素相关:
a.表的总记录数
b.索引列的键值多少,列的不同值越少,所需的位图就越少.
c.操作的类型,批量插入比单条插入所面的位图要少得多,8i,9i下是这样的,10G则没有这种区别,详见后面的分析.
d.索引列相同键值的物理分布,8i,9i中,不同块上的数据,相同的键值,会建立不同的位图行(段)来表示


注:本文提到的8i,9i,10g,我试验的环境是8.1.7,9.2.0.5,10.2


2.Bitmap索引创建的速度
位图索引创建时不需要排序,并且按位存储,所需的空间也少.
B*Tree索引则在创建时需要排序,定位等操作,速度要慢得多.


3.Bitmap索引允许键值为空
B*Tree索引由于不记录空值,当基于is null的查询时,会使用全表扫描,
而对位图索引列进行is null查询时,则可以使用索引.


4.Bitmap索引对表记录的高效访问
当使用count(XX),可以直接访问索引就快速得出统计数据.
当根据位图索引的列进行and,or或 in(x,y,..)查询时,直接用索引的位图进行或运算,在访问数据之前可事先过滤数据.


5.Bitmap索引对批量DML操作只需进行一次索引
由于通过位图反映数据情况,批量操作时对索引的更新速度比B*Tree索引一行一行的处理快得多.


6.Bitmap索引的锁机制
对于B*Tree索引,insert操作不会锁定其它会话的DML操作.
而位图索引,由于用位图反映数据,不同会话更新相同键值的同一位图段,insert、update、delete相互操作都会发锁定。


对于oracle 8i,9i,单行插入时,由于一个位图行(位图段)只记录8行记录,所以最多锁住相同键值的8行数据的DML操作.
而批量插入时,和10G一样,同一键值只有一个位图行(位图段),所以,相同键值的所有数据的DML操作都会被锁住。


下面,针对8i,9i观察一下锁机制:
SQL> Declare
Begin
For i In 1..9
Loop
Insert Into H病人挂号记录(Id,No,号别,执行人) Values(i,'G000001',1,'张1');
End Loop;
Commit;
End;
/
SQL> delete H病人挂号记录 where id=1;
不提交,另开一个会话,
SQL> delete H病人挂号记录 where id=9;
操作可以进行,没有锁定。
SQL> delete H病人挂号记录 where id=8;
操作等待,由于和另外一个会话操作的记录的位图索引在同一个位图段上(一个位图段最多8行),所以被锁住了。



三.位图索引的适用场合
1.位图索引是Oracle数据库在7.3版本中加入的,8i,9i企业版和个人版支持,标准版不支持.
2.基于规则的优化器无法使用Bitmap索引
3.适应于有大量重复值的列查询
4.对于8i,9i版本,不适用于单行插入,适用于批量插入的数据,
因为单行插入时,相同键值,每插入8行就会生成一行索引块中的位图段,即使相同的值.
而批量插入时,相同键值只生成一个位图段.
5.由于并发DML操作锁定的是整个位图段的大量数据行,所以位图索引主要是用于OLAP应用,也可以用于OLTP中主要为读操作的表.



关于bitmap的两个参数
SQL> show parameter bitmap;


NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
bitmap_merge_area_size integer 1048576
create_bitmap_area_size integer 8388608


其中bitmap_merge_area_size是bitmap索引进行合并操作时使用的内存区域,create_bitmap_area_size是创建时使用的内存区域.
8i,9i中,需要根据bitmap的大小以及常见的使用情况来调整.
9i以上,只需设置pga_aggregate_target的值,Oracle即会自动进和内存的调整.



四.位图索引存储原理
位图索引对数据表的列的每一个键值分别存储为一个位图,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对位图索引的存储进行了优化,一个键值在索引块中只有一个位图


关于位图索引的一些信息,可以参考:bitmap 的一点探究 http://www.itpub.net/114023.html
注意,其中有些结论并不是完全正确的,可以自己实验证明,另外,该文涉及的实验没有标明Oracle版本,不同的版本,结果有差异.

oracle 8.1.7.0.0


下面的实验:
--修改索引的一个键值(更新或插入行),是否会更新关于该索引的所有键的位图
--a.更新或插入的行,在同一个块内
--b.更新或插入的行,不在同一个块内


Oracle 8i
SQL> alter table H病人挂号记录 add 备注 char(2000);
SQL> truncate table H病人挂号记录;
SQL> Insert Into H病人挂号记录(Id,No,号别,执行人) Values(1,'G000001',1,'张1');
SQL> Insert Into H病人挂号记录(Id,No,号别,执行人) Values(2,'G000002',1,'张2');
SQL> commit;
--此时的位图
SQL> alter system dump datafile 1 block 40028;


row#0[8008] flag: -----, lock: 2
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 1; (1): 00 --位图,当一个段中,只有一条记录的时候,oracle不是采用bitmap映射的方式,而是直接给出这条记录在这个段中的位置。例如这里的00表示了这个段中的第一条记录
row#1[7986] flag: -----, lock: 2
col 0; len 3; (3): d5 c5 32
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 1; (1): 01
row#2[8030] flag: ---D-, lock: 2
col 0; NULL
col 1; NULL
col 2; NULL
col 3; NULL
----- end of leaf block dump -----
End dump data blocks tsn: 0 file#: 1 minblk 40028 maxblk 40028



再插入一行键值为'张2'的,观察键值为'张1'的位图是否改变
SQL> Insert Into H病人挂号记录(Id,No,号别,执行人) Values(2,'G000002',1,'张2');
SQL> commit;
SQL> alter system dump datafile 1 block 40028;


row#0[8008] flag: -----, lock: 0
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 1; (1): 00
row#1[7986] flag: ---D-, lock: 2
col 0; len 3; (3): d5 c5 32
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 1; (1): 01
row#2[7963] flag: -----, lock: 2
col 0; len 3; (3): d5 c5 32
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 06
----- end of leaf block dump -----
End dump data blocks tsn: 0 file#: 1 minblk 40028 maxblk 40028


对比位图的变化,键值为'张1'的位图(row#0[8008])并没有变化
键值为'张2'的位图,索引块内新增加了一个位图行(row#2[7963]),用新的位图来表示插入的行,旧的索引位图(row#1[7986])做了删除标记.
与10G的差别是,先删除了旧的位图,再增加新的位图来表示.


--b.更新或插入的行,不在同一个块内


SQL> exec show_space('H病人挂号记录','SYS','TABLE');


Free Blocks.............................1
Total Blocks............................8
Total Bytes.............................65536
Unused Blocks...........................6
Unused Bytes............................49152
Last Used Ext FileId....................1
Last Used Ext BlockId...................40019
Last Used Block.........................2


PL/SQL procedure successfully completed


--添加一列备注,添加5行信息,使表数据的存储超过1个块
SQL> Insert Into H病人挂号记录(Id,No,号别,执行人,备注) Values(1,'G000001',1,'张1',lpad('2000byte',2000,'*'));
1 row inserted
SQL> /
1 row inserted
SQL> /
1 row inserted
SQL> /
1 row inserted
SQL> /
1 row inserted


SQL> exec show_space('H病人挂号记录','SYS','TABLE');


Free Blocks.............................1
Total Blocks............................8
Total Bytes.............................65536
Unused Blocks...........................5
Unused Bytes............................40960
Last Used Ext FileId....................1
Last Used Ext BlockId...................40019
Last Used Block.........................3


PL/SQL procedure successfully completed


--对比前面的Last Used Block,由2变为了3,Unused Blocks由6变成了5,说明高水标记已移动了一个块的位置.
此时再插入数据,会放入块ID为40021的块中.


--先记下此时的索引块的情况
SQL> alter system dump datafile 1 block 40028;


row#0[8008] flag: ---D-, 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 1; (1): 00
row#1[7940] flag: ---D-, 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 09
row#2[7917] flag: ---D-, 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 19
row#3[7894] 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 39
row#4[7872] flag: ---D-, lock: 2
col 0; len 3; (3): d5 c5 31
col 1; len 6; (6): 00 40 9c 55 00 00
col 2; len 6; (6): 00 40 9c 55 00 07
col 3; len 1; (1): 00
row#5[7849] flag: -----, lock: 2
col 0; len 3; (3): d5 c5 31
col 1; len 6; (6): 00 40 9c 55 00 00
col 2; len 6; (6): 00 40 9c 55 00 07
col 3; len 2; (2): c8 03
row#6[7963] flag: -----, lock: 0
col 0; len 3; (3): d5 c5 32
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 06
----- end of leaf block dump -----
End dump data blocks tsn: 0 file#: 1 minblk 40028 maxblk 40028
--以上数据可以发现,由于刚才插入了5条键值为"张1"(d5 c5 31)的记录,
索引块中的位图行,发生了5次变化,删除了4个位图行,最后,键值为'张1'的共有两个位图行.(7894,7849)



--再插入键值为'张2'的记录,注意,此时插入的记录已经不在该表以前的块上了(块号:40019+3)
SQL> Insert Into H病人挂号记录(Id,No,号别,执行人) Values(2,'G000002',1,'张2');
SQL> commit;


--索引的块没有变,仍是40028
SQL> alter system dump datafile 1 block 40028;


row#0[7894] flag: -----, lock: 0
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 39
row#1[7849] flag: -----, lock: 0
col 0; len 3; (3): d5 c5 31
col 1; len 6; (6): 00 40 9c 55 00 00
col 2; len 6; (6): 00 40 9c 55 00 07
col 3; len 2; (2): c8 03
row#2[7963] flag: -----, lock: 2
col 0; len 3; (3): d5 c5 32
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 06
row#3[7827] flag: -----, lock: 2
col 0; len 3; (3): d5 c5 32
col 1; len 6; (6): 00 40 9c 55 00 00
col 2; len 6; (6): 00 40 9c 55 00 07
col 3; len 1; (1): 02
----- end of leaf block dump -----
End dump data blocks tsn: 0 file#: 1 minblk 40028 maxblk 40028


--与上面的比较发现,索引块内键值为"张2"的范围(7963)没有像上次增加5条键值为"张1"的那样被删除,而是只增加了新的段:7827
--这是因为新插入的数据行已经不在原来的块上,原有的索引块内的段位图已无法表示,需要索引块内用新的段位图来表示.
--另外,注意到由于新的索引数据的产生,以前插入5条记录时标记为删除(-D)的索引块内的段已经被回收.


--这一点与10G差别较大,10G中一个键值一直只有一个位图,即使键值对应的数据分布在不同的数据块中.

Oracle 10G R2


下面的实验:
--修改索引的一个键值(更新或插入行),是否会更新关于该索引的所有键的位图
--a.更新或插入的行,在同一个块内
--b.更新或插入的行,不在同一个块内


SQL> alter table H病人挂号记录 add 备注 char(2000);
SQL> truncate table H病人挂号记录;
SQL> Insert Into H病人挂号记录(Id,No,号别,执行人) Values(1,'G000001',1,'张1');
SQL> Insert Into H病人挂号记录(Id,No,号别,执行人) Values(2,'G000002',1,'张2');
SQL> commit;
--此时的位图
SQL> alter system dump datafile 1 block 61434;


row#0[7988] flag: ------, lock: 2, len=27
col 0; len 3; (3): d5 c5 31 --键值'张1'
col 1; len 6; (6): 00 00 00 00 00 00 --rowid的起始位置
col 2; len 6; (6): 00 40 ef f2 00 07 --rowid的终止位置
col 3; len 6; (6): c0 d2 d5 dc bc 01 --位图
row#1[7940] flag: ------, lock: 2, len=27
col 0; len 3; (3): d5 c5 32
col 1; len 6; (6): 00 00 00 00 00 00
col 2; len 6; (6): 00 40 ef f2 00 07
col 3; len 6; (6): c1 d2 d5 dc bc 01
----- end of leaf block dump -----
End dump data blocks tsn: 0 file#: 1 minblk 61434 maxblk 61434


再插入一行键值为'张2'的,观察键值为'张1'的位图是否改变
SQL> Insert Into H病人挂号记录(Id,No,号别,执行人) Values(2,'G000002',1,'张2');
SQL> commit;
SQL> alter system dump datafile 1 block 61434;


row#0[7988] flag: ------, lock: 0, len=27
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 07
col 3; len 6; (6): c0 d2 d5 dc bc 01
row#1[7912] flag: ------, lock: 2, len=28
col 0; len 3; (3): d5 c5 32
col 1; len 6; (6): 00 00 00 00 00 00
col 2; len 6; (6): 00 40 ef f2 00 07
col 3; len 7; (7): f8 e4 d5 dc bc 01 06
----- end of leaf block dump -----
End dump data blocks tsn: 0 file#: 1 minblk 61434 maxblk 61434


--对比发现,索引键'张1'的位图c0 d2 d5 dc bc 01没有发生变化,
索引键'张2'的位图c1 d2 d5 dc bc 01变成了f8 e4 d5 dc bc 01 06
与8i的差别是,不用删除旧的位图,而是直接修改对应的索引键的位图来表示新的数据信息.



--b.更新或插入的行,不在同一个块内


SQL> exec show_space('H病人挂号记录','SYS','TABLE');


Free Blocks.............................1
Total Blocks............................8
Total Bytes.............................65536
Unused Blocks...........................6
Unused Bytes............................49152
Last Used Ext FileId....................1
Last Used Ext BlockId...................61425
Last Used Block.........................2


PL/SQL procedure successfully completed


--添加一列备注,添加5行信息,使表数据的存储超过1个块
SQL> Insert Into H病人挂号记录(Id,No,号别,执行人,备注) Values(1,'G000001',1,'张1',lpad('2000byte',2000,'*'));
1 row inserted
SQL> /
1 row inserted
SQL> /
1 row inserted
SQL> /
1 row inserted
SQL> /
1 row inserted


SQL> exec show_space('H病人挂号记录','SYS','TABLE');


Free Blocks.............................1
Total Blocks............................8
Total Bytes.............................65536
Unused Blocks...........................5
Unused Bytes............................40960
Last Used Ext FileId....................1
Last Used Ext BlockId...................61425
Last Used Block.........................3


PL/SQL procedure successfully completed


--对比前面的Last Used Block,由2变为了3,Unused Blocks由6变成了5,
说明高水标记已移动了一个块的位置.此时再插入数据,会放入块ID为61428的块中.


--先记下此时的索引块的情况
SQL> alter system dump datafile 1 block 61434;


row#0[7767] flag: ------, lock: 2, len=31
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 f3 00 07
col 3; len 10; (10): f8 e4 d5 dc bc 01 39 f8 56 03
row#1[7912] flag: ------, lock: 0, len=28
col 0; len 3; (3): d5 c5 32
col 1; len 6; (6): 00 00 00 00 00 00
col 2; len 6; (6): 00 40 ef f2 00 07
col 3; len 7; (7): f8 e4 d5 dc bc 01 06
----- end of leaf block dump -----
End dump data blocks tsn: 0 file#: 1 minblk 61434 maxblk 61434


--再插入键值为'张2'的记录,注意,此时插入的记录已经不在该表以前的块上了(块号:61425+3)
SQL> Insert Into H病人挂号记录(Id,No,号别,执行人) Values(2,'G000002',1,'张2');
SQL> commit;


--索引的块没有变,仍是61434
SQL> alter system dump datafile 1 block 61434;


row#0[7767] flag: ------, lock: 0, len=31
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 f3 00 07
col 3; len 10; (10): f8 e4 d5 dc bc 01 39 f8 56 03
row#1[7737] flag: ------, lock: 2, len=30
col 0; len 3; (3): d5 c5 32
col 1; len 6; (6): 00 00 00 00 00 00
col 2; len 6; (6): 00 40 ef f3 00 07
col 3; len 9; (9): f8 e4 d5 dc bc 01 06 c2 44
----- end of leaf block dump -----
End dump data blocks tsn: 0 file#: 1 minblk 61434 maxblk 61434


--与上面的比较发现,索引块内的位图行并没有增加,只是改变了位图f8 e4 d5 dc bc 01 06变成了f8 e4 d5 dc bc 01 06 c2 44
这一点,与8i区别很大,也就是说,10G中一个键值只有一个索引位图,
而8i中一个键值可能有一个或多个位图,单行数据插入时,每8行数据,或者当数据行分布在不同的数据块时,就会新建一个位图来记录.


是不是数据量少,才只有一个位图呢,下面再插入10000行数据的情况下,观察发现一个键值仍然只有一个位图.
一个位图索引块,由于采用位方式表示键值对应的数据,表达的数据是非常大的.
SQL> Declare
Begin
For i In 1..10000
Loop
Insert Into H病人挂号记录(Id,No,号别,执行人) Values(i,'G000001',1,'张1');
End Loop;
Commit;
End;
/


SQL> exec show_space('H病人挂号记录_IX_执行人','SYS','INDEX');


Free Blocks.............................0
Total Blocks............................8
Total Bytes.............................65536
Unused Blocks...........................6
Unused Bytes............................49152
Last Used Ext FileId....................1
Last Used Ext BlockId...................61433
Last Used Block.........................2


PL/SQL procedure successfully completed


SQL> alter system dump datafile 1 block 61434;


row#0[390] flag: ------, lock: 2, len=1530
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 f3 27 00 67
col 3; len 1508; (1508):
ff e4 d5 dc bc 01 ff ff ff ff ff ff ff ff cf ff ff ff ff ff ff ff ff cf ff
ff ff ff ff ff ff ff cb ff ff ff 7f ff 3b ff ff ff ff ff ff ff ff cf ff ff
ff ff ff ff ff ff cf ff ff ff ff ff ff ff ff cb ff ff ff 0f ff 3b ff ff ff
ff ff ff ff ff cf ff ff ff ff ff ff ff ff cf ff ff ff ff ff ff ff ff cb ff
ff ff 0f ff 3b ff ff ff ff ff ff ff ff cf ff ff ff ff ff ff ff ff cf ff ff
ff ff ff ff ff ff cb ff ff ff 0f ff 3b ff ff ff ff ff ff ff ff cf ff ff ff
ff ff ff ff ff cf ff ff ff ff ff ff ff ff cb ff ff ff 0f ff 3b ff ff ff ff
ff ff ff ff cf ff ff ff ff ff ff ff ff cf ff ff ff ff ff ff ff ff cb ff ff
ff 0f ff 3b ff ff ff ff ff ff ff ff cf ff ff ff ff ff ff ff ff cf ff ff ff
ff ff ff ff ff cb ff ff ff 0f ff a3 06 ff ff ff ff ff ff ff ff cf ff ff ff
ff ff ff ff ff cf ff ff ff ff ff ff ff ff cb ff ff ff 0f ff 3b ff ff ff ff
ff ff ff ff cf ff ff ff ff ff ff ff ff cf ff ff ff ff ff ff ff ff cb ff ff
ff 0f ff 3b ff ff ff ff ff ff ff ff cf ff ff ff ff ff ff ff ff cf ff ff ff
ff ff ff ff ff cb ff ff ff 0f ff 3b ff ff ff ff ff ff ff ff cf ff ff ff ff
ff ff ff ff cf ff ff ff ff ff ff ff ff cb ff ff ff 0f ff 3b ff ff ff ff ff
ff ff ff cf ff ff ff ff ff ff ff ff cf ff ff ff ff ff ff ff ff cb ff ff ff
0f ff 3b ff ff ff ff ff ff ff ff cf ff ff ff ff ff ff ff ff cf ff ff ff ff
ff ff ff ff cb ff ff ff 0f ff 3b ff ff ff ff ff ff ff ff cf ff ff ff ff ff
ff ff ff cf ff ff ff ff ff ff ff ff cb ff ff ff 0f ff 3b ff ff ff ff ff ff
ff ff cf ff ff ff ff ff ff ff ff cf ff ff ff ff ff ff ff ff cb ff ff ff 0f
ff bb ae 04 ff ff ff ff ff ff ff ff cf ff ff ff ff ff ff ff ff cf ff ff ff
ff ff ff ff ff cb ff ff ff 0f ff 3b ff ff ff ff ff ff ff ff cf ff ff ff ff
ff ff ff ff cf ff ff ff ff ff ff ff ff cb ff ff ff 0f ff 3b ff ff ff ff ff
ff ff ff cf ff ff ff ff ff ff ff ff cf ff ff ff ff ff ff ff ff cb ff ff ff
0f ff 3b ff ff ff ff ff ff ff ff cf ff ff ff ff ff ff ff ff cf ff ff ff ff
ff ff ff ff cb ff ff ff 0f ff 3b ff ff ff ff ff ff ff ff cf ff ff ff ff ff
ff ff ff cf ff ff ff ff ff ff ff ff cb ff ff ff 0f ff 3b ff ff ff ff ff ff
ff ff cf ff ff ff ff ff ff ff ff cf ff ff ff ff ff ff ff ff cb ff ff ff 0f
ff 3b ff ff ff ff ff ff ff ff cf ff ff ff ff ff ff ff ff cf ff ff ff ff ff
ff ff ff cb ff ff ff 0f ff 3b ff ff ff ff ff ff ff ff cf ff ff ff ff ff ff
ff ff cf ff ff ff ff ff ff ff ff cb ff ff ff 0f ff 3b ff ff ff ff ff ff ff
ff cf ff ff ff ff ff ff ff ff cf ff ff ff ff ff ff ff ff cb ff ff ff 0f ff
3b ff ff ff ff ff ff ff ff cf ff ff ff ff ff ff ff ff cf ff ff ff ff ff ff
ff ff cb ff ff ff 0f ff 3b ff ff ff ff ff ff ff ff cf ff ff ff ff ff ff ff
ff cf ff ff ff ff ff ff ff ff cb ff ff ff 0f ff 3b ff ff ff ff ff ff ff ff
cf ff ff ff ff ff ff ff ff cf ff ff ff ff ff ff ff ff cb ff ff ff 0f ff 3b
ff ff ff ff ff ff ff ff cf ff ff ff ff ff ff ff ff cf ff ff ff ff ff ff ff
ff cb ff ff ff 0f ff 3b ff ff ff ff ff ff ff ff cf ff ff ff ff ff ff ff ff
cf ff ff ff ff ff ff ff ff cb ff ff ff 0f ff 3b ff ff ff ff ff ff ff ff cf
ff ff ff ff ff ff ff ff cf ff ff ff ff ff ff ff ff cb ff ff ff 0f ff 3b ff
ff ff ff ff ff ff ff cf ff ff ff ff ff ff ff ff cf ff ff ff ff ff ff ff ff
cb ff ff ff 0f ff 3b ff ff ff ff ff ff ff ff cf ff ff ff ff ff ff ff ff cf
ff ff ff ff ff ff ff ff cb ff ff ff 0f ff 3b ff ff ff ff ff ff ff ff cf ff
ff ff ff ff ff ff ff cf ff ff ff ff ff ff ff ff cb ff ff ff 0f ff 3b ff ff
ff ff ff ff ff ff cf ff ff ff ff ff ff ff ff cf ff ff ff ff ff ff ff ff cb
ff ff ff 0f ff 3b ff ff ff ff ff ff ff ff cf ff ff ff ff ff ff ff ff cf ff
ff ff ff ff ff ff ff cb ff ff ff 0f ff 3b ff ff ff ff ff ff ff ff cf ff ff
ff ff ff ff ff ff cf ff ff ff ff ff ff ff ff cb ff ff ff 0f ff 3b ff ff ff
ff ff ff ff ff cf ff ff ff ff ff ff ff ff cf ff ff ff ff ff ff ff ff cb ff
ff ff 0f ff 3b ff ff ff ff ff ff ff ff cf ff ff ff ff ff ff ff ff cf ff ff
ff ff ff ff ff ff cb ff ff ff 0f ff 3b ff ff ff ff ff ff ff ff cf ff ff ff
ff ff ff ff ff cf ff ff ff ff ff ff ff ff cb ff ff ff 0f ff 3b ff ff ff ff
ff ff ff ff cf ff ff ff ff ff ff ff ff cf ff ff ff ff ff ff ff ff cb ff ff
ff 0f ff 3b ff ff ff ff ff ff ff ff cf ff ff ff ff ff ff ff ff cf ff ff ff
ff ff ff ff ff cb ff ff ff 0f ff 3b ff ff ff ff ff ff ff ff cf ff ff ff ff
ff ff ff ff cf ff ff ff ff ff ff ff ff cb ff ff ff 0f ff 3b ff ff ff ff ff
ff ff ff cf ff ff ff ff ff ff ff ff cf ff ff ff ff ff ff ff ff cb ff ff ff
0f ff 3b ff ff ff ff ff ff ff ff cf ff ff ff ff ff ff ff ff cf ff ff ff ff
ff ff ff ff cb ff ff ff 0f ff 3b ff ff ff ff ff ff ff ff cf ff ff ff ff ff
ff ff ff cf ff ff ff ff ff ff ff ff cb ff ff ff 0f ff 3b ff ff ff ff ff ff
ff ff cc ff ff ff ff 01
----- end of leaf block dump -----

下面的对比实验表明,使用位图索引时,由于访问的索引块少了很多,查询速度得到较大的提高
查询1087行数据,B树索引用了157块,CPU耗用0.05,位图索引用了82块,CPU耗用0.01



--使用B*Tree索引查询
SQL> drop index H票据使用明细_IX_领用ID;
SQL> create index H票据使用明细_IX_领用ID on H票据使用明细(领用ID);
SQL> Select * From H票据使用明细 Where 领用id=102;


已选择1087行。


Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 TABLE ACCESS (BY INDEX ROWID) OF 'H票据使用明细'
2 1 INDEX (RANGE SCAN) OF 'H票据使用明细_IX_领用ID' (NON-UNIQUE)
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
157 consistent gets
0 physical reads
0 redo size
53393 bytes sent via SQL*Net to client
1295 bytes received via SQL*Net from client
74 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1087 rows processed


Select *
From
H票据使用明细 Where 领用id=102



call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.00 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 74 0.01 0.05 2 157 0 1087
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 76 0.01 0.05 2 157 0 1087


Misses in library cache during parse: 1
Optimizer goal: ALL_ROWS
Parsing user id: 311


Rows Row Source Operation
------- ---------------------------------------------------
1087 TABLE ACCESS BY INDEX ROWID H票据使用明细
1087 INDEX RANGE SCAN H票据使用明细_IX_领用ID (object id 49919)



--使用位图索引查询
SQL> drop index H票据使用明细_IX_领用ID;
SQL> create bitmap index H票据使用明细_IX_领用ID on H票据使用明细(领用ID);
SQL> Select * From H票据使用明细 Where 领用id=102;


已选择1087行。


Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=ALL_ROWS (Cost=3 Card=10 Bytes=1060)
1 0 TABLE ACCESS (BY INDEX ROWID) OF 'H票据使用明细' (Cost=3 Card=10 Bytes=1060)
2 1 BITMAP CONVERSION (TO ROWIDS)
3 2 BITMAP INDEX (SINGLE VALUE) OF 'H票据使用明细_IX_领用ID'
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
82 consistent gets
0 physical reads
0 redo size
53393 bytes sent via SQL*Net to client
1295 bytes received via SQL*Net from client
74 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1087 rows processed


Select *
From
H票据使用明细 Where 领用id=102



call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.00 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 74 0.01 0.01 0 82 0 1087
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 76 0.01 0.01 0 82 0 1087


Misses in library cache during parse: 0
Optimizer goal: ALL_ROWS
Parsing user id: 311


Rows Row Source Operation
------- ---------------------------------------------------
1087 TABLE ACCESS BY INDEX ROWID H票据使用明细
1087 BITMAP CONVERSION TO ROWIDS
1 BITMAP INDEX SINGLE VALUE H票据使用明细_IX_领用ID (object id 49920)

  评论这张
 
阅读(494)| 评论(1)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017