Oracle bitmap 初步解析(二)

转载:http://www.itpub.net/thread-1003097-1-1.html

位图(bitmap)索引是另外一种索引类型,它的组织形式与B树索引相同,也是一棵平衡树。与B树索引的区别在于叶子节点里存放索引条目的方式不同。从前面我们知道,B树索引的叶子节点里,对于表里的每个数据行,如果被索引列的值不为空的,则会为该记录行在叶子节点里维护一个对应的索引条目。
而位图索引则不是这样,其叶子节点里存放的索引条目如下图所示。


假设某个表T里所有的记录在列C1上只具有三个值:01、02和03。在表T的C1列上创建位图索引以后,则叶子节点的内容如图9-14所示。可以看到,位图索引只有三个索引条目,也就是每个C1列的值对应一个索引条目。位图索引条目上还包含表里第一条记录所对应的ROWID以及最后一条记录所对应的ROWID。索引条目的最后一部分则是由多个bit位所组成的bitmap,每个bit位就对应一条记录。



位图索引的结构

当发出where>oracle会去搜索01所在的索引条目,然后扫描该索引条目中的bitmap里所有的bit位。第一个bit位为1,则说明第一条记录上的C1值为01,于是返回第一条记录所在的ROWID(根据该索引条目里记录的start ROWID加上行号得到该记录所在的ROWID)。第二个bit位为0,则说明第二条记录上的C1值不为01,依此类推。另外,如果索引列为空,也会在位图索引里记录,也就是将对应的bit位设置为0即可。
如果索引列上不同值的个数比较少的时候,比如对于性别列(男或女)等,则使用位图索引会比较好,因为它对空间的占用非常少(因为都是用bit位来表示表里的数据行),从而在扫描索引的时候,扫描的索引块的个数也比较少。可以试想一下,如果在列的不同值非常多的列上,比如主键列上,创建位图索引,则产生的索引条目就等于表里记录的条数,同时每个索引条目里的bitmap里,只有一个1,其它都是0。这样还不如B树索引的效率高。
如果被索引的列经常被更新的话,则不适合使用位图索引。因为当更新位图所在的列时,由于要在不同的索引条目之间修改bit位,比如将第一条记录从01变为02,则必须将01所在的索引条目的第一个bit位改为0,再将02所在的索引条目的第一个bit位改为1。因此,在更新索引条目的过程中,会锁定位图索引里多个索引条目。也就是同时只能有一个用户能够更新表T,从而降低了并发性。
位图索引比较适合用在数据仓库系统里,不适合用在OLTP系统里。

[php]
SQL>> SQL> select * from t_bitmap_test;

        ID     BITCOL
---------- ----------
         1         3
         2         2
         3         1
         4         3
         5         3
         6         1
         7         1
         8         2
         9         3
        10         2
        11         3
        12         1
        13         1
        14         3
        15         2
        16         2
        17         3
        18         2
        19         1
        20         3

SQL>>
OBJECT_ID
----------
     24499

SQL>> qerbiIPI length=0
qerbiAllocate> qerbiStart first start
qerbiRop:> qerbiCmpSz notfound pctfree=10
qerbiCmpSz> qerbiRop keysize=4 maxbm=3531
kdibcoinit(3116714):> qerbiRop: rid=00c01ce4.0001, new=Y , key: (2):  c1 03
kdibcoinit(3116698):> qerbiRop: rid=00c01ce4.0002, new=Y , key: (2):  c1 02
kdibcoinit(311661c):> qerbiRop: rid=00c01ce4.0003, new=N, key: (2):  c1 04
qerbiRop:> qerbiRop: rid=00c01ce4.0005, new=N, key: (2):  c1 02
qerbiRop:> qerbiRop: rid=00c01ce4.0007, new=N, key: (2):  c1 03
qerbiRop:> qerbiRop: rid=00c01ce4.0009, new=N, key: (2):  c1 03
qerbiRop:> qerbiRop: rid=00c01ce4.000b, new=N, key: (2):  c1 02
qerbiRop:> qerbiRop: rid=00c01ce4.000d, new=N, key: (2):  c1 04
qerbiRop:> qerbiRop: rid=00c01ce4.000f, new=N, key: (2):  c1 03
qerbiRop:> qerbiRop: rid=00c01ce4.0011, new=N, key: (2):  c1 03
qerbiRop:> qerbiRop: rid=00c01ce4.0013, new=N, key: (2):  c1 04
kdibcoend(3116714):> qerbiCon: key: (2):  c1 04
srid=00c01ce4.0> kdibcoend(3116698):> qerbiCon:> kdibcoend(311661c):> qerbiCon:>
这一段是创建bitmap索引的过程。我们先把被索引的列的值换算成十六进制:
[php]
SQL>>
_______________
[/php]

