OS 환경 : Oracle Linux 8.7 (64bit)
DB 환경 : Oracle Database 19.31.0.0
방법 : 오라클 19c CLUSTER_BY_ROWID 힌트 사용 테스트
CLUSTER_BY_ROWID 힌트는 인덱스에서 읽은 ROWID를 ROWID 순서로 정렬한 후 테이블에 접근하도록 유도하는 힌트임
일반적인 인덱스 스캔은 인덱스 키 순서로 ROWID를 읽고 해당 ROWID가 가리키는 테이블 블록에 접근함
인덱스의 CF가 안좋은 경우 인덱스 키 순서와 테이블의 물리적인 저장 순서가 다르기 때문에 같은 테이블 블록을 반복해서 접근할 수 있음
이 힌트를 사용하면 인덱스에서 읽은 ROWID를 정렬한 후 테이블에 접근함
이 기능이 적용되면 실행계획의 INDEX RANGE SCAN과 TABLE ACCESS 사이에 SORT CLUSTER BY ROWID Operation이 추가됨
ROWID를 정렬하는 작업이 추가되지만 같은 테이블 블록에 포함된 행을 연속해서 처리할 수 있기 때문에 테이블 접근 시 발생하는 Buffers를 줄일 수 있음
이 힌트는 Oracle 11.2.0.4의 V$SQL_HINT에서 확인된 내부 힌트이고
Oracle 19c에서도 사용할 수 있지만 공식 SQL Language Reference의 힌트 목록에는 나오지 않는 미문서화 힌트이므로 운영 쿼리에 적용하기 전 해당 RU에서 충분한 테스트가 필요함
참고 : https://jonathanlewis.wordpress.com/2014/02/12/caution-hints/
참고로 19c에서 이 기능 관련 히든 파라미터를 확인하면
_optimizer_cluster_by_rowid, _optimizer_cluster_by_rowid_batch_size, _optimizer_cluster_by_rowid_batched, _optimizer_cluster_by_rowid_control
이렇게 4개가 존재함, 이중 활성화, 비활성화 하는 파라미터의 기본값은 true임(_optimizer_cluster_by_rowid, _optimizer_cluster_by_rowid_batched)
본문에서는 CF가 좋은 C1 인덱스와 CF가 안좋은 C2 인덱스를 생성한 후 NO_CLUSTER_BY_ROWID와 CLUSTER_BY_ROWID 힌트를 각각 사용하여 실행계획과 Buffers를 비교해봄
테스트
사전준비
1. CF가 좋은 인덱스 테스트
1_1. C1 인덱스에서 CLUSTER_BY_ROWID를 사용하지 않은 경우
1_2. C1 인덱스에서 CLUSTER_BY_ROWID를 사용한 경우
2. CF가 안좋은 인덱스 테스트
2_1. C2 인덱스에서 CLUSTER_BY_ROWID를 사용하지 않은 경우
2_2. C2 인덱스에서 CLUSTER_BY_ROWID를 사용한 경우
테스트
사전준비
CLUSTER_BY_ROWID 힌트 확인
|
1
2
3
4
5
6
7
8
9
10
11
12
13
|
SQL>
set lines 200 pages 1000
col name for a25
col inverse for a30
col version for a15
select name, inverse, version
from v$sql_hint
where name in ('CLUSTER_BY_ROWID', 'NO_CLUSTER_BY_ROWID');
NAME INVERSE VERSION
------------------------- ------------------------------ ---------------
CLUSTER_BY_ROWID NO_CLUSTER_BY_ROWID 12.1.0.1
NO_CLUSTER_BY_ROWID CLUSTER_BY_ROWID 12.1.0.1
|
CLUSTER_BY_ROWID와 반대 동작을 하는 NO_CLUSTER_BY_ROWID 힌트가 확인됨
두 힌트 모두 VERSION은 12.1.0.1로 표시됨
CLUSTER_BY_ROWID 기능 관련 히든 파라미터 확인
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
SQL>
set lines 200 pages 1000
col name for a40
col session for a10
col default_value for a15
col default_t_f for a15
col instance for a10
col desc for a70
select
a.ksppinm "name",
decode(p.isses_modifiable,'FALSE',NULL,NULL,NULL,b.ksppstvl) "session",
c.ksppstvl "instance",
b.ksppstdfl "default_value",
b.ksppstdf "default_t_f",
a.ksppdesc "desc"
from x$ksppi a, x$ksppcv b, x$ksppsv c, v$parameter p
where 1=1
and a.indx=b.indx
and a.indx=c.indx
AND p.name(+) = a.ksppinm
AND SUBSTR(a.KSPPINM, 1, 1) = '_'
and a.ksppinm like '%cluster_by%'
order by 1;
name session instance default_value default_t_f desc
---------------------------------------- ---------- ---------- --------------- --------------- --------------------------------------------------------
_optimizer_cluster_by_rowid TRUE TRUE TRUE enable/disable the cluster by rowid feature
_optimizer_cluster_by_rowid_batch_size 100 100 TRUE Sorting batch size for cluster by rowid feature
_optimizer_cluster_by_rowid_batched TRUE TRUE TRUE enable/disable the cluster by rowid batching feature
_optimizer_cluster_by_rowid_control 129 3 TRUE internal control for cluster by rowid feature mode
|
4개가 존재하지만 _optimizer_cluster_by_rowid 파라미터와 _optimizer_cluster_by_rowid_batched 파라미터가 활성화, 비활성화 하는 파라미터로 보임
두가지 파라미터 모두 기본값이 true임
테스트 테이블 및 인덱스 생성, 통계정보 수집
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
SQL>
drop table t_cluster_rowid purge;
create table t_cluster_rowid (
c1 number, c2 number, c3 varchar2(100)
);
insert into t_cluster_rowid
select level,
mod(level, 1000),
rpad('X', 100, 'X')
from dual
connect by level <= 1000000;
commit;
create index ix_cluster_rowid_c1 on t_cluster_rowid(c1);
create index ix_cluster_rowid_c2 on t_cluster_rowid(c2);
exec dbms_stats.gather_table_stats(user, 'T_CLUSTER_ROWID', cascade => true);
|
C1 순서로 100만 건을 Insert하고 C2에는 0부터 999까지의 값이 반복해서 들어가도록 구성함
C1은 테이블에 저장된 순서와 인덱스 키 순서가 동일함
따라서 C1 인덱스는 CF가 좋은 상태로 생성됨
C2는 테이블에 0부터 999까지의 값이 반복해서 저장되지만 C2 인덱스에는 동일한 값의 ROWID가 연속해서 저장됨
따라서 C2 인덱스의 키 순서와 테이블에 저장된 행 순서가 다르며 CF가 안좋은 상태로 생성됨
테이블 및 인덱스 통계 확인
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
SQL>
set lines 200 pages 1000
col owner for a10
col table_name for a25
col index_name for a30
select owner, table_name, num_rows, blocks
from dba_tables
where table_name = 'T_CLUSTER_ROWID';
OWNER TABLE_NAME NUM_ROWS BLOCKS
---------- ------------------------- ---------- ----------
IMSI T_CLUSTER_ROWID 1000000 16217
select owner, index_name, num_rows, leaf_blocks, clustering_factor
from dba_indexes
where owner = 'IMSI'
and table_name = 'T_CLUSTER_ROWID'
order by index_name;
OWNER INDEX_NAME NUM_ROWS LEAF_BLOCKS CLUSTERING_FACTOR
---------- ------------------------------ ---------- ----------- -----------------
IMSI IX_CLUSTER_ROWID_C1 1000000 2226 15873
IMSI IX_CLUSTER_ROWID_C2 1000000 2077 1000000
|
IX_CLUSTER_ROWID_C1의 CF는 테이블의 BLOCKS에 가까운 값임
C1 인덱스의 키 순서와 테이블에 저장된 행 순서가 비슷하기 때문
반면에 IX_CLUSTER_ROWID_C2의 CF는 1000000으로 테이블의 NUM_ROWS와 동일함
C2 인덱스의 키 순서와 테이블에 저장된 행 순서가 다르기 때문
먼저 CF가 좋은 C1 인덱스와 CF가 안좋은 C2 인덱스에 각각 CLUSTER_BY_ROWID 힌트를 적용하여 Buffers 차이를 확인해봄
1. CF가 좋은 인덱스 테스트
1_1. C1 인덱스에서 CLUSTER_BY_ROWID를 사용하지 않은 경우
IX_CLUSTER_ROWID_C1 인덱스로 조회하고 NO_CLUSTER_BY_ROWID 힌트를 사용해 인덱스에서 읽은 ROWID를 별도로 정렬하지 않도록 함
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
|
SQL>
set timing on
alter session set statistics_level = all;
select /*+ index(t ix_cluster_rowid_c1) no_cluster_by_rowid(t) */
sum(length(c3))
from t_cluster_rowid t
where c1 between 1 and 100000;
SELECT * FROM DBMS_XPLAN.DISPLAY_CURSOR(NULL, NULL, 'ALLSTATS LAST -rows -Projection +HINT_REPORT +outline');
Plan hash value: 2614982675
-------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | A-Rows | A-Time | Buffers |
-------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 1 |00:00:00.09 | 1812 |
| 1 | SORT AGGREGATE | | 1 | 1 |00:00:00.09 | 1812 |
| 2 | TABLE ACCESS BY INDEX ROWID BATCHED| T_CLUSTER_ROWID | 1 | 100K|00:00:00.03 | 1812 |
|* 3 | INDEX RANGE SCAN | IX_CLUSTER_ROWID_C1 | 1 | 100K|00:00:00.01 | 224 |
-------------------------------------------------------------------------------------------------------------
Outline Data
-------------
/*+
BEGIN_OUTLINE_DATA
IGNORE_OPTIM_EMBEDDED_HINTS
OPTIMIZER_FEATURES_ENABLE('19.1.0')
DB_VERSION('19.1.0')
ALL_ROWS
OUTLINE_LEAF(@"SEL$1")
INDEX_RS_ASC(@"SEL$1" "T"@"SEL$1" ("T_CLUSTER_ROWID"."C1"))
BATCH_TABLE_ACCESS_BY_ROWID(@"SEL$1" "T"@"SEL$1")
END_OUTLINE_DATA
*/
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("C1">=1 AND "C1"<=100000)
Hint Report (identified by operation id / Query Block Name / Object Alias):
Total hints for statement: 2
---------------------------------------------------------------------------
2 - SEL$1 / T@SEL$1
- index(t ix_cluster_rowid_c1)
- no_cluster_by_rowid(t)
44 rows selected.
|
인덱스를 읽고 테이블에 접근할 때 약 1600 블록(1812-224)을 소모함
C1 인덱스의 키 순서와 테이블의 물리적인 저장 순서가 비슷하기 때문에
INDEX RANGE SCAN에서 반환된 ROWID 순서대로 테이블에 접근해도 같은 블록의 행을 연속해서 읽게 됨
1_2. C1 인덱스에서 CLUSTER_BY_ROWID를 사용한 경우
동일한 쿼리에 CLUSTER_BY_ROWID 힌트를 사용함
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
|
SQL>
set timing on
alter session set statistics_level = all;
select /*+ index(t ix_cluster_rowid_c1) cluster_by_rowid(t) */
sum(length(c3))
from t_cluster_rowid t
where c1 between 1 and 100000;
SELECT * FROM DBMS_XPLAN.DISPLAY_CURSOR(NULL, NULL, 'ALLSTATS LAST -rows -Projection +HINT_REPORT +outline');
Plan hash value: 3299769871
----------------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | A-Rows | A-Time | Buffers | OMem | 1Mem | Used-Mem |
----------------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 1 |00:00:00.11 | 1812 | | | |
| 1 | SORT AGGREGATE | | 1 | 1 |00:00:00.11 | 1812 | | | |
| 2 | TABLE ACCESS BY INDEX ROWID BATCHED| T_CLUSTER_ROWID | 1 | 100K|00:00:00.05 | 1812 | | | |
| 3 | SORT CLUSTER BY ROWID | | 1 | 100K|00:00:00.03 | 224 | 2958K| 766K| 2629K (0)|
|* 4 | INDEX RANGE SCAN | IX_CLUSTER_ROWID_C1 | 1 | 100K|00:00:00.01 | 224 | | | |
----------------------------------------------------------------------------------------------------------------------------------------
Outline Data
-------------
/*+
BEGIN_OUTLINE_DATA
IGNORE_OPTIM_EMBEDDED_HINTS
OPTIMIZER_FEATURES_ENABLE('19.1.0')
DB_VERSION('19.1.0')
ALL_ROWS
OUTLINE_LEAF(@"SEL$1")
INDEX_RS_ASC(@"SEL$1" "T"@"SEL$1" ("T_CLUSTER_ROWID"."C1"))
BATCH_TABLE_ACCESS_BY_ROWID(@"SEL$1" "T"@"SEL$1")
CLUSTER_BY_ROWID(@"SEL$1" "T"@"SEL$1" SORT BATCH=NO)
END_OUTLINE_DATA
*/
Predicate Information (identified by operation id):
---------------------------------------------------
4 - access("C1">=1 AND "C1"<=100000)
Hint Report (identified by operation id / Query Block Name / Object Alias):
Total hints for statement: 2 (U - Unused (1))
---------------------------------------------------------------------------
2 - SEL$1 / T@SEL$1
U - cluster_by_rowid(t)
- index(t ix_cluster_rowid_c1)
46 rows selected.
|
인덱스를 읽고 테이블에 접근할 때 약 1600 블록(1812-224)을 소모함
Hint Report에는 CLUSTER_BY_ROWID 힌트가 U로 표시됐지만 실행계획에 SORT CLUSTER BY ROWID Operation이 추가됐고 Outline Data에도 CLUSTER_BY_ROWID가 확인됨
하지만 C1 인덱스에서 읽은 ROWID는 이미 테이블에 저장된 행 순서와 비슷함
ROWID를 다시 정렬해도 테이블 블록 접근 순서가 크게 달라지지 않기 때문에 TABLE ACCESS의 Buffers 감소 효과가 없음
SORT CLUSTER BY ROWID에서 2629K의 메모리를 사용했지만 전체 Buffers는 1812로 동일함
2. CF가 안좋은 인덱스 테스트
2_1. C2 인덱스에서 CLUSTER_BY_ROWID를 사용하지 않은 경우
INDEX와 NO_CLUSTER_BY_ROWID 힌트를 사용하여 C2 인덱스를 사용하되 ROWID 정렬 기능은 사용하지 않도록 함
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
|
SQL>
set timing on
alter session set statistics_level = all;
select /*+ index(t ix_cluster_rowid_c2) no_cluster_by_rowid(t) */
sum(length(c3))
from t_cluster_rowid t
where c2 between 1 and 100;
SELECT * FROM DBMS_XPLAN.DISPLAY_CURSOR(NULL, NULL, 'ALLSTATS LAST -rows -Projection +HINT_REPORT +outline');
Plan hash value: 503128056
-------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | A-Rows | A-Time | Buffers |
-------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 1 |00:00:00.14 | 100K|
| 1 | SORT AGGREGATE | | 1 | 1 |00:00:00.14 | 100K|
| 2 | TABLE ACCESS BY INDEX ROWID BATCHED| T_CLUSTER_ROWID | 1 | 100K|00:00:00.07 | 100K|
|* 3 | INDEX RANGE SCAN | IX_CLUSTER_ROWID_C2 | 1 | 100K|00:00:00.01 | 417 |
-------------------------------------------------------------------------------------------------------------
Outline Data
-------------
/*+
BEGIN_OUTLINE_DATA
IGNORE_OPTIM_EMBEDDED_HINTS
OPTIMIZER_FEATURES_ENABLE('19.1.0')
DB_VERSION('19.1.0')
ALL_ROWS
OUTLINE_LEAF(@"SEL$1")
INDEX_RS_ASC(@"SEL$1" "T"@"SEL$1" ("T_CLUSTER_ROWID"."C2"))
BATCH_TABLE_ACCESS_BY_ROWID(@"SEL$1" "T"@"SEL$1")
END_OUTLINE_DATA
*/
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("C2">=1 AND "C2"<=100)
Hint Report (identified by operation id / Query Block Name / Object Alias):
Total hints for statement: 2
---------------------------------------------------------------------------
2 - SEL$1 / T@SEL$1
- index(t ix_cluster_rowid_c2)
- no_cluster_by_rowid(t)
44 rows selected.
|
인덱스를 읽고 테이블에 접근할 때 약 100K 블록을 소모함
2_2. C2 인덱스에서 CLUSTER_BY_ROWID를 사용한 경우
동일한 쿼리에 CLUSTER_BY_ROWID 힌트를 사용함
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
|
SQL>
set timing on
alter session set statistics_level = all;
select /*+ index(t ix_cluster_rowid_c2) cluster_by_rowid(t) */
sum(length(c3))
from t_cluster_rowid t
where c2 between 1 and 100;
SELECT * FROM DBMS_XPLAN.DISPLAY_CURSOR(NULL, NULL, 'ALLSTATS LAST -rows -Projection +HINT_REPORT +outline');
Plan hash value: 1777193093
----------------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | A-Rows | A-Time | Buffers | OMem | 1Mem | Used-Mem |
----------------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 1 |00:00:00.11 | 2989 | | | |
| 1 | SORT AGGREGATE | | 1 | 1 |00:00:00.11 | 2989 | | | |
| 2 | TABLE ACCESS BY INDEX ROWID BATCHED| T_CLUSTER_ROWID | 1 | 100K|00:00:00.05 | 2989 | | | |
| 3 | SORT CLUSTER BY ROWID | | 1 | 100K|00:00:00.03 | 417 | 2958K| 766K| 2629K (0)|
|* 4 | INDEX RANGE SCAN | IX_CLUSTER_ROWID_C2 | 1 | 100K|00:00:00.01 | 417 | | | |
----------------------------------------------------------------------------------------------------------------------------------------
Outline Data
-------------
/*+
BEGIN_OUTLINE_DATA
IGNORE_OPTIM_EMBEDDED_HINTS
OPTIMIZER_FEATURES_ENABLE('19.1.0')
DB_VERSION('19.1.0')
ALL_ROWS
OUTLINE_LEAF(@"SEL$1")
INDEX_RS_ASC(@"SEL$1" "T"@"SEL$1" ("T_CLUSTER_ROWID"."C2"))
BATCH_TABLE_ACCESS_BY_ROWID(@"SEL$1" "T"@"SEL$1")
CLUSTER_BY_ROWID(@"SEL$1" "T"@"SEL$1" SORT BATCH=NO)
END_OUTLINE_DATA
*/
Predicate Information (identified by operation id):
---------------------------------------------------
4 - access("C2">=1 AND "C2"<=100)
Hint Report (identified by operation id / Query Block Name / Object Alias):
Total hints for statement: 2 (U - Unused (1))
---------------------------------------------------------------------------
2 - SEL$1 / T@SEL$1
U - cluster_by_rowid(t)
- index(t ix_cluster_rowid_c2)
46 rows selected.
|
인덱스를 읽고 테이블에 접근할 때 약 2500 블록(2989-417)만 소모함(100K에 비하면 buffer를 많이 절약함)
Hint Report에는 CLUSTER_BY_ROWID 힌트가 U로 표시됐지만 실행계획에 SORT CLUSTER BY ROWID Operation이 추가됐고 Outline Data에도 CLUSTER_BY_ROWID가 확인됨
SORT CLUSTER BY ROWID에서 정렬 작업이 추가되었기 때문에 Used-Mem이 2629K으로 표시됨
INDEX RANGE SCAN에서 읽은 ROWID를 SORT CLUSTER BY ROWID에서 정렬한 후 테이블에 전달함
따라서 같은 테이블 블록에 저장된 행을 연속해서 처리할 가능성이 높아짐
실행 결과 비교
구분 인덱스 CF CLUSTER_BY_ROWID INDEX RANGE SCAN A-Rows INDEX RANGE SCAN Buffers 테이블 접근 Buffers 전체 Buffers SORT Used-Mem A-Time
C1 인덱스 15,873 미사용 100K 224 1,588 1,812 - 0.09초
C1 인덱스 15,873 사용 100K 224 1,588 1,812 2,629K 0.11초
C2 인덱스 1,000,000 미사용 100K 417 약 100K 약 100K - 0.14초
C2 인덱스 1,000,000 사용 100K 417 2,572 2,989 2,629K 0.11초
| 구분 | 인덱스 CF | CLUSTER_BY_ROWID | INDEX RANGE SCAN A-Rows |
INDEX RANGE SCAN Buffers |
테이블 접근 Buffers | 전체 Buffers | SORT Used-Mem | A-Time |
| C1 인덱스 | 15,873 | 미사용 | 100K | 224 | 1,588 | 1,812 | - | 0.09초 |
| C1 인덱스 | 15,873 | 사용 | 100K | 224 | 1,588 | 1,812 | 2,629K | 0.11초 |
| C2 인덱스 | 1,000,000 | 미사용 | 100K | 417 | 약 100K | 약 100K | - | 0.14초 |
| C2 인덱스 | 1,000,000 | 사용 | 100K | 417 | 2,572 | 2,989 | 2,629K | 0.11초 |
결론 :
CF가 좋은 IX_CLUSTER_ROWID_C1 인덱스는 인덱스 키 순서와 테이블에 저장된 행 순서가 비슷함
CLUSTER_BY_ROWID를 사용하지 않아도 같은 테이블 블록의 행을 연속해서 읽었기 때문에 TABLE ACCESS의 Buffers는 약 1600 소모함
CLUSTER_BY_ROWID를 사용한 경우 SORT CLUSTER BY ROWID Operation이 추가됐지만 TABLE ACCESS의 Buffers는 약 1600으로 동일했음
ROWID 정렬 작업이 추가되면서 실행시간은 0.09초에서 0.11초로 변경됨
CF가 안좋은 IX_CLUSTER_ROWID_C2 인덱스는 인덱스 키 순서와 테이블에 저장된 행 순서가 다름
CLUSTER_BY_ROWID를 사용하지 않은 경우 TABLE ACCESS의 Buffers는 약 100K 소모함
CLUSTER_BY_ROWID를 사용한 경우 SORT CLUSTER BY ROWID Operation이 추가됐고 TABLE ACCESS의 Buffers는 약 2500으로 감소함
INDEX RANGE SCAN의 Buffers는 2배 정도로 늘었지만 테이블에 접근하는 순서가 변경되면서 TABLE ACCESS의 Buffers가 크게 감소함
본문 테스트 결과처럼 CLUSTER_BY_ROWID는 CF가 안좋은 인덱스를 사용하면서 조회 대상 ROWID가 같은 테이블 블록에 여러 개 포함된 경우 Buffers를 줄일 수 있음
반대로 CF가 좋은 인덱스는 인덱스에서 읽은 ROWID가 이미 테이블 블록 순서와 비슷하기 때문에 ROWID를 다시 정렬해도 Buffers 감소 효과가 크지 않을 수 있음
SORT CLUSTER BY ROWID에서 정렬 작업이 추가되므로 CF가 나쁘다는 이유만으로 항상 성능이 좋아지는 것은 아님
대상 SQL의 조회 건수, ROWID 분포, Buffers, Reads, 실행시간과 정렬 작업의 메모리 사용량을 확인한 후 적용해야 함
참조 :
https://jonathanlewis.wordpress.com/2014/02/12/caution-hints/
https://hrjeong.tistory.com/244
'ORACLE > Performance Tuning' 카테고리의 다른 글
| 오라클 19c Batch I/O 동작 및 버퍼 사용량 테스트 (0) | 2026.08.11 |
|---|---|
| 오라클 19c stale percent 변경 및 자동통계수집 (0) | 2026.06.22 |
| 오라클 19c 펜딩 통계(Pending Statistics) (0) | 2026.06.18 |
| 오라클 19c 병렬 ITAS를 이용한 Clustering Factor 개선 테스트 (0) | 2026.06.17 |
| 오라클 19c AWR 스냅샷의 플랜을 SPM(SQL Plan Management)으로 고정 (0) | 2026.05.24 |
