在B树索引中,索引键值与行之间存在一种一对一的关系,一个索引键值引向一行,而在位图索引中,一个索引键值则对应多行,位图索引通常适用于高度重复(相对于很多的行数,列值可能只有几个,列值/行数越接近0则越适合使用位图索引)而且经常只读的列,通常查询这种列返回的数据占比很大,因此也不适合使用B树索引。对比来看,B树索引通常是选择性的,位图索引位通常不是选择性的。位图索引的键值使用0,1存储,相较B树索引节省很大的空间另外位图索引可以存储NULL值。
使用场景
- 一个查询条件包含多个列,并且要创建索引的列只有几个不同的值及大量的聚合统计查询where条件中使用and/or/in
如截图中的查询例子。如果建立B树索引,为了高效的满足查询要求,就要建立2或者更多的索引组合来实现,这将会占用大量的数据库空间如果后期条件有调整维护起来也比较麻烦。
如果建立位图索引,oracle会对3个索引的位图使用and、or或not得到合并后的位图,如果有必要可以将位图中的‘1’转换成rowid来访问数据,如果是计数则直接统计1的个数。
如下例子:
create table t4
(
a not null,
b not null,
c not null
)
as
select
decode(ceil(dbms_random.value(1,2)),1,'M',2,'F'),
ceil(dbms_random.value(1,50)),
decode
(
ceil(dbms_random.value(1,5)),
1,'18 and under',
2,'19-25',
3,'26-30',
4,'31-40',
5,'41 and over'
)
from dual connect by level<=100000;
create bitmap index t4_1 on t4(a);
create bitmap index t4_2 on t4(b);
create bitmap index t4_3 on t4(c);
begin
dbms_stats.gather_table_stats
('scott','t4');
end;
总结
1. 位图索引使用于低基数的列(比如说性别列,数据仓库中的维表的主键),相对于B树索引,它的count,and,or操作更有效
2. 位图索引存放的是0,1的比特位,相对于B树索引,占字节数特别少
使用位图索引要特别注意
1. 列的基数比较多,不适合位图索引,因为它会占用更多的存储空间
2.索引列DML频繁的列,不适合位图索引,容易造成死锁,原因是一个位图索引键值指向多行,如果一个会话修改了一行数据,大多数情况下这个键值所对应的所有行都会被锁定。大大影响到系统并发性。数据仓库项目中对于位图索引的维护一般建议先删掉索引加载完完数据后再建立索引
3.关于列偏态或称列倾斜、倾斜列对使用索引的影响,这种列的特点是数据大多集中在某几个值。这种情况下一般会影响索引的使用,通常情况下需要收集表的直方图信息来使优化器决定是否使用索引。
如下例子:
create table t8
(a not null,b not null,c not null)
as
select
'M',
ceil(dbms_random.value(1,50)),
decode
(
ceil(dbms_random.value(1,5)),
1,'18 and under',
2,'19-25',
3,'26-30',
4,'31-40',
5,'41 and over'
)
from dual connect by level<=100000;
insert into t8
select
'F',
ceil(dbms_random.value(1,50)),
decode
(
ceil(dbms_random.value(1,5)),
1,'18 and under',
2,'19-25',
3,'26-30',
4,'31-40',
5,'41 and over'
)
from dual connect by level<=10;
create bitmap index t8_1 on t8(a);
create bitmap index t8_2 on t8(b);
create bitmap index t8_3 on t8(c);
begin
dbms_stats.gather_table_stats
('scott','t8');
end;
create table t7
(
a not null,
b not null,
c not null
)
as
select
'M',
ceil(dbms_random.value(1,50)),
decode
(
ceil(dbms_random.value(1,5)),
1,'18 and under',
2,'19-25',
3,'26-30',
4,'31-40',
5,'41 and over'
)
from dual connect by level<=100000;
insert into t7
select
'F',
ceil(dbms_random.value(1,50)),
decode
(
ceil(dbms_random.value(1,5)),
1,'18 and under',
2,'19-25',
3,'26-30',
4,'31-40',
5,'41 and over'
)
from dual connect by level<=10;
create bitmap index t7_1 on t7(a);
create bitmap index t7_2 on t7(b);
create bitmap index t7_3 on t7(c);
begin
dbms_stats.gather_table_stats
('scott','t7',METHOD_OPT=>’for all columns size skewonly’);
end;
建立t8/t7,其中M对应10w条数据,f对应10条数据,这个一个典型的偏态列,区别是对t7收集了其直方图信息。
执行相同的查询,在查询t8表时无论查‘M’还是‘F’两个执行计划都使用了全表扫描,而且优化器认为两个值的基数一致都是5w左右。
在查询t7表时,可以发现执行计划明显不同,而且优化器识别出了两个值的基数不同,接近真实值。