一. 简介
位图索引(Bitmap Index) 是 Oracle 数据库中一种特殊类型的索引,适用于低基数(Low Cardinality)列,即那些列中可选值相对较少的情况下使用。它与常规的 B-tree 索引不同,位图索引通过位图(bitmap)来表示列的不同取值及其在表中的位置,从而有效加速特定类型的查询。但位图索引有很大的弊端,使用不当会造成大量的等待事件,甚至造成系统崩溃,位图索引需要谨慎使用。
二. 实验
create table bm_table as select * from dba_objects;
insert into bm_table select * from bm_table;
/
/
/
/
/
update bm_table set object_id = rownum;
复制
此时表内记录约 230 万。
未建立索引的执行计划:
set autotrace traceonly
set linesize 200
select count(*) from bm_table;
复制
建立普通索引的执行计划
create index idx1 on bm_table(object_id);
复制
此时仍走全表扫描,原因是索引无法存储空行,CBO 不会为整行都为空行的表走索引。
设置 object_id 列为 not null,让语句走索引。
alter table bm_table modify object_id not null;
select count(*) from bm_table;
复制
意料之中,走索引快速全扫描,一次性读取多个数据块,速度要快于 INDEX FULL SCAN。
建立位图索引的执行计划
create bitmap index index2 on bm_table(status);
复制
根据位图索引的特性,这个索引我们建在 status 列上,因为 status 列 可选值相对较少。
select count(*) from bm_table;
复制
执行结果比较
Cost | 逻辑读 | |
未建立索引 | 12146 | 44790 |
普通索引 | 1461 | 5453 |
位图索引 | 52 | 144 |
为什么位图索引这么快?
这是由位图索引的原理决定的
如果某个列有三个可能的值(例如,性别列有 "M"、"F"、"U"),那么 Oracle 会为每个可能的值创建一个位图,如下所示:
值为'M' 的位图: 101001
值为'F' 的位图: 010100
值为'U' 的位图: 000010
位图索引以二进制的形式存储,性能极高。
三. 位图索引的弊端
insert into bm_table(OBJECT_ID,STATUS) values(195555,'invalid');
select distinct status from bm_table;
复制
此时模拟 DML 语句。
当前会话 id:
select sid from v$mystat where rownum=1;
复制
另开一个会话 2,此时的会话 id:
会话 1 执行 DML 操作:
insert into bm_table(OBJECT_ID,STATUS) values(195555,'invalid');
复制
会话 2 执行 DML 操作:
insert into bm_table(OBJECT_ID,STATUS) values(195555,'invalid');
复制
发现 sql 被锁,一直在等待。
中止 sql 执行,换一条 sql 执行。
insert into bm_table(OBJECT_ID,STATUS) values(195555,'valid');
复制
sql 反而又执行成功。
如果不更新 status 列呢?
insert into bm_table(OBJECT_ID) values(195555);
复制
也会更新成功。
四. 总结
在会话一中,我们做了插入 status 列 为 invalid, 在会话二中,我们做了插入 status 列为 invalid 造成 sql 等待,又做了插入 status 列 为 valid 反而插入成功,继续做插入不涉及 status 列 也可以更新成功。
结合位图索引结构与存储方式,发现