一:什么是位图索引?
位图索引是从oracle 7.3版本开始引入的。目前oracle企业版和个人版都支持位图索引,但是标准版不支持。位图索引是这样一种结构,其中用一个索引键条目存储指向多行的指针,这与B*树结构不同,在b*树结构中,索引键和表中的行存在着对应关系。在位图索引中,可能只有很少的索引条目,每个索引条目指向多行,而在传统的B*树中,一个索引条目就指向一行那么什么是位图索引呢?(借用网络的例子讲解)
有张表名为table的表,由三列组成,分别是姓名、性别和婚姻状况,其中性别只有男和女两项,婚姻状况由已婚、未婚、离婚这三项,该表共有100w个记录。现在有这样的查询:select * from table where Gender=‘男’ and Marital=“未婚”;
![](http://img.blog.itpub.net/blog/attachment/201801/18/29654823_1516261961ZQkr.png?x-oss-process=style/bb)
1)不使用索引
不使用索引时,数据库只能一行行扫描所有记录,然后判断该记录是否满足查询条件。
2)B树索引
对于性别,可取值的范围只有'男','女',并且男和女可能各站该表的50%的数据,这时添加B树索引还是需要取出一半的数据, 因此完全没有必要。相反,如果某个字段的取值范围很广,几乎没有重复,比如身份证号,此时使用B树索引较为合适。事实上,当取出的行数据占用表中大部分(超过20%)的数据时,即使添加了B树索引,数据库如oracle、mysql也不会使用B树索引,很有可能还是一行行全部扫描。
接下来说下位图索引原理:
如果在性别列上建立了位图索引,对于性别这个列,针对每行的rowid(rowid可以理解为每行的物理位置),位图索引形成两个向量,男向量为10100...,向量的每一位表示该行是否是男,如果是则位1,否为0,同理,女向量位01011,(可以理解为给每行数据的性别列中为产生两个向量分别为男向量和女向量:男向量中 值为男:用1表示,值不是男用0表示,同理女向量中 值为女:用1表示,值不是女:用0表示)
![](http://img.blog.itpub.net/blog/attachment/201801/18/29654823_1516261973WbBP.png?x-oss-process=style/bb)
如果在婚姻状况列上建立了位图索引 对于婚姻状况这一列,位图索引生成三个向量,已婚为11000...,未婚为00100...,离婚为00010...。
![](http://img.blog.itpub.net/blog/attachment/201801/18/29654823_1516261984r8h8.png?x-oss-process=style/bb)
二:oracle 位图索引检索数据的过程:
当我们使用查询语句“select * from table where Gender=‘男’ and Marital=“未婚”;”的时候 首先取出男向量10100...,然后取出未婚向量00100...,将两个向量做and操作,这时生成新向量00100...,可以发现rowid=3的and之后的结果为1,表示该表的rowid=3的这行数据就是我们需要查询的结果(如下“and的结果”为1的就是需要查询的结果),然后根据rowid找到需要的数据
![](http://img.blog.itpub.net/blog/attachment/201801/18/29654823_151626199577Vl.png?x-oss-process=style/bb)
三:什么情况下应该使用位图索引?
位图索引适合只有几个固定值的列,如性别、婚姻状况、行政区等等,而身份证号这种类型不适合用位图索引,如果用户查询的列的相异基数非常的小, 要为这些相异基数值比较小的列建索引,就需要建立位图索引。
那么何谓相异基数非常的小?可以认为行集中不同项的个数除以行数应该是一个很小的数(接近0),例如,某个列(性别)可能取值为M、F、null.如果一个表中有20000条数据,那么3/20000=0.00015,那么这就算是个相异基数很小的情况,类似的,如果有100000个不同的值,与10000000条结果相比,比值是0.01,同样也很小,也可以认为是相异基数很小的情况,都可以建立位图索引;
四:位图索引的限制或者说是弊端?
位图索引在读密集的环境中能很好地工作,但是对于写密集的环境则极不适合,原因在于,一个位图索引键条目(可以理解为前面的男 、女、未婚、已婚等)指向多行。如果一个会话修改了有索引的列的数据,那么大多数情况下,这个索引条目只想的所有行都会被锁定。oracle无法锁定一个位图索引条目中的单独一位,而是会锁定整个位图索引条目,倘若其他会话修改也需要更新同样的这个位图索引条目,就会被“关在门外”,这样就大大影响了并发性,因为每个更新都有可能锁定数百行,不允许并发地更新他们的位图列;
举个例子说明:有这样一个字段job,记录各个员工的职位如:dba 、java、php等等 ,假设我们在这个job列上建立了位图索引。假如rowid=100的员工职业为php,rowid=120的员工职业为php;
如果会话1使用update更新某个员工的职位(job),比如update table set table.job='dba' where rowid=100;,但还没有commit,而会话2也使用update更新另一个员工的职位,update table set table.job='dba' where rowid=120; 这个时候会话2怎么也更新不了,需要等待会话1 commit。
原因:会话1更新rowid=100的这个员工的职位,假如这个员工原来是php,现在改成dba,那么在commit之前,就会锁定所有job=php和job=dba的所有行,所以当会话2尝试更新job=dba只能等待锁,只有commit之后才解锁。这样就大大影响了并发性;
总结:
位图索引是为数据仓库(也就是查询环境设计的),位图索引特别不适合OLTP系统,位图索引不适合与dml频繁的环境,位图索引适用于DSS系统,位图索引不适合频繁修改的系统,弊端是严重影响
并发性,因为update索引列值的时候,会锁定新值和旧值指向的所有数据行,所以使用位图索引需慎重。