OS환경 : Oracle Linux 7.6 (64bit)
DB 환경 : Oracle Database 19.3.0.0
방법 : 오라클 19c 신규인덱스 생성시 기존인덱스 이용
다른 테스트를 하던 중 10046 트레이스를 보다보니 인덱스 생성시에 테이블 FULL SCAN을 하는것을 보고
인덱스 생성시에도 인덱스를 이용할수 있지 않을까 생각이 들어 테스트를 진행해봄
테스트1. 아무 인덱스가 없는 상태에서 id1, id2로 인덱스 생성
테스트2. 기존 인덱스(id1, id2)가 존재하는 상태에서 id1만으로 인덱스 생성(완전중복 인덱스)
테스트3. 기존 인덱스(id1, id2)가 존재하는 상태에서 id1, id2, name으로 인덱스 생성
샘플 대용량 테이블 생성 및 데이터 삽입
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
|
SQL>
conn imsi/imsi
drop table sample_t purge;
create table sample_t
(id1 number, id2 number, name varchar2(10),
date1 varchar2(8), date2 varchar2(8), date3 date,
phone varchar2(13), price number, qty number,
test1 number, test2 varchar2(5), test3 varchar2(4)
)
nologging;
SQL>
DECLARE
TYPE tbl_ins IS TABLE OF sample_t%ROWTYPE INDEX BY BINARY_INTEGER;
w_ins tbl_ins;
BEGIN
FOR d IN 1..10 LOOP
FOR i IN 1..1000000 LOOP
w_ins(i).id1 := i;
w_ins(i).id2 := i||ceil(dbms_random.value(1, 10000000));
w_ins(i).name := dbms_random.string('x',5);
w_ins(i).date1 := round(dbms_random.value(2010,2021))||to_char(round(dbms_random.value(1,12)), 'FM09')||to_char( round(dbms_random.value(1,28)), 'FM09');
w_ins(i).date2 := '2021'||to_char(round(dbms_random.value(1,12)) , 'FM09')||to_char(round(dbms_random.value(1,28)), 'FM09');
w_ins(i).date3 := to_date(round(dbms_random.value(2019,2021))||'-'||round(dbms_random.value(1,12))||'-'||round(dbms_random.value(1,28))||' '||round(dbms_random.value(1, 23))||':'||round(dbms_random.value(0, 59))||':'||round(dbms_random.value(0, 59)), 'YYYY-MM-DD HH24:MI:SS');
w_ins(i).phone := '010-'||ceil(dbms_random.value(1000, 9999))||'-'||ceil(dbms_random.value(1000, 9999));
w_ins(i).price := ceil(dbms_random.value(1, 10))*1000;
w_ins(i).qty := ceil(dbms_random.value(1, 10));
w_ins(i).test1 := 1234;
w_ins(i).test2 := 'SQLP';
w_ins(i).test3 := 'A'||ceil(dbms_random.value(100, 999));
END LOOP;
FORALL i in 1..1000000 INSERT INTO sample_t VALUES w_ins(i);
COMMIT;
END LOOP;
END;
/
|
용량 확인
1
2
3
4
5
6
7
8
|
SQL>
col segment_name for a20
select segment_name, bytes/1024/1024 mb from dba_segments
where segment_name = 'SAMPLE_T';
SEGMENT_NAME MB
-------------------- ----------
SAMPLE_T 912
|
test1 테이블 생성
1
2
3
|
SQL>
drop table test1 purge;
create table test1 as select * from sample_t;
|
테스트1. 아무 인덱스가 없는 상태에서 id1, id2로 인덱스 생성
1
2
3
4
5
6
7
8
9
10
11
|
SQL> alter session set events '10046 trace name context forever, level 12';
Session altered.
SQL> create index test1_ix01 on test1(id1, id2);
Index created.
SQL> alter session set events '10046 trace name context off';
Session altered.
|
생성된 trace 확인
1
2
3
4
5
6
7
8
9
10
|
SQL>
set lines 200 pages 1000
col name for a20
col value for a80
select name, value from v$diag_info
where name = 'Default Trace File';
NAME VALUE
-------------------- --------------------------------------------------------------------------------
Default Trace File /ORA19/app/oracle/diag/rdbms/oracle19/oracle19/trace/oracle19_ora_35647.trc
|
tkprof 실행
1
2
3
4
5
|
$ tkprof /ORA19/app/oracle/diag/rdbms/oracle19/oracle19/trace/oracle19_ora_35647.trc oracle19_ora_35647_t1_tkprof.txt sys=no
TKPROF: Release 19.0.0.0.0 - Development on Sat Feb 17 16:11:46 2024
Copyright (c) 1982, 2019, Oracle and/or its affiliates. All rights reserved.
|
트레이스 확인
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
|
$ vi /ORA19/app/oracle/diag/rdbms/oracle19/oracle19/trace/oracle19_ora_35647_t1_tkprof.txt
SQL ID: 8v4nxz0687c3a Plan Hash: 3511723362
create index test1_ix01 on test1(id1, id2)
call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.00 0 6 10 0
Execute 1 6.43 7.60 156870 117815 38673 0
Fetch 0 0.00 0.00 0 0 0 0
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 2 6.44 7.61 156870 117821 38683 0
Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 78
Number of plan statistics captured: 1
Rows (1st) Rows (avg) Rows (max) Row Source Operation
---------- ---------- ---------- ---------------------------------------------------
1 1 1 INDEX BUILD NON UNIQUE test1_IX01 (cr=117826 pr=156870 pw=74014 time=7608606 us starts=1)(object id 0)
10000000 10000000 10000000 SORT CREATE INDEX (cr=117517 pr=156869 pw=39372 time=4525331 us starts=1)
10000000 10000000 10000000 TABLE ACCESS FULL TEST1 (cr=117517 pr=117497 pw=0 time=1101942 us starts=1 cost=32144 size=140000000 card=10000000)
Elapsed times include waiting on following events:
Event waited on Times Max. Wait Total Waited
---------------------------------------- Waited ---------- ------------
index (re)build lock or pin object 4 0.00 0.00
db file scattered read 938 0.00 0.37
PGA memory operation 279 0.00 0.00
Disk file operations I/O 1 0.00 0.00
direct path write temp 1405 0.00 0.61
direct path read temp 1539 0.00 0.03
db file sequential read 1 0.00 0.00
direct path write 8863 0.00 1.36
log file sync 1 0.00 0.00
SQL*Net message to client 1 0.00 0.00
SQL*Net message from client 1 0.00 0.00
********************************************************************************
|
당연하게도 인덱스 생성을 위해 테이블을 FULL SCAN 함
트레이스 설정을 위해 sqlplus 세션 재접속
테스트2. 기존 인덱스(id1, id2)가 존재하는 상태에서 id1만으로 인덱스 생성(완전중복 인덱스)
1
2
3
4
5
6
7
8
9
10
11
|
SQL> alter session set events '10046 trace name context forever, level 12';
Session altered.
SQL> create index test1_ix02 on test1(id1);
Index created.
SQL> alter session set events '10046 trace name context off';
Session altered.
|
생성된 trace 확인
1
2
3
4
5
6
7
8
9
10
|
SQL>
set lines 200 pages 1000
col name for a20
col value for a80
select name, value from v$diag_info
where name = 'Default Trace File';
NAME VALUE
-------------------- --------------------------------------------------------------------------------
Default Trace File /ORA19/app/oracle/diag/rdbms/oracle19/oracle19/trace/oracle19_ora_35857.trc
|
tkprof 실행
1
2
3
4
5
|
$ tkprof /ORA19/app/oracle/diag/rdbms/oracle19/oracle19/trace/oracle19_ora_35857.trc oracle19_ora_35857_t2_tkprof.txt sys=no
TKPROF: Release 19.0.0.0.0 - Development on Sat Feb 17 16:14:13 2024
Copyright (c) 1982, 2019, Oracle and/or its affiliates. All rights reserved.
|
트레이스 확인
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
|
$ vi /ORA19/app/oracle/diag/rdbms/oracle19/oracle19/trace/oracle19_ora_35857_t2_tkprof.txt
SQL ID: c90udqyf4uuq4 Plan Hash: 3711363751
create index test1_ix02 on test1(id1)
call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.00 0 2 0 0
Execute 1 7.57 8.47 64141 10000365 25791 0
Fetch 0 0.00 0.00 0 0 0 0
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 2 7.57 8.47 64141 10000367 25791 0
Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 78
Number of plan statistics captured: 1
Rows (1st) Rows (avg) Rows (max) Row Source Operation
---------- ---------- ---------- ---------------------------------------------------
1 1 1 INDEX BUILD NON UNIQUE test1_IX02 (cr=10000375 pr=64141 pw=51825 time=8475986 us starts=1)(object id 0)
10000000 10000000 10000000 SORT CREATE INDEX (cr=10000101 pr=64141 pw=29499 time=6195909 us starts=1)
10000000 10000000 10000000 INDEX FAST FULL SCAN TEST1_IX01 (cr=10000101 pr=34642 pw=0 time=3547557 us starts=1)(object id 26906)
Elapsed times include waiting on following events:
Event waited on Times Max. Wait Total Waited
---------------------------------------- Waited ---------- ------------
PGA memory operation 314 0.00 0.00
index (re)build lock or pin object 4 0.00 0.00
Disk file operations I/O 3 0.00 0.00
db file scattered read 288 0.00 0.02
direct path write temp 1087 0.00 0.28
direct path read temp 1152 0.00 0.02
direct path write 5704 0.00 0.84
log file sync 1 0.00 0.00
SQL*Net message to client 1 0.00 0.00
SQL*Net message from client 1 0.00 0.00
********************************************************************************
|
인덱스 생성시 테이블 FULL SCAN이 아닌 TEST1_IX01 인덱스를 사용해 INDEX FAST FULL SCAN을 함
인덱스 생성시 필요한 컬럼(id1)이 TEST1_IX01에 모두 포함되어 있기 때문에 테이블을 굳이 엑세스하지 않고 기존 인덱스만 읽어 신규 인덱스를 생성한것으로 보임
트레이스 설정을 위해 sqlplus 세션 재접속
테스트3. 기존 인덱스(id1, id2)가 존재하는 상태에서 id1, id2, name으로 인덱스 생성
1
2
3
4
5
6
7
8
9
10
11
|
SQL> alter session set events '10046 trace name context forever, level 12';
Session altered.
SQL> create index test1_ix03 on test1(id1, id2, name);
Index created.
SQL> alter session set events '10046 trace name context off';
Session altered.
|
생성된 trace 확인
1
2
3
4
5
6
7
8
9
10
|
SQL>
set lines 200 pages 1000
col name for a20
col value for a80
select name, value from v$diag_info
where name = 'Default Trace File';
NAME VALUE
-------------------- --------------------------------------------------------------------------------
Default Trace File /ORA19/app/oracle/diag/rdbms/oracle19/oracle19/trace/oracle19_ora_35883.trc
|
tkprof 실행
1
2
3
4
5
|
$ tkprof /ORA19/app/oracle/diag/rdbms/oracle19/oracle19/trace/oracle19_ora_35883.trc oracle19_ora_35883_t3_tkprof.txt sys=no
TKPROF: Release 19.0.0.0.0 - Development on Sat Feb 17 16:17:00 2024
Copyright (c) 1982, 2019, Oracle and/or its affiliates. All rights reserved.
|
트레이스 확인
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
|
$ vi /ORA19/app/oracle/diag/rdbms/oracle19/oracle19/trace/oracle19_ora_35883_t3_tkprof.txt
SQL ID: 4xrwwbk8mp9tz Plan Hash: 3536447883
create index test1_ix03 on test1(id1, id2, name)
call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.00 0 1 1 0
Execute 1 5.95 7.43 160658 117830 41333 0
Fetch 0 0.00 0.00 0 0 0 0
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 2 5.95 7.44 160658 117831 41334 0
Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 78
Number of plan statistics captured: 1
Rows (1st) Rows (avg) Rows (max) Row Source Operation
---------- ---------- ---------- ---------------------------------------------------
0 0 0 INDEX BUILD NON UNIQUE test1_IX03 (cr=0 pr=0 pw=0 time=8 us starts=1)(object id 0)
8746979 8746979 8746979 SORT CREATE INDEX (cr=117517 pr=160650 pw=49252 time=4157609 us starts=1)
10000000 10000000 10000000 TABLE ACCESS FULL TEST1 (cr=117517 pr=117497 pw=0 time=654757 us starts=1 cost=32157 size=200000000 card=10000000)
Elapsed times include waiting on following events:
Event waited on Times Max. Wait Total Waited
---------------------------------------- Waited ---------- ------------
PGA memory operation 327 0.00 0.00
index (re)build lock or pin object 2 0.00 0.00
asynch descriptor resize 1 0.00 0.00
Disk file operations I/O 2 0.00 0.00
db file scattered read 938 0.00 0.08
direct path write temp 1660 0.00 0.52
direct path read temp 1684 0.00 0.04
direct path write 9625 0.00 1.47
db file sequential read 8 0.00 0.00
reliable message 6 0.00 0.00
enq: RO - fast object reuse 3 0.00 0.00
enq: CR - block range reuse ckpt 3 0.00 0.00
log file sync 1 0.00 0.00
SQL*Net break/reset to client 2 0.00 0.00
SQL*Net message to client 1 0.00 0.00
SQL*Net message from client 1 0.00 0.00
********************************************************************************
|
인덱스 생성시 테스트2 처럼 TEST1_IX01 인덱스를 사용하지 않고 테이블 FULL SCAN을 함
인덱스 생성시 name 이라는 신규 컬럼이 추가 되었기 때문에 index ffs 로는 처리할수 없어서 FULL SCAN이 동작한것으로 보임
결론 :
위 경우와 같이 기존에 존재하는 인덱스의 선두 컬럼들을 이용해 신규인덱스 생성 하는 경우
테이블 스캔을 하지 않고 기존 인덱스를 index fast full scan 방식으로 읽어 신규 인덱스를 생성함
하지만 이 방법은 완전중복 인덱스를 생성하는 방법이기 때문에 실무에 큰 도움이 되지는 않을듯 함
참조 : 친절한 SQL 튜닝