비트맵 인덱스

  • 비트맵 인덱스는 오라클 7.3 버전에서 추가되었다.
  • Enterprise 및 Personal Edition에서는 사용 가능하지만 Standard Edition에서는 사용할 수 없다.
  • 비트맵 인덱스는 시스템 구축 시에는 요청될 쿼리를모두 알 수 없는 데이터 웨어하우스 또는 ad hoc 쿼리 환경(쿼리가 재사용되지않고 CPU 높이거나 성능저하를 요하는것)에 적합하도록 설계되었다.
  • OLTP (on-line transaction processing 데이터베이스의 데이터를 수시로 갱신하는 프로세싱을 의미)또는 동시에 여러 세션에 의해 빈번하게 UPDATE되는 환경에는 적합하지 않다.
  • 비트맵 인덱스는 하나의 인텍스 키 엔트리에 많은 로우에 대한 포인터를 저장하고 있는 구조로, 인텍스키와 테이블 로우가 한 쌍을 이루는 B*Tree 인텍스와는 다르다.
  • 비트맵 인덱스는 매우 적은 수의 인덱스 엔트리를 가지게 되고,각 엔트리는 많은 수의 로우를 가리키지만, 일반적인 B*Tree 인덱스는 하나의 인덱스 엔트리가 하나의 로우를 가리키고 있다


비트맵 인덱스 생성

create BITMAP index job_idx on emp(job);



  • 표 11 \-6에서 로우8,10,13은 ANALYST고, 로우4,6,7은 MANAGER고 NULL 인 로우는 없다(비트맵 인덱스는 null 엔트리도 저장한다 비트랩 인텍스에 null 엔트리가 없다는 것은 null 인 로우가 없다는 것을 의미한다).
  • 만약 MANAGER 값을 가진 로우의 수를 찾는다면 비트맵 인덱스를 이용하여 빠르게 셀 수있다 JOB 이 CLERK이거나 MANAGER 인 로우를 찾고자 한다면 표 11-7에서 보여주듯이 인덱스의 비트맵을 조합하여 쉽게 찾을수 있다.





select count(*) from emp where job = 'CLERK' or job = 'MANGER';

Plan hash value: 3446200557

-----------------------------------------------------------------------------------------
| Id  | Operation                     | Name    | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT              |         |     1 |     8 |     1   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE               |         |     1 |     8 |            |          |
|   2 |   BITMAP CONVERSION COUNT     |         |     6 |    48 |     1   (0)| 00:00:01 |
|*  3 |    BITMAP INDEX FAST FULL SCAN| JOB_IDX |       |       |            |          |
-----------------------------------------------------------------------------------------


  • 비트맵 인덱스로부터 직접 결과를 추출할 수 있다.
select * from emp where job = 'CLERK' or job = 'MANGER'
Plan hash value: 3956160932

--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |     6 |   228 |     3   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| EMP  |     6 |   228 |     3   (0)| 00:00:01 |
--------------------------------------------------------------------------


  • 위 쿼리는 테이블에 액세스할 필요가 있다.
  • 오라클은 비트맵의 1번째 비트를 실제 테이블의 rowid로 바꾸는 함수 를 적용해서 테이블에 액세스할 수 있도록 한다.


언제 비트맵 인덱스를 사용해야 하는가?

  • 비트맵 인덱스는 낮은 카디널리티 (low distind cardinality , 적은수의 구별된 카디널리티 ex) 색상 , 성별) 데이터에 적당하다.
  • 낮은카디널리티는 결과 집합의 크기에 상대적이다. ex) 수억개중 만개는 낮은 카디널티가 될수있다.
  • 컬럼의 갯수에 비례하여 작은값의 경우에 사용된다.
  • 여러 컬럼을 ad hoc 방식으로참조하거나 COUNT와 같은 집계 처리를 해야 하는 경우


  • 예를 들어 GENDER, LOCATION, 그리고 AGE_GROUP 3개 컬럼을 가진 대형 테이블이 있다고 하자.
  • 이 테이블에서 GENDER는 M과 F,LOCATION은 1부터 50까지의 값 AGE_GROUP은 18 and under, 19-25, 26-30, 31-40,41 and over 값으로 되어있다.
 Select count(*)
 from T
 where gender = 'M'
 and location in ( 1, 10, 30 )
 and age_group = '41 and over';

