프린트 하기 URL 복사

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, 11= '_'
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