关于ORACLE位图索引内部浅论

我们都知道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. 

时间: 2024-09-20 22:27:21

关于ORACLE位图索引内部浅论的相关文章

如何提高Oracle位图索引的使用效果

位图索引是Oralce数据库索引中的异类,其在某些比较特殊的场合中有突出的表现.一般来说,位图索引的效果直接跟列的基 数相关.为此在谈到如何提高位图索引的使用效果时,也往往跟这个列的基数相关.为此必须对这个列的基数有一个清晰的认识. 一.什么时候改采用位图索引 基数是指某个列可能拥有的不重复数值的个数.比如说在一个中华人民共和国公民的信息表中,包含着十几亿条的记录.但是在这些记录中 ,有几个比较特殊的列,其指包含几个有限的重复数值.如性别这一字段,其就只有男与女两个可能值;如在民族一列内也只有5

关于ORACLE组合索引内部存储浅谈

本文任何观点为作者观点,水平有限难免有误    关于组合索引不需要多谈就是多个列一起建立的索引,关于组合索引很常见的一个问题就是当谓词中出现了前导列才能够使用索引,如果 没有出现前导列是不能使用索引,当然index skip scan和index full scan除外.    理论如此,但是为什么谓词中没有前导列就不能使用索引,接下来通过DUMP来看看组合索引如何存放数据 建立测试表,为了方便论述和区别这里使用全数字同时组合索引的两个列完全是反序的: create table testt1 (

Oracle 重建索引的必要性

      索引重建是一个争论不休被不断热烈讨论的议题.当然Oracle官方也有自己的观点,我们很多DBA也是遵循这一准则来重建索引,那就是Oracle建议对于索引深度超过4级以及已删除的索引条目至少占有现有索引条目总数的20% 这2种情形下需要重建索引.近来Oracle也提出了一些与之相反的观点,就是强烈建议不要定期重建索引.本文是参考了1525787.1并进行相应描述.   1.重建索引的理由    a.Oracle的B树索引随着时间的推移变得不平衡(误解)    b.索引碎片在不断增加  

[20150204]关于位图索引3.txt

[20150204]关于位图索引3.txt --许多人知道在oltp系统不适合使用位图索引.它的索引的记录结构如下是: 字段0:键值 字段1:开始rowid 字段2:结束rowid 字段3:位图信息,指示那行记录,位图1=>表示存在.位图0=>表示不存在. --但是字段4的位图信息,介绍的资料太少,我自己看了链接http://juliandyke.com/Presentations/BitmapIndexInternals.ppt,做一个简单探究. --昨天讲了Single-Byte Grou

位图索引:原理(BitMap index)

位图(BitMap)索引 前段时间听同事分享,偶尔讲起Oracle数据库的位图索引,顿时大感兴趣.说来惭愧,在这之前对位图索引一无所知,因此趁此机会写篇博文介绍下位图索引. 1. 案例 有张表名为table的表,由三列组成,分别是姓名.性别和婚姻状况,其中性别只有男和女两项,婚姻状况由已婚.未婚.离婚这三项,该表共有100w个记录.现在有这样的查询:     select * from table where Gender='男' and Marital="未婚"; 姓名(Name)

Oracle 9i索引

  索引是若干数据行的关键字的列表,查询数据时,通过索引中的关键字可以快速定位到要访问的记录所在的数据块,从而大大减少读取数据块的I/O次数,因此可以显著提高性能. 索引的原理     下面通过查询数据表"scott.student"的ROWID列为例,在[SQLPlus Worksheet]中执行下面的语句,查询结果如图7.38所示.    ―――――――――――――――――――――――――――――――――――――    select rowid,student_id,name,pr

Oracle B-Tree索引与Bitmap索引的锁代价的比较

环境: SQL> select * from v$version; BANNER -------------------------------------------------------------------------------- Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - Production PL/SQL Release 11.2.0.1.0 - Production CORE  11.2.0.1.0  

Oracle管理索引(三)Oracle建立索引

1.建立b-tree索引 (1)相关概念 根块(一个):索引顶级块,它包含指向下一级节点(分支块或叶块)的信息. 分支块:它包含指向下一级的节点(分支块或叶块)的信息. 叶块:它包含索引入口数据,索引入口包含索引列值或受限ROWID (2)建立索引 如果在where子句中要经常引用某列或某几列,应该给予这些列值建立B-*树索引 10:23:58 SQL> create index ind_ename on scott.emp(ename) pctfree 30 10:24:32   2  tab

Oracle管理索引(二)Oracle管理索引基本概念

1.索引的概念 索引是用于加速数据存取的数据库对象.合理地使用索引可以大大降低I/O次数. 2.索引的功能:对记录进行排序,加快表的查询速度. 3.索引的分类: (1)单列索引和复合索引 单列索引是基于单个列所建立的索引:复合索引时基于两列或多列所建立的索引. SQL>CREATE    INDEX    emp_idx1   on  emp( ename, job); SQL>CREATE    INDEX    emp_idx2   on   emp (job  , ename); 可以在