select *
from t
where ( (gender = 'M ' and location = 20 )
or ( gender = 'F' and location = 22 ))
and age_group = '18 and under';

select count(*) from t where location in (11 ,20,30);

select count(*) from t where age_group = '41 and over' and gender = 'F';


B*Tree 인텍스 구성
  • GENDER, LOCATION, AGE_GROUP: 3 개 컬럼이 모두 사용되기 때문에 GENDER와 LOCATION 결합 인덱스 또는 GENDER 단독 인덱스
  • LOCATION, AGE_GROUP: LOCATION과 AGE_GROUP 결합 인덱스 또는 LOCATION 단독인덱스
  • AGE_GROUP, GENDER: AGE_GROUP와 GENDER 또는 AGE_GROUP 단독 인덱스


BITMAP 인덱스 구성

drop table t ;

create table t
(gender not null,
location not null,
age_group not null,
data
)
as

select decode( round(dbms_random.value(1,2)) ,
                1, 'M' ,
                2, 'F' ) gender,
 round(dbms_random.value(1 ,50)) location,
 decode( round(dbms_random.value(1, 5)),
         1, '18 and under' ,
         2, '19-25' ,
         3, '26-30' ,
         4, '31-40' ,
         5, '41 and over') ,
 rpad( '*' ,20,'*')
 from dual connect by level <= 100000 ;



 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);


 
exec dbms_stats.gather_table_stats( user, 'T' )

 Select count(*)
 from T
 where gender = 'M'
 and location in ( 1, 10, 30 )
 and age_group = '41 and over';


Plan hash value: 2919078855

-----------------------------------------------------------------------------------------------
| Id  | Operation                     | Name          | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT              |               |     1 |    13 |     7   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE               |               |     1 |    13 |            |          |
|   2 |   BITMAP CONVERSION COUNT     |               |   308 |  4004 |     7   (0)| 00:00:01 |
|   3 |    BITMAP AND                 |               |       |       |            |          |
|*  4 |     BITMAP INDEX SINGLE VALUE | AGE_GROUP_IDX |       |       |            |          |
|   5 |     BITMAP OR                 |               |       |       |            |          |
|*  6 |      BITMAP INDEX SINGLE VALUE| LOCATION_IDX  |       |       |            |          |
|*  7 |      BITMAP INDEX SINGLE VALUE| LOCATION_IDX  |       |       |            |          |
|*  8 |      BITMAP INDEX SINGLE VALUE| LOCATION_IDX  |       |       |            |          |
|*  9 |     BITMAP INDEX SINGLE VALUE | GENDER_IDX    |       |       |            |          |
-----------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   4 - access("AGE_GROUP"='41 and over')
   6 - access("LOCATION"=1)
   7 - access("LOCATION"=10)
   8 - access("LOCATION"=30)
   9 - access("GENDER"='M')


  • 이 예제는 비트맵 인덱스의 강력한 장점을 보여주고 있다 location이 1,10,30 인 3개의 인덱스를 각각 읽어서 비트맵으로 '비트' 의 OR 연산을 할 것 이다. 그리고 AGE_GROUP='41 AND OVER'인 비트맵과 GENDER='M'인 비트맵과 AND 연산을 수행한다.

select *
from t
where ( (gender = 'M' and location = 20 )
   or ( gender = 'F' and location = 22 ))
and age_group = '18 and under';


Plan hash value: 1064409070

------------------------------------------------------------------------------------------------
| Id  | Operation                      | Name          | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT               |               |   276 |  9384 |    52   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID   | T             |   276 |  9384 |    52   (0)| 00:00:01 |
|   2 |   BITMAP CONVERSION TO ROWIDS  |               |       |       |            |          |
|   3 |    BITMAP AND                  |               |       |       |            |          |
|*  4 |     BITMAP INDEX SINGLE VALUE  | AGE_GROUP_IDX |       |       |            |          |
|   5 |     BITMAP OR                  |               |       |       |            |          |
|   6 |      BITMAP AND                |               |       |       |            |          |
|*  7 |       BITMAP INDEX SINGLE VALUE| LOCATION_IDX  |       |       |            |          |
|*  8 |       BITMAP INDEX SINGLE VALUE| GENDER_IDX    |       |       |            |          |
|   9 |      BITMAP AND                |               |       |       |            |          |
|* 10 |       BITMAP INDEX SINGLE VALUE| LOCATION_IDX  |       |       |            |          |
|* 11 |       BITMAP INDEX SINGLE VALUE| GENDER_IDX    |       |       |            |          |
------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   4 - access("AGE_GROUP"='18 and under')
   7 - access("LOCATION"=22)
   8 - access("GENDER"='F')
  10 - access("LOCATION"=20)
  11 - access("GENDER"='M')


  • 이 경우도 비슷한 로직으로 처리한다 OR 조건은 AND 조건에 맞는 비트맵을 찾고,각각의 AND 결과를 OR 조건으로 연산을 수행한다. 그리고 AGE_GROUP='18 AND UNDER' AND 를 만족하는 조건인 비트맵을 찾고, 최종 결과를 산출하게 된다.


