8. 비트맵 인덱스
2009.12.28 01:18
CHAPTER 8. 비트맵 인덱스
발표/총정리 - 위충환
시작하면서 222 / 위충환
[테스트 테이블]
- 비트맵 인덱스 사용의 비용에 영향을 미치는 주요 포인트 발견 목적
- 테이블스페이스 : 8KB 블록
- 될 수 있으면 1MB의 동일 익스텐트를 가진 LMT(Local Management Tablespace)를 사용
- 교재와 같은 결과를 얻고싶을 시 ASSM이 아니어야 함
- 온라인 코드집 init.ora 정의를 사용하지 않을 경우 db_file_multiblock_read_count = 8 설정 확인
- CPU Costing = Disabled
- PCTFREE 90
bitmap_cost_01.sql
테이블의 6개의 중요 컬럼 (각 개별적 인덱스 존재)
- n1 : 20개의 distinct 값이 골고루 흩어있음 (비트맵 인덱스)
n2 : 20개의 distinct 값 중 특정 값의 모든 로우가 500개 그룹에 모여있음 (비트맵 인덱스)
☞ 데이터 클러스터링의 변화가 통계정보에 어떤 영향을 미치는가?
- n3 : 25개의 distinct 값이 골고루 흩어있음 (비트맵 인덱스)
n4 : 25개의 distinct 값 중 특정 값의 모든 로우가 400개 그룹에 모여있음 (비트맵 인덱스)
☞ 옵티마이저가 비트맵 인덱스 결합 시 비용을 어떻게 산출하는가?
- n5 : 25개의 distinct 값이 골고루 흩어있음 (B-tree 인덱스)
n6 : 25개의 distinct 값 중 특정 값의 모든 로우가 400개 그룹에 모여있음 (B-tree 인덱스)
☞ 비트맵 인덱스와 B-tree 인덱스 크기 변화는?
- n1 : 20개의 distinct 값이 골고루 흩어있음 (비트맵 인덱스)
▼ 표 8-1. 비트맵과 B-tree 인덱스의 통계정보 차이
통계항목 t1_i1 t1_i2 t1_i3 t1_i4 t1_i5 t1_i6 (B-tree) blevel 1 1 1 1 1 1 60 10 63 9 217 217 distinct_keys 20 20 25 25 25 25 num_rows 120 20 125 25 10,000 10,000 clustering_factor 120 20 125 25 10,000 1,112 avg_leaf_blocks_per_key 3 1 2 1 8 8 avg_data_blocks_per_key 6 1 5 1 400 44
(비트맵)
(비트맵)
(비트맵)
(비트맵)
(B-tree) leaf_blocks
[결과]
- 데이터클러스팅 : 비트맵 인덱스 내 리프 블록 개수에 영향 (n1 & n2 차이 有, n3 & n4 차이 有, n5 & n6 차이 無)
☞ 비트맵 인덱스 : 흩어진 데이터 > 모아진 데이터
B-tree 인덱스 : 흩어진 데이터 = 모아진 데이터
직관적이지 못한 비트맵 인덱스 크기와 관련된 세부항목
t1_i1 과 t1_i3 비교 시 distinct 와 리프블록은 정비례
- but t1_i2 과 t1_i4 비교 시 distinct 와 리프블록은 반대 효과
- 테이블이 아주 크지 않을 시, 비트맵 인덱스의 distinct_keys = num_rows
(규칙이 아닌 우연, 8i 이하는 distinct_keys = num_rows 성립 )
Q. 데이터가 군집한 비트맵 인덱스 경우에만 해당되는가?? 흩어진 경우는 아래와 같은 규칙이 나옴.
- 데이터가 흩어진 경우(t1_i1, t1_i3) num_rows > distinct_keys
∵ 각 키의 비트 문자열이 리프 블록에 맞도록 여러조각으로 쪼개짐
- 비트맵 인덱스의 clustering_factor는 인덱스에 대한 num_rows의 복사본
☞ 테이블 내 데이터의 흩어짐과 직접적 연관성 無
데이터의 흩어짐 ▶ 비트맵 인덱스 엔트리의 크기에 영향
- avg_leaf_blocks_per_key = round(leaf_blocks / distinct_keys)
☞ 비트맵 인덱스와 관계 有
- avg_data_blocks_per_key = round(clustering_factor / distinct_keys)
☞ 비트맵 인덱스와 관계 無 ∵ 비트맵 인덱스의 clustering_factor가 테이블을 표현하지 않음
▼ 인덱스 사용의 추정 비용에 영향을 미치는 것을 알기 위한 테스트
(테스트 환경 : n3~n6 까지 각 컬럼에 대하여 '컬럼 = 상수' 쿼리 수행, autotrace 실행)
Execution Plan - (n6: B-tree index on clustered column) Execution Plan - (n5: B-tree index on scattered column) Execution Plan - (n4: bitmap index on clustered column) Execution Plan - (n3: bitmap index on scattered column)
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=ALL_ROWS (Cost=54 Card=400 Bytes=5600)
1 0 TABLE ACCESS (BY INDEX ROWID) OF 'T1' (Cost=54 Card=400 Bytes=5600)
2 1 INDEX (RANGE SCAN) OF 'T1_I6' (NON-UNIQUE) (Cost=9 Card=400)
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=ALL_ROWS (Cost=170 Card=400 Bytes=5600)
1 0 TABLE ACCESS (FULL) OF 'T1' (Cost=170 Card=400 Bytes=5600)
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=ALL_ROWS (Cost=114 Card=400 Bytes=5600)
1 0 TABLE ACCESS (BY INDEX ROWID) OF 'T1' (Cost=114 Card=400 Bytes=5600)
2 1 BITMAP CONVERSION (TO ROWIDS)
3 2 BITMAP INDEX (SINGLE VALUE) OF 'T1_I4'
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=ALL_ROWS (Cost=117 Card=400 Bytes=5600)
1 0 TABLE ACCESS (BY INDEX ROWID) OF 'T1' (Cost=117 Card=400 Bytes=5600)
2 1 BITMAP CONVERSION (TO ROWIDS)
3 2 BITMAP INDEX (SINGLE VALUE) OF 'T1_I3'
[결과]
B-tree 인덱스를 소유한 컬럼 중, 군집된 데이터 실행계획과 흩어진 데이터 실행계획이 다름
- 군집된 데이터 : 인덱스가 효율적 -> 비용이 적음
- 사용흩어진 데이터 : 인덱스의 공간 낭비 -> 비용이 높음
- 군집된 데이터 : 인덱스가 효율적 -> 비용이 적음
비트맵 인덱스를 소유한 컬럼의 군집된 데이터와 흩어진 데이터 실행계획은 차이 없고 비용이 비슷함
☞ 두 가지 경로에 대한 비용은 비슷하나 leaf_blocks은 7배까지 차이를 보임
인덱스 컴포넌트 227 / 위충환
▼ event 10053 트레이스 활성화 후 상세내용
For the query n3 = 2 bitmap on scattered data
---------------------------------------------
Access path: index (equal)
Index: T1_I3
TABLE: T1
RSC_CPU: 0 RSC_IO: 3
IX_SEL: 4.0000e-002 TB_SEL: 4.0000e-002
******** Bitmap access path accepted ********
Cost: 117 Cost_io: 117 Cost_cpu: 0.000000 Selectivity: 0.040000
Not believed to be index-only.
BEST_CST: 116.54 PATH: 20 Degree: 1
For the query n4 = 2 bitmap on clustered data
---------------------------------------------
Access path: index (equal)
Index: T1_I4
TABLE: T1
RSC_CPU: 0 RSC_IO: 1
IX_SEL: 4.0000e-002 TB_SEL: 4.0000e-002
******** Bitmap access path accepted ********
Cost: 114 Cost_io: 114 Cost_cpu: 0.000000 Selectivity: 0.040000
Not believed to be index-only.
BEST_CST: 114.34 PATH: 20 Degree: 1
- BEST_CST : 소수점 2자리까지 나타낸 후 가장 가까운 정수로 반올림
- RSC_IO : 인덱스 비용 = ceiling(leaf_blocks * 유효 인덱스 선택도) + blevel의 0(단, blevel = 1 일때 생략)
- 최종 비용 : 기술된 인덱스 컴포넌트 비용 * 1.1
인덱스 t1_i3 사용 : 인덱스 비용 = 3 ☞ 3.3으로 증가, Best_cst = 116.54 적용
∴ 실제 테이블 블록 액세스 비용 = 116.54 - 3.3 = 113.24 추정
인덱스 t1_i4 사용 : 인덱스 비용 = 1 ☞ 1.1로 증가, Best_cst = 114.34 적용
∴ 실제 테이블 블록 액세스 비용 = 114.34 - 1.1 = 113.24 추정
※ 비트맵 인덱스에서는 특정 양 데이터에 대해 실제 테이블을 액세스하는 '계산된 비용'이 데이터 군집성과 흩어짐과 상관없음
∵ 비용기반 옵티마이저는 실제 데이터의 흩어짐에 대해 전혀 모름
- 일반적 비용 전략
옵티마이저가 예상한 일이 일어나고, 옵티마이저가 예측한 횟수만큼의 I/O 요청 발생 기대
But 옵티마이저가 무시한 B-tree 인덱스를 → Bitmap 인덱스로 변경 시 발생
- 비트맵 인덱스와 B-tree 인덱스 사이 적절한 테스트 수행 시, 예상 비용과 무관하게 수행된 일량은 둘다 같음
(요청된 테이블 블록 읽기 수가 같음)
- 옵티마이저가 테이블 내 데이터 흩어짐에 대한 중요 정보를 읽어버릴 시, 대체 방법 필요
[상황] : B-tree 인덱스를 비트맵 인덱스로 변경
* 낮은 비용의 B-tree 인덱스 시작 시 : 동등 비트맵 인덱스는 높은 비용
test ☞ t1_i6 (cost 54) → t1_i4 (cost 114)
* 높은비용의 B-tree 인덱스 시작 시 : 동등 비트맵 인덱스는 낮은 비용
test ☞ t1_i5 (비용이 너무 높아 사용되지 않음) → t1_i3 (cost 117, 비용이 매우 낮아 사용)
테이블 컴포넌트 229 / 이태경
80/20 규칙
where n1 = {constant}137.99 ≒ 113.24*(500/400)
where n2 = {constant}
테이블 방문 비용 137.99
500 로우 리턴
where n3 = {constant}
where n4 = {constant}
테이블 방문 비용 113.23
400 로우 리턴
옵티마이저는 대상 데이터의 80%가 빈틈없이 모여있고 나머지 20%는 넓게 흩어져 있다고 가정(Oracle Wait Interface)
표 8-2. 비트맵 계산 - 80/20 분할의 확인
항목 | n1과 n2 컬럼의 인덱스 | n3과 n4 컬럼의 인덱스 |
테이블 내 로우 수 | 10,000 | 10,000 |
테이블 내 블록 수 | 1,112 | 1,112 |
블록 당 로우 수 | 8.993 | 8.993 |
한 값(=equal)에 해당하는 로우 수 | 500 | 400 |
모여있는 로우 수 | 0.8*500=400 | 0.8*400=320 |
모여있는 로우에 필요한 블록 수 | 400/8.993=44.48 | 320/8.993=35.58 |
남은 블록 수 | 1,112-44.48=1,067 | 1,112-35.55=1,076 |
흩어진 로우 수(20%) | 0.2*500=100 | 0.2*400=80 |
흩어진 로우에 필요한 블록 수 | 100 | 80 |
필요한 전체 블록 수 | 100+44.48=144.48 | 80+35.58=115.58 |
보고된 테이블 비용(10053 트레이스로부터) | 137.99 | 113.24 |
db_file_multiblock_read_count 값 변경에 따른 쿼리의 비용
80 201
81 199
82 200
83 202
84 200
85 201
86 203
87 204
db_file_multiblock_read_count COST
---------------------------- ----------
88 202
89 203
90 205
91 206
92 204
93 205
94 206
95 207
96 205
97 206
*db_file_multiblock_read_count가 올라갈수록 비용이 일반적으로 증가하지만, 패턴이 안정적이지 않다.
비트맵 결합 231 / 이태경
select
mod((rownum-1),20) n1, -- 20 values, scattered
trunc((rownum-1)/500) n2, -- 20 values, clustered
mod((rownum-1),25) n3, -- 25 values, scattered
trunc((rownum-1)/400) n4, -- 25 values, clustered
lpad(rownum,10,'0') small_vc,
rpad('x',220) padding
from
all_objects
where
rownum <= 10000
;
create bitmap index t1_i1 on t1(n1)
nologging
pctfree 90
;
create bitmap index t1_i2 on t1(n2)
nologging
pctfree 90
;
create bitmap index t1_i3 on t1(n3)
nologging
pctfree 90
;
create bitmap index t1_i4 on t1(n4)
nologging
pctfree 90
;USER_INDEXES 정보
NAME BLEVEL LEAF_BLOCKS DISTINCT_KEYS NUM_ROWS CLUSTERING_FACTOR AVG_LEAF_BLOCKS_PER_KEY AVG_DATA_BLOCKS_PER_KEY T1_I1 1 60 20 120 120 3 6 T1_I2 1 10 20 20 20 1 1 T1_I3 1 63 20 125 125 2 5 T1_I4 1 9 20 25 25 1 1
select
small_vc
from
t1
where n1 = 2 -- one in 20, scattered data
and n3 = 2 -- one in 25, scattered data
;
Execution Plan
----------------------------------------------------------
Plan hash value: 885960881
----------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost |
----------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 20 | 340 | 13 |
| 1 | TABLE ACCESS BY INDEX ROWID | T1 | 20 | 340 | 13 |
| 2 | BITMAP CONVERSION TO ROWIDS| | | | |
| 3 | BITMAP AND | | | | |
|* 4 | BITMAP INDEX SINGLE VALUE| T1_I3 | | | |
|* 5 | BITMAP INDEX SINGLE VALUE| T1_I1 | | | |
----------------------------------------------------------------------
비트맵 인덱스 Scan 비용 = { ceiling(60/20) + ceiling(63/25) } * 비트맵 비율(1.1) = 6.6
bitmap and 인덱스의 distinct 한 값 = 각 인덱스의 distinct_key를 크로스 체크 = 20*25 = 500
∴ 전체 로우의 1/500 을 리턴한다. 즉 20개의 로우를 리턴.
그럼 80/20 추정에 근거인덱스에 필요한 총 블록의 갯수 = 2+4 = 6
row 개수 row의 분산 성격 차지하는 블록 갯수 16 clustered 2 block(1block per 9 row) 4 scattered 4 block(흩어져 있으므로 1 block per row)
∴ 비용 = round(6.6+6) = 13
실제 트레이스 파일에는 12.87 (필자는 옵티마이저의 테이블 방문 비용을 6.27로 예상)
select
small_vc
from
t1
where n2 = 2 -- one in 20, clustered data
and n4 = 2 -- one in 25, clustered data
;
Execution Plan
----------------------------------------------------------
Plan hash value: 2751833032
----------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost |
----------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 20 | 340 | 9 |
| 1 | TABLE ACCESS BY INDEX ROWID | T1 | 20 | 340 | 9 |
| 2 | BITMAP CONVERSION TO ROWIDS| | | | |
| 3 | BITMAP AND | | | | |
|* 4 | BITMAP INDEX SINGLE VALUE| T1_I4 | | | |
|* 5 | BITMAP INDEX SINGLE VALUE| T1_I2 | | | |
----------------------------------------------------------------------
선택도가 제일 좋은(Row를 가장 많이 제거하는) 인덱스를 먼저 처리
==> 전개하고 비교해야 할 비트맵 인덱스 조각(Fragment)의 개수↓
인덱스 방문 비용 = 1(∵인덱스의 크기가 작아)
비용 = 6.27(테이블 비용) + 1(인덱스 방문 비용)*2(인덱스 총 갯수) = ceiling(8.47) = 9
낮은 카디널리티 234 / 박우창
? 오라클은 언제 비트맵 인덱스를 하나 더 사용해도 이득이 없다고 결정할까?
-> 비트맵 인덱스의 리프 블록 세트를 하나 더 스캔하는 비용이 조회해야 할 테이블 블록 개수의 감소량 보다 클 때 사용한다. 소수의 distinct 값을 가지는 속성은 비트맵인덱스에 적합지 않다.
? 예제 - (bitmap_cost_03.sql)
- 3천6백만 로우, 800M(107,543블록), 6가지의 속성 및 비트맵 인덱스를 가지는 테이블
(속성) 성별 : 2가지, 눈의 색 : 3가지, 머리카락 색 : 7가지,
거주지 : 31가지,연령대 : 47가지, 직업분류 : 79가지
(질의 조건)
sex= 1 and eyes= 1 and hair= 1
and town= 15 and age= 25 and work= 40
(인덱스 통계 정보)
Column |
blevel |
leaf_ blocks |
distinct_ keys |
leaf_blocks/distinct_keys (blevel 포함) |
sex_code |
2 |
1,340 |
2 |
670(672) |
eye_code |
2 |
2,010 |
3 |
670(672) |
hair_code |
2 |
4,690 |
7 |
670(672) |
town_code |
2 |
5,022 |
31 |
162(164) |
age_code |
2 |
5,241 |
47 |
112(114) |
work_code |
2 |
5,688 |
79 |
72(74) |
(질의 결과) 6개 비트맵 조건을 다 사용하지 않고 정밀도가 높은 3개의 인덱스를 사용
(실행계획)
Execution Plan
----------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost |
----------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 18 | 314 |
| 1 | SORT AGGREGATE | | 1 | 18 | |
|* 2 | TABLE ACCESS BY INDEX ROWID | T1 | 7 | 126 | 314 |
| 3 | BITMAP CONVERSION TO ROWIDS| | | | |
| 4 | BITMAP AND | | | | |
|* 5 | BITMAP INDEX SINGLE VALUE| I6 | | | |
|* 6 | BITMAP INDEX SINGLE VALUE| I5 | | | |
|* 7 | BITMAP INDEX SINGLE VALUE| I4 | | | |
----------------------------------------------------------------------
? 옵티마이저의 판단 과정
- town_code, age_code, work_code를 지정하여 식별한 로우의 평군 개수를 산출하면, 옵티마이저가 이들 세 컬럼만으로 테이블 방문을 36,000,000/(31*47*79) = 313 로우로 제한하기에 충분하다고 결정할 것이다. 최악으로 흩어져 있어도 313개 테이블 블록을 방문하는 정도이다.
- 차선책에 해당하는 hair_code 인덱스가 테이블 방문 횟수를 313에서 0으로 감소시킬지라도 672개의 인덱스 블록을 방문해야 하므로 인덱스를 사용하는 것은 별 가치가 없다.
- 위의 조건의 경우 실제로, 정밀도가 높은 세 개의 인덱스 만을 사용한다. 쿼리의 총 비용은 314로 보고되었다. 그러나 이들 인덱스에 대해서 (blevel+leaf_blocks*유효 인덱스 선택도)의 합계를 보면, 352(=164+114+74)가 된다. 314는 1.1의 배율을 곱해서 테이블 방문 비용을 더하기 전의 값이다.
- 최종적으로 보고된 비용은 두 인덱스의 통계정보를 무시하고 가장 비용이 낮은 인덱스의 값을 세 번 사용하는 것으로 생각된다.
추측비용 (목표치는 314) =
74 * 1.1 * 3 + (가장 비용이 낮은 인덱스를 1.1배 증가시켜서 세 번 사용)
0.8 * 313 / 355 + (로우의 80%는 블록 당 335개씩 모여있음)
0.2 * 313 = (로우의 20%는 개별 블록에 흩어져 있음)
244.2 + 62.6 + 0.75 = 307.55 (에러율 2.1%)
- hair 컬럼을 정렬하여 테스트 사례를 재생성한 후 다시 실행해 보면 4개의 인덱스를 선택한다. (비용 336)
- 옵티마이저는 언제나 비용이 가장 낮은 실행계획을 선택해야 한다. 위의 결과에서는 비용이 더 높은 경우(314 -> 336)에도 비트맵 인덱스를 사용하였다. 비트맵 인덱스 비용을 계산하는 코드에 약간의 문제가 있는 것이 분명하다.
? 비트맵인덱스는 테이블에 비하여 크기가 작다. 따라서 비트맵인덱스를 메모리에서 액세스하는 논리적 I/O를 높이더라도 테이블을 읽는 물리적 I/O를 줄이는 것도 좋은 방법이다.
NULL 컬럼 238 / 박우창
? 오라클에게 데이터에 대해서 가능한 많은 정보를 알려주는 것은 언제나 중요하다.
NOT NULL 컬럼은 특히 중요한데, 이것은 옵티마이저에게 허용되는 여러 가능성에 큰 차이를 만들 수 있다.
? 예제(! 조건 포함)
alter table t1 modify n1 not null;
select small_vc
from t1
where n1 != 2 and n3= 3
(실행계획)
-----------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost |
-----------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 380 | 6460 | 113 |
| 1 | TABLE ACCESS BY INDEX ROWID | T1 | 380 | 6460 | 113 |
| 2 | BITMAP CONVERSION TO ROWIDS | | | | |
| 3 | BITMAP MINUS | | | | |
| 4 | BITMAP MINUS | | | | |
|* 5 | BITMAP INDEX SINGLE VALUE| T1_I3 | | | |
|* 6 | BITMAP INDEX SINGLE VALUE| T1_I1 | | | |
|* 7 | BITMAP INDEX SINGLE VALUE | T1_I1 | | | |
-----------------------------------------------------------------------
- 실행계획에 bitmap minus 연산이 두번 나타남. 비트맵 인덱스는 모든 키가 NULL인 엔트리를 포함하기 때문에, 두 번의 minus 연산이 나타난다. n1이 NOT NULL 임을 알고있다면 BITMAP MINUS 가 1번만 나타나도 된다. 해당 컬럼이 절대 NULL이 되지 않음을 알고 있다면, 이런 이유에서라도 테이블 정의에 사소한 정보를 더 포함해야 한다.
? 2개의 비트맵 인덱스가 OR로 같이 사용되면 비트맵 컬럼 내에 NULL의 개수를 놓쳐버릴 수 있다.(bitmap_or.sql 참조)
- 1,000,000개 튜플중 n1 컬럼이 NOT NULL인 로우는 정확히 1,000개, 1,000개의 distinct 값, density는 1/1,000이므로 옵티마이저는 카디널리티가 1이라고 결정할 것이다. n1을 n2로 변경해도 마찬가지이다.
select small_vc
from t1
where n1 = 50000 orn2 = 50000
- 계산된 카디널리티 = 1,999 : 오라클은 NULL을 고려하지 않고 테이블 내 전체 로우의 개수에 density를 적용한 것으로 보인다. 이것은 두 조건의 and 연산에 대한 표준공식을 사용하여 n1 조건에 의한 1,000개(1,000,000 * 1/1,000)의 로우에 n2 조건에 의한 1,000개의 로우를 더한 후, 서로 겹치는 한 개의 로우를 뺀 값이다.
- is not null 조건을 하나 추가하면, 계산된 카디널리티가 1,000으로 떨어진다.
(n1 = 50000 and n1 is not null)
- is not null 조건을 하나 더 추가하면, 카디널리티가 정확히 1까지 떨어진다.
(n2 = 50000 and n2 is not null)
CPU costing 242 / 이창헌
alter session set "_optimizer_cost_model"=cpu
begin
dbms_stats.set_system_stats('MBRC',8);
dbms_stats.set_system_stats('MREADTIM',20);
dbms_stats.set_system_stats('SREADTIM',10);
dbms_stats.set_system_stats('CPUSPEED',350);
exception
when others then null;
end
설정 후 bitmap_cost_05.sql
1. 실제 cup 비용이 대략 두 배가 됨
2. 비트맵 인덱스의 i/o비용을 계산하는 코드는 cpu costing이 활성화되면 다른 i/o수치를 제공함.
재미있는 사례들 244 / 이창헌
비트맵 인덱스에서 약간 더 애매모호한 부분 몇 가지를 다중 컬럼 인덱스, 비트맵 변환에서 소개 합니다.
다중 컬럼 인덱스 244 / 남송휘
- 필요예
- 컬럼에 대한 distinct 값이 테이블 전체에 분산된 방식에 따라
- 두컬럼에 대한 단일 비트맵 인덱스가 개별적인 두개의 비트맵 인덱스 크기의 합보다 실제로 더 작을 수있음
- 다른 테이블에서 가져운 키 값을 가짐
create bitmap index fct_dim_name on fact_table(dim.dim_name)
from
dim_table dim,
fact_table fct
where
dim.id = fct.dim_id
;
매우 큰 팩트 테이블에 긴 디멘션명을 사용하는 인덱스를 생성하지만 디멘션명이 팩트 테이블에 수백만건 저장되지 않음
create bitmap index fct_dim_par on fact_table(dim.par_name)
from
dim_table dim,
fact_table fct
where
dim.id = fct.dim_id
;
디멘션 테이블의 속성( 디멘션 ID보다 아주적은 distinct값을 가짐) 에 대한 쿼리를 통해서 팩트 테이블 액세스
(인덱스가 매우 작으며 유용- 사용자가 이런 속성을 빈번하게 사용한다고 가정)
bitmap_cost_06.sql
TABLE_NAME BLOCKS NUM_ROWS
-------------------- ---------- ----------
DIM_TABLE 58 10000
FACT_TABLE 30526 1000000
2 rows selected.
Elapsed: 00:00:00.06
TABLE_NAME COLUMN_NAME NUM_NULLS NUM_DISTINCT DENSITY LOW_VALUE HIGH_VALUE
---------- ------------ ---------- ------------ ---------- ------------------------ ------------------------
DIM_TABLE DIM_NAME 0 10000 .0001 44696D656E73696F6E5F3030 44696D656E73696F6E5F3030
30303030 39393939
ID 0 10000 .0001 80 C26464
PAR_NAME 0 100 .01 506172656E745F303030 506172656E745F303939
FACT_TABLE DIM_ID 0 10000 .0001 80 C26464
ID 0 1000000 .000001 C102 C402
PADDING 0 1 1 782020202020202020202020 782020202020202020202020
202020202020202020202020 202020202020202020202020
2020202020202020 2020202020202020
NAME BLEVEL LEAF_BLOCKS KEYS NUM_ROWS CLU_FAC LEAF_PER_KEY DATA_PER_KEY
------------ ---------- ----------- ---------- ---------- ---------- ------------ ------------
DIM_PK 1 20 10000 10000 52 1 1
FCT_DIM_NAME 2 625 10000 10000 10000 1 1
FCT_DIM_PAR 2 450 100 900 900 4 9
인덱스 생성후 실행
Execution Plan
-----------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost |
-----------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 9 | 2222 |
| 1 | SORT AGGREGATE | | 1 | 9 | |
| 2 | TABLE ACCESS BY INDEX ROWID | FACT_TABLE | 10000 | 90000 | 2222 |
| 3 | BITMAP CONVERSION TO ROWIDS| | | | |
|* 4 | BITMAP INDEX SINGLE VALUE | FCT_DIM_PAR | | | |
-----------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
4 - access("FCT"."SYS_NC00005$"='Parent_001')
- 80/20 분할을 적용하면
- 2000개는 넓게 흩어져 있고 8000개는 빈틈없이 모여있는것으로 추측
- 2,000 + 8,000 / 32 ( 1,000,000 개의 로우 30526의 블록)
- 약 2250 오차있음
인덱스 사용안했을경우
/*+ no_index(fct) */ 사용
Execution Plan
----------------------------------------------------------
Plan hash value: 1564920903
------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost |
------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 24 | 2947 |
| 1 | SORT AGGREGATE | | 1 | 24 | |
|* 2 | HASH JOIN | | 10000 | 234K| 2947 |
|* 3 | TABLE ACCESS FULL| DIM_TABLE | 100 | 1500 | 7 |
| 4 | TABLE ACCESS FULL| FACT_TABLE | 1000K| 8789K| 2937 |
------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("FCT"."DIM_ID"="DIM"."ID")
3 - filter("DIM"."PAR_NAME"='Parent_001')인덱스를 사용하고 db_file_multiblock_read_count 를 증가시킨후
alter session set db_file_multiblock_read_count = 20;
Execution Plan
----------------------------------------------------------
Plan hash value: 3000870606
-----------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost |
-----------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 9 | 2249 |
| 1 | SORT AGGREGATE | | 1 | 9 | |
| 2 | TABLE ACCESS BY INDEX ROWID | FACT_TABLE | 10000 | 90000 | 2249 |
| 3 | BITMAP CONVERSION TO ROWIDS| | | | |
|* 4 | BITMAP INDEX SINGLE VALUE | FCT_DIM_PAR | | | |
-----------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
4 - access("FCT"."SYS_NC00005$"='Parent_001')
값이 계산 결과에 근접
비트맵 변환 247 /남송휘
- bitmap conversion (to rowids) : 배열의 엔트리를 테이블 엔트리로 변환하는 계산
- bitmap conversion (from rowids) : b-tree to bitmap conversion (bitmap_cost_07.sql)
bitmap_cost_07.sql
NUM_ROWS BLOCKS
---------- ----------
1000000 18032
INDEX_NAME BLEVEL LEAF_BLOCKS DISTINCT_KEYS CLUSTERING_FACTOR
-------------------- ---------- ----------- ------------- -----------------
T1_I1 2 1951 50 604587
T1_I2 2 1951 50 604886
Hinted bitmap combine
/*+ index_combine(t1 t1_i1 t1_i2) */
Execution Plan
----------------------------------------------------------
Plan hash value: 2658754337
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 400 | 6800 | 188 |
| 1 | TABLE ACCESS BY INDEX ROWID | T1 | 400 | 6800 | 188 |
| 2 | BITMAP CONVERSION TO ROWIDS | | | | |
| 3 | BITMAP AND | | | | |
| 4 | BITMAP CONVERSION FROM ROWIDS| | | | |
|* 5 | INDEX RANGE SCAN | T1_I1 | | | 42 |
| 6 | BITMAP CONVERSION FROM ROWIDS| | | | |
|* 7 | INDEX RANGE SCAN | T1_I2 | | | 42 |
--------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
5 - access("N1"=33)
7 - access("N2"=21)
Unhinted
Execution Plan
----------------------------------------------------------
Plan hash value: 2658754337
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 400 | 6800 | 188 |
| 1 | TABLE ACCESS BY INDEX ROWID | T1 | 400 | 6800 | 188 |
| 2 | BITMAP CONVERSION TO ROWIDS | | | | |
| 3 | BITMAP AND | | | | |
| 4 | BITMAP CONVERSION FROM ROWIDS| | | | |
|* 5 | INDEX RANGE SCAN | T1_I1 | | | 42 |
| 6 | BITMAP CONVERSION FROM ROWIDS| | | | |
|* 7 | INDEX RANGE SCAN | T1_I2 | | | 42 |
--------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
5 - access("N1"=33)
7 - access("N2"=21)
/*+ and_equal(t1 t1_i1 t1_i2) */
Execution Plan
----------------------------------------------------------
---------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost |
---------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 400 | 6800 | 477 |
|* 1 | TABLE ACCESS BY INDEX ROWID| T1 | 400 | 6800 | 477 |
| 2 | AND-EQUAL | | | | |
|* 3 | INDEX RANGE SCAN | T1_I1 | 20000 | | 42 |
|* 4 | INDEX RANGE SCAN | T1_I2 | 20000 | | 42 |
---------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("N1"=33 AND "N2"=21)
3 - access("N1"=33)
4 - access("N2"=21)
- 비트맵 엔트리와 rowid를 서로 변환할수 있다면 실행계획의 어떤 지점에서도 이 변환을 수행할수 있음(bitmap_cost_08.sql)
bitmap_cost_08.sql
select
to_char(trunc(sysdate),'dd-mon-yyyy') today,
to_char(trunc(sysdate) + 3,'dd-mon-yyyy') future
from dual;
TODAY FUTURE
----------- -----------
24-jan-2010 27-jan-2010
TABLE_NAME BLOCKS NUM_ROWS
-------------------- ---------- ----------
T1 32490 1000000
COLUMN_NAME NUM_NULLS NUM_DISTINCT DENSITY LOW_VALUE HIGH_VALUE
-------------------- ---------- ------------ ---------- ------------------------ ------------------------
D1 0 1000 .001 786E0118010101 78700A13010101
N1 0 5 .2 80 C105
PADDING 0 1 1 782020202020202020202020 782020202020202020202020
202020202020202020202020 202020202020202020202020
2020202020202020 2020202020202020
V1 0 1000000 .000001 30303030303030303031 30303031303030303030
NAME BLEVEL LEAF_BLOCKS DISTINCT_KEYS NUM_ROWS CLUSTERING_FACTOR AVG_LEAF_BLOCKS_PER_KEY AVG_DATA_BLOCKS_PER_KEY
----- ---------- ----------- ------------- ---------- ----------------- ----------------------- -----------------------
T1_D1 1 32 1000 1000 1000 1 1
T1_N1 1 140 5 280 280 28 56
select
d1,
count(*)
from
t1
where
n1 = 2
and d1 between to_date('&m_today', 'DD-MON-YYYY')
and to_date('&m_future','DD-MON-YYYY')
group by
d1
;
Execution Plan
----------------------------------------------------------
-----------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost |
-----------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 4 | 44 | 44 |
| 1 | HASH GROUP BY | | 4 | 44 | 44 |
|* 2 | VIEW | index$_join$_001 | 801 | 8811 | 41 |
|* 3 | HASH JOIN | | | | |
| 4 | BITMAP CONVERSION TO ROWIDS| | 801 | 8811 | 2 |
|* 5 | BITMAP INDEX RANGE SCAN | T1_D1 | | | |
| 6 | BITMAP CONVERSION TO ROWIDS| | 801 | 8811 | 28 |
|* 7 | BITMAP INDEX SINGLE VALUE | T1_N1 | | | |
-----------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("N1"=2 AND "D1">=TO_DATE('2010-01-24 00:00:00', 'yyyy-mm-dd
hh24:mi:ss') AND "D1"<=TO_DATE('2010-01-27 00:00:00', 'yyyy-mm-dd
hh24:mi:ss'))
3 - access(ROWID=ROWID)
5 - access("D1">=TO_DATE('2010-01-24 00:00:00', 'yyyy-mm-dd
hh24:mi:ss') AND "D1"<=TO_DATE('2010-01-27 00:00:00', 'yyyy-mm-dd
hh24:mi:ss'))
7 - access("N1"=2)
요약 250 / 위충환
테스트 스크립트 251 / 위충환
댓글 0
번호 | 제목 | 글쓴이 | 날짜 | 조회 수 |
---|---|---|---|---|
17 | 진행기록 | 운영자 | 2010.04.10 | 7747 |
16 |
Front Page
![]() | 운영자 | 2009.12.20 | 130233 |
15 | 비용기반의 오라클 원리 첫 모임 [4] | 운영자 | 2010.01.03 | 5894 |
14 | 1. 비용(COST)이란? | ZoneWorker2 | 2009.12.28 | 18069 |
13 | 2. 테이블 스캔 | balto | 2009.12.28 | 13286 |
12 | 3. 단일 테이블 선택도 | ZoneWorker2 | 2009.12.28 | 8462 |
11 |
4. 단순 B-tree 액세스
![]() | 휘휘 | 2009.12.28 | 8945 |
10 | 5. 클러스터링 팩터 | balto | 2009.12.28 | 14830 |
9 | 6. 선택도 이슈 | ZoneWorker2 | 2009.12.28 | 7688 |
8 | 7. 히스토그램 | ZoneWorker2 | 2009.12.28 | 10160 |
» | 8. 비트맵 인덱스 | ZoneWorker2 | 2009.12.28 | 8088 |
6 | 9. 쿼리 변환 | 헌쓰 | 2009.12.28 | 15181 |
5 | 10. 조인 카디널리티 | 휘휘 | 2009.12.28 | 8894 |
4 | 11. NL(Nested Loops) 조인 | 휘휘 | 2009.12.28 | 11925 |
3 |
12. 해시 조인
![]() | 헌쓰 | 2009.12.28 | 17078 |
2 | 13. 정렬과 머지 조인 | 실천하자 | 2009.12.28 | 16217 |
1 |
14. 10053 트레이스 파일
![]() | 휘휘 | 2009.12.28 | 10340 |