# Good Clustering Factor
Index = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ...
Table = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ...
# Bad Clustering Factor
Index = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ...
Table = 3, 8, 7, 1, 4, 5, 10, 2, 6, 9, ...
# Good Clustering Factor ?
Index = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ...
Table = 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, ...
1) Index Scan 의 Cost 에 큰 영향을 준다.
Cost = Blevel +
Leaf Blocks * Index Selectivity +
Clustering Factor * Table Selectivity +
Adjusted CPU Count
-- 예제
-- T_CLSF_I1 : 최적 Clustering Factor 값
-- T_CLSF_I2 : 최악 Clustering Factor 값
-- create objects
drop table t_clsf purge;
create table t_clsf(c1 int, c22 int);
create index t_clsf_i1 on t_clsf(c1);
create index t_clsf_i2 on t_clsf(c2);
-- c : same order as table, c2 : random
insert into t_clsf
select rownum, lvl
from
(select level lvl
from dual connect by level <= 10000
order by dbms_random.random)
;
-- gather stats
exec dbms_stats.gather_table_stats(user, 't_clsf', method_opt=>'for all columns size 1', cascade=>'true');
-- Table 통계
TABLE_NAME NUM_ROWS BLOCKS SAMPLE_SIZE
------------------------- ------------- --------- ---------------
T_CLSF 10000 20 10000
-- Column 통계 (NDV 와 DENSITY 가 완벽히 일치)
COLUMN_NAME NUM_DISTINCT NUM_NULLS DENSITY
------------------------- ------------- --------- ---------------
C1 10000 0 .0001
C2 10000 0 .0001
-- Clustering Factor (완전 다르다)
TABLE_NAME BLEVEL LEAF_BLOCKS CLUSTERING_FACTOR
------------------------- ----------- ------------ ------------------
T_CLSF_I1 1 19 18
T_CLSF_I2 1 32 9444
-- T_CLSF_I1 의 Clustering Factor(18)가 Table Block 수(20) 보다 작은 것은
-- 1) Sampling
-- 2) Table Block 중 데이터가 없는 Block 존재
-- T_CLSF_I2 의 Clustering Factor(9444)는 Table Row 수(10000)와 거의 일치
-- 좋은 Clustering Factor
explain plan for
select /*+ good clsf */ *
from t_clsf
where c1 between 1 and 100;
--------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU) |
--------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 100 | 700 | 3 (0) |
| 1 | TABLE ACCESS BY INDEX ROWID | T_CLSF | 100 | 700 | 3 (0) |
|* 2 | INDEX RANGE SCAN | T_CLSF_I1 | 100 | | 2 (0) |
--------------------------------------------------------------------------------
-- 나쁜 Clustering Factor (Selectivity 가 1% 인데 TABLE ACCESS FULL ?)
explain plan for
select /*+ bad clsf */ *
from t_clsf
where c2 between 1 and 100;
--------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU) |
--------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 100 | 700 | 22 (0) |
|* 1 | TABLE ACCESS FULL | T_CLSF | 100 | 700 | 22 (0) |
--------------------------------------------------------------------------------
-- 억지 (TABLE ACCESS FULL 이 낫네 ㅋ)
explain plan for
select /*+ bad clsf index(t_clsf) */ *
from t_clsf
where c2 between 1 and 100;
--------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU) |
--------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 100 | 700 | 97 (0) |
| 1 | TABLE ACCESS BY INDEX ROWID | T_CLSF | 100 | 700 | 97 (0) |
|* 2 | INDEX RANGE SCAN | T_CLSF_I2 | 100 | | 2 (0) |
--------------------------------------------------------------------------------
2) Clustering Factor 는 Index Lookup 을 통해 Data 를 읽는 일량을 결정한다.
질문
T_CLSF_I1 를 경유하는 경우, T_CLSF_I2 를 경유하는 경우, 그리고 TABLE FULL SCAN 하는 경우 단순히 Cost 의 차이가 아닌 실제 성능 차이가 존재하는가? 존재 한다면 어느 정도나?
-- 예제
alter session set statistics_level = all;
select /*+ index(t_clsf) */ *
from t_clsf
where c1 between 1 and 100
union all
select /*+ full(t_clsf) */ *
from t_clsf
where c1 between 1 and 100
union all
select /*+ full(t_clsf) */ *
from t_clsf
where c2 between 1 and 100
;
select * from table(dbms_xplan.display_cursor(null,null,'iostats cost last'));
alter session set statistics_level = typical;
-----------------------------------------------------------------------------
| Id | Operation | Name | Cost (%CPU) | Buffers |
--------------------------------------------------------------------------------
| 1 | UNION-ALL | | | 151 |
| 2 | TABLE ACCESS BY INDEX ROWID | T_CLSF | 3 (0) | 17 |
|* 3 | INDEX RANGE SCAN | T_CLSF_I1 | 2 (0) | 9 |
|* 4 | TABLE ACCESS FULL | T_CLSF | 22 (0) | 30 |
| 5 | TABLE ACCESS BY INDEX ROWID | T_CLSF | 97 (0) | 104 |
|* 6 | INDEX RANGE SCAN | T_CLSF_I2 | 2 (0) | 8 |
-----------------------------------------------------------------------------
* Clustering Factor 가 불량 해지면 Physical Reads 뿐 아니라 Logical Reads(Buffers)도 증가 한다?
* Case A : Clustering Factor(1), Table Data Block Logical Reads(1)
* Case B : Clustering Factor(3), Table Data Block Logical Reads(3)
* Buffer Pinning 메카니즘
* 한번의 Fetch 에서 두 번 연속 방문하는 Block 에 대해서 Pinning을 수행
* Pinned Block 은 cache buffers chains latch 를 패스 하고, Memory Access 를 이용해 직접 읽음 (buffer is pinned count)
* 케이스별 Table Data Block Logical Reads 의 세부 내용은...
* Case A : session logical reads(1), buffer is pinned count(2)
* Case B : session logical reads(3), buffer is pinned count(0)
@mysid
@mon_on &v_sid
select /*+ index(t_clsf) */ *
from t_clsf
where c1 between 1 and 100;
@mon_off
@mon_show
@mon_on &v_sid
select /*+ index(t_clsf) */ *
from t_clsf
where c2 between 1 and 100;
@mon_off
@mon_show
-- T_CLSF_I1 Index 를 경유하는 경우
NAME DIFF
---------------------------------------- ------------
redo size 3,096
bytes sent via SQL*Net to client 2,708
sorts (rows) 2,304
undo change vector size 2,244
bytes received via SQL*Net from client 1,609
buffer is pinned count 184
table fetch by rowid 100
session logical reads 50
db block changes 31
consistent gets from cache 29
consistent gets 29
buffer is not pinned count 23
-- T_CLSF_I2 Index 를 경유하는 경우
redo size 3,036
bytes sent via SQL*Net to client 2,708
sorts (rows) 2,304
undo change vector size 2,244
bytes received via SQL*Net from client 1,609
session logical reads 136
consistent gets from cache 117
consistent gets 117
buffer is not pinned count 111
no work - consistent read gets 103
table fetch by rowid 100
buffer is pinned count 96
-- Clustering Factor 가 불량한 경우 Buffer Pinning 의 발생률이 떨어지고, 이로 인해 Logical Reads 가 증가함
-- 넓은 범위의 Data 를 Index 를 경유해서 읽을 경우, Clustering Factor 가 Physical Reads 의 발생 빈도에 큰 영향을 미침 (Data Block 이 Cache 에서 밀려나고, 다시 읽고...)
-- t_clsf.c2 의 Clustering Factor 개선 하기 (대신 t_clsf.c1 는 나빠짐)
create table t_clsf2
as
select *
from t_clsf
order by c2;
drop table t_clsf;
alter t_clsf2 rename to t_clsf;
-- sys_op_countchg
select /*+ index(t1 t1(c1)) */
sys_op_countchg(substrb(rowid, 1, 15), 1) as cf
from t1
where c1 is not null
;
select /*+ index(t1 t1(c2)) */
sys_op_countchg(substrb(rowid, 1, 15), 1) as cf
from t1
where c2 is not null
;
- 강좌 URL : http://www.gurubee.net/lecture/3910
- 구루비 강좌는 개인의 학습용으로만 사용 할 수 있으며, 다른 웹 페이지에 게재할 경우에는 출처를 꼭 밝혀 주시면 고맙겠습니다.~^^
- 구루비 강좌는 서비스 제공을 위한 목적이나, 학원 홍보, 수익을 얻기 위한 용도로 사용 할 수 없습니다.