4、3、2对应的十六进制则是04、03、02。也就是上面转储部分显示的key部分的键值。
可以看到,oracle在创建bitmap索引时,先从第一条记录开始扫描,取出第一条记录的键值(bitcol=3),也就是“qerbiRop:rid=00c01ce4.0000,> 接下来扫描到的记录所对应的bitcol的值都是1、2、3中的一个,因此都不会产生新的索引条目,因此它们的new都为N。

然后扫描完表里的所有记录以后,开始创建bitmap索引条目,也就是下面的部分:
qerbiCon: key: (2):  c1 04
srid=00c01ce4.0> kdibcoend(3116698):> qerbiCon:> kdibcoend(311661c):> qerbiCon:>
这里的srid表示start> [php]
SQL>> 在二进制里,每个应该倒过来,从右到左排列,因此为:
[php]
C3         C2       C1
00001001   00100101   00011001

_______________
[/php]

然后将它们组织为一个由多个bit位组成的bitmap的话,仍然从右到左,依次取出每个bit位,于是我们有:100110001010010010010000。我们可以把这个bit串与理论上的bitmap比较一下:
100110001010010010010000
10011000101001001001
很明显,除了最后多出来的4个0以外,其余部分完全一致。而最后多出的0并不影响这个索引条目的使用。

类似的,我们可以使用相同的方法来依次验证另外两个键值,都是符合理论值的。
数据都在一个数据块里的情况比较容易理解。如果被索引的数据分布在多个数据块里呢?

经常会有人问到,只记录start> 创建一个表:
[php]
SQL>> ……
qerbiCon:> *** 2008-06-10 11:21:08.000
很明显,这里的两个索引条目的start> 其数据块从1d04到1d08,也就是:
[php]
SQL>> 其数据块从1d04到1d09,也就是:
[php]
SQL> select sys.pkg_number_trans.f_hex_to_dec('1d04') as s_blk#,
  2  sys.pkg_number_trans.f_hex_to_dec('1d09') as e_blk#
  3  from dual;

S_BLK#     E_BLK#
---------- ----------
7428       7433

_______________
[/php]

这个时候,
[php]
SQL> select substr(bitcol,1,1) asbitcol,dbms_rowid.rowid_block_number(rowid) as block# from t_bitmap_2;

BI     BLOCK#
-- ----------
B        7428
A        7428
A        7428
A        7429
B        7429
B        7429
B        7430
B        7430
B        7430
A        7431
A        7431
A        7431
B        7432
A        7432
A        7432
B        7433

_______________
[/php]

这时,oracle放了很多的bit来对应这15条记录,但是oracle如何根据这些bit位来找对应的rowid就猜不出了。还希望各位牛人继续补充。


而键值B的索引条目为:
srid=00c01d04.0> 其数据块从1d04到1d09,也就是:
[php]
SQL> select sys.pkg_number_trans.f_hex_to_dec('1d04')as s_blk#,
  2  sys.pkg_number_trans.f_hex_to_dec('1d09')as e_blk#
  3  from dual;

S_BLK#     E_BLK#
---------- ----------
7428       7433

_______________
[/php]

这个时候,
[php]
SQL> select substr(bitcol,1,1) asbitcol,dbms_rowid.rowid_block_number(rowid) as block# from t_bitmap_2;

BI     BLOCK#
-- ----------
B        7428
A        7428
A        7428
A        7429
B        7429
B        7429
B        7430
B        7430
B        7430
A        7431
A        7431
A        7431
B        7432
A        7432
A        7432
B        7433

_______________
[/php]

这时,oracle放了很多的bit来对应这15条记录,但是oracle如何根据这些bit位来找对应的rowid就猜不出了。还希望各位牛人继续补充。



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值