微信公众号:DBA之友
当业务方给你扔来一堆天马行空的“即席查询”需求时,DBA 的内心是不是总会咯噔一下?
“即席查询” ——这种以随机、多条件组合为特点的查询,堪称数据库的性能杀手。比如,数据分析师可能上一秒想看“华东地区、30-40岁、男性”的用户画像,下一秒又要查“西南地区、所有年龄段、女性”的订单数据。
面对这种场景,我们惯用的 B-Tree 索引常常会显得力不从心。今天,我们就通过一个实战 case,来看看在这种特殊战场上,谁才是真正的性能主宰。
听着有点抽象?别急,我们用实验说话。
我们先创建一个 10 万行数据的用户表
T
,包含
gender
(性别)、
location
(地区)、
age_group
(年龄段)等字段。
gender
:只有 'M', 'F' 两个值,是典型的
低基数字段
。
location
:有 50 个地区,属于
中等基数字段
。
age_group
:有 4 个年龄段,也是
低基数字段
。
-- 创建测试表并插入10万条随机数据
create table t
(
name_id,
gender not null, -- 性别
location not null, -- 地区
age_group not null, -- 年龄段
data
)
as
select
rownum,
decode(ceil(dbms_random.value(0,2)), 1,'M', 2,'F') gender,
ceil(dbms_random.value(1,50)) location,
decode(ceil(dbms_random.value(0,4)), 1,'child', 2,'young', 3,'middle_age', 4,'old') age_group,
rpad('*',400,'*')
from dual connect by rownum <= 100000;
我们的目标是测试下面这条典型的即席查询的性能:
select *
from t
where gender='M'
and location in (1,10,30)
and age_group='child';
在没有任何索引的情况下,Oracle 只能选择最笨的办法——全表扫描(Full Table Scan)。
执行计划与统计信息:
点评: 意料之中的糟糕表现。为了找出几百条数据,把整张表从头到尾读了一遍,IO 代价巨大。
大多数 DBA 的第一反应可能是创建一个复合索引。毕竟,查询条件都包含在内了。
-- 创建 B-Tree 复合索引
create index idx_union on t(gender,location,age_group);
执行计划与统计信息:

点评: 性能有了显著提升!逻辑读大幅下降。但它并非完美:
回表代价:
索引扫描后,仍然需要根据
ROWID
回到表中去捞取
select *
的其他数据,这会产生大量的随机 I/O。
索引效率:
对于
gender
这样的低基数列,B-Tree 索引的选择性很差,索引本身也会比较臃肿。
现在,让我们请出今天的主角——位图索引。我们为每个查询字段单独创建一个位图索引。
-- 创建三个独立的位图索引
create bitmap index gender_idx on t(gender);
create bitmap index location_idx on t(location);
create bitmap index age_group_idx on t(age_group);
执行计划与统计信息:

逻辑读 与 B-Tree 索引相当,但 成本(Cost) 降低了超过 70%!
核心优势
体现在执行计划中:Oracle 首先通过
BITMAP INDEX SINGLE VALUE
快速定位到每个条件对应的位图,然后使用
BITMAP AND
和
BITMAP OR
操作,在内存中直接对这些位图进行高效的逻辑运算(与、或),瞬间筛选出同时满足所有条件的
ROWID
集合。最后再通过
BITMAP CONVERSION TO ROWIDS
一次性回表,效率极高。
在处理
数据仓库、BI报表、用户画像分析
等涉及大量
即席查询
和
低基数列
的场景时,
位图索引
无疑是你的首选性能优化利器。它的核心竞争力,就是将复杂的 SQL
AND/OR
条件,转换成了计算机最擅长的、近乎零成本的位运算。
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/70013625/viewspace-3096462/ ,如需转载,请注明出处,否则将追究法律责任。