단점
  • 비트맵 인덱스는 읽기 중심의 환경에서는 적합한 인덱스이나 쓰기를 많이 하는 환경에서는 결코 적절하지 않다.
  • 하나의 비트맵 인덱스 키가 많은 로우를 가리키기때문에, 하나의 세션에서 인텍스 엔트리를 수정하게 되면 그 인텍스가 포인터하고 있는 모든 로우를 락킹하기 때문이다.
  • 오라클은 비트맵 인덱스에서 수정되는 한 비트만 락을 걸지 않고, 비트맵 인덱스 엔트리 전체를 락킹하기 때문에 같은 비트맵 인덱스 엔트리를 수정 하는 모든 작업은 이전의 락킹이 풀릴때까지 기다려야 한다.


비트맵 조인 인덱스

  • 일반적으로 인텍스는 하나의 테이블에 속한 컬럼을 대상으로 생성되는 반면 비트맵 조인 인텍스는 이러한 규칙을 깨고 다른 테이블의 컬럼을 이용한 인텍성이 가능하도록 한다.
-- 일반적인 쿼리
select count(*)
from emp, dept
where emp.deptno = dept.deptno
and dept.dname = 'SALES'
/
select emp.*
from emp, dept
where emp.deptno = dept.deptno
and dept.dname = 'SALES'
/


create bitmap index emp_bm_idx
 on emp( d.dname )
 from emp e, dept d
 where e.deptno = d.deptno

begin
 dbms_stats.set_table_stats( user, 'EMP' , numrows => 1000000, numblks => 300000 ) ;
 dbms_stats.set_table_stats( user, 'DEPT' , numrows => 100000, numblks => 30000 );
 dbms_stats.delete_index_stats( user, 'EMP_BM_IDX');
end;



select count (*)
  from emp, dept
 where emp.deptno = dept.deptno
  and dept.dname = 'SALES'

------------------------------------------------------------------------------------------
| Id  | Operation                   | Name       | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |            |     1 |     3 |     1   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE             |            |     1 |     3 |            |          |
|   2 |   BITMAP CONVERSION COUNT   |            |   333K|   976K|     1   (0)| 00:00:01 |
|*  3 |    BITMAP INDEX SINGLE VALUE| EMP_BM_IDX |       |       |            |          |
------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   3 - access("EMP"."SYS_NC00010$"='SALES')


  • 위의 예제를 보면, 결과를 생성하기 위해 EMP 테이블이나 DEPT 테이블에 실제로 액세스할 필요가 없다.
  • 최종 결과는 인덱스 자체만으로 생성된다.SQL의 결과를 만드는 데 필요한 모든 정보는 인덱스구조에 모두 포함되어 있기 때문이다.
  • 비트맵 조인 인덱스는 전제 조건이 있다. 조인 조건은다른테이블의 기본 키 또는유니크 키 조인을 포함해야 한다는 것이다.

-- 아래 예제는 bitmap index를 타야하는데 해쉬조인로 되버림 
select emp.*
  from emp, dept
 where emp.deptno = dept.deptno
  and dept.dname = 'SALES'


---------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |  6250M|   296G|   133K (34)| 00:26:43 |
|*  1 |  HASH JOIN         |      |  6250M|   296G|   133K (34)| 00:26:43 |
|*  2 |   TABLE ACCESS FULL| DEPT | 25000 |   317K|  8143   (1)| 00:01:38 |
|   3 |   TABLE ACCESS FULL| EMP  |  1000K|    36M| 81422   (1)| 00:16:18 |
---------------------------------------------------------------------------

   1 - access("EMP"."DEPTNO"="DEPT"."DEPTNO")
   2 - filter("DEPT"."DNAME"='SALES')