OS환경 : Oracle Linux 7.6 (64bit)
DB 환경 : Oracle Database 19.3.0.0
방법 : 오라클 19c FULL TABLE SCAN (FTS)시 direct path read 이벤트 관련
이전 테스트(group by 테스트) 진행중 테이블 full 스캔을 할때 direct path read 가 발생하는것을 보고 궁금해서 찾아봄
참고 ( 오라클 19c group by 컬럼 나열 및 집계함수 적용 성능 비교 https://positivemh.tistory.com/1034 )
오라클에서 기본적으로 Direct Path I/O 는 아래 경우에만 동작함
- CTAS 작업
- APPEND 힌트를 사용한 insert 작업
- PARALLEL 힌트를 사용한 병렬쿼리 FULL 스캔
- PARALLEL_INDEX 힌트를 사용한 병렬쿼리 INDEX 스캔
- PARALLEL 힌트를 사용한 병렬 DML
- TEMP를 읽고 쓰는 작업
- NOCACHE 옵션으로 LOB 컬럼 읽는 작업
- Datapump 사용시
- 전통 EXP/IMP 중 Direct=Y 옵션 사용시
- SQL*Loader 중 Direct=Y 옵션 사용시
이 경우가 아닌 일반 테이블을 FULL 스캔 할때는
테이블 블록이 데이터 파일에서 버퍼 캐시로 읽혀지기 때문에 db file scattered read 이벤트가 발생해야함
그런데 일반 테이블을 FULL 스캔(병렬X 직렬) 할 때 10046 트레이스를 보니 db file scattered read 가 아닌 direct path read 이벤트가 발생하는걸 발견함
10046 테스트
샘플 테이블 생성
1
2
3
4
5
6
7
8
9
|
SQL>
conn / as sysdba
drop table test_emp;
create table big_emp as select * from emp connect by level <= 6;
select count(*) from test_emp;
COUNT(*)
----------
8108730
|
버퍼캐시 플러쉬
1
2
3
|
SQL> alter system flush buffer_cache;
System altered.
|
테이블 풀스캔 후 10046 트레이스 확인
(오라클 19c 10046 trace 및 tkprof 자동수행 스크립트 https://positivemh.tistory.com/960 활용)
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
|
$ sh 10046.sh
$ cat /ORA19/app/oracle/diag/rdbms/oracle19/oracle19/trace/oracle19_ora_37734_t1_tkprof.txt
********************************************************************************
SQL ID: fqhvntk5d9gbr Plan Hash: 2674116386
select count(*)
from
test_emp
call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.00 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 1 0.10 0.10 47730 47733 0 1
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 3 0.10 0.10 47730 47733 0 1
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 SORT AGGREGATE (cr=47733 pr=47730 pw=0 time=104406 us starts=1)
8108730 8108730 8108730 TABLE ACCESS FULL TEST_EMP (cr=47733 pr=47730 pw=0 time=110225 us starts=1 cost=13110 size=0 card=8108730)
Elapsed times include waiting on following events:
Event waited on Times Max. Wait Total Waited
---------------------------------------- Waited ---------- ------------
PGA memory operation 5 0.00 0.00
SQL*Net message to client 1 0.00 0.00
Disk file operations I/O 1 0.00 0.00
db file sequential read 1 0.00 0.00
direct path read 6003 0.00 0.03 ***
SQL*Net message from client 1 0.00 0.00
*****************************************************1***************************
|
direct path read 이벤트가 많이 발생함
원인은 11g 부터 도입된 _serial_direct_read 파라미터 였음
_serial_direct_read 파라미터는 FULL 스캔시(병렬쿼리가 아닌 직렬 방식) direct path read를 가능하게 제어 해주는 파라미터임
파라미터 값이 auto(기본값) 일 경우 _small_table_threshold 파라미터 값보다 대상 테이블의 blocks(dba_tables) 값이 크다면 Direct Path I/O가 동작함
(여기서 참조하는 blocks 갯수는 _direct_read_decision_statistics_driven 파라미터와도 연관이 있다고함 기본값은 true임)
파라미터 값이 never 일 경우 절대 direct path read를 수행하지 않음
이외에 true, false, always 옵션이 존재함
참고로 _small_table_threshold 파라미터 기본값은 _db_block_buffers 파라미터의 2%임
관련 히든 파라미터 조회
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
|
SQL>
set lines 200 pages 1000
col desc for a70
col name for a40
col current_value for a15
col default_value for a15
col default_t_f for a15
select
ksppinm "name",
ksppstvl "current_value",
b.ksppstdfl "default_value",
b.ksppstdf "default_t_f",
ksppdesc "desc"
from sys.x$ksppi a, sys.x$ksppcv b
where 1=1
and a.indx=b.indx
AND SUBSTR(a.KSPPINM, 1, 1) = '_'
and a.ksppinm in ('_serial_direct_read', '_small_table_threshold',
'_very_large_object_threshold', '_direct_read_decision_statistics_driven',
'_db_block_buffers')
order by 1;
name current_value default_value default_t_f desc
---------------------------------------- --------------- --------------- --------------- ----------------------------------------------------------------------
_db_block_buffers 17622 0 TRUE Number of database blocks cached in memory: hidden parameter
_direct_read_decision_statistics_driven TRUE TRUE TRUE enable direct read decision based on optimizer statistics
_serial_direct_read auto auto TRUE enable direct read in serial
_small_table_threshold 352 TRUE lower threshold level of table size for direct reads
_very_large_object_threshold 500 500 TRUE upper threshold level of object size for direct reads
|
*_db_block_buffers 값이 17622임, _small_table_threshold 값은 352임(_db_block_buffers 17622의 2%임)
파라미터에 따른 변화 테스트
테스트1. _small_table_threshold 값보다 테이블의 블록이 많은 경우
테스트2. _small_table_threshold 값보다 테이블의 블록이 적은 경우
테스트3. _serial_direct_read 값이 never인 경우
샘플 테이블 생성 후 통계 수집
1
2
3
4
5
|
SQL>
conn / as sysdba
drop table test_emp purge;
create table test_emp as select * from emp, dual connect by level <= 6;
exec dbms_stats.gather_table_stats('SYS', 'TEST_EMP');
|
블록 갯수 확인
1
2
3
4
5
6
7
8
9
|
SQL>
col table_name for a20
select table_name, num_rows, blocks
from dba_tables
where table_name = 'TEST_EMP';
TABLE_NAME NUM_ROWS BLOCKS
-------------------- ---------- ----------
TEST_EMP 8108730 50029
|
50029개 블록을 가지고 있음
테스트1. _small_table_threshold 값보다 테이블의 블록이 많은 경우
현재 _serial_direct_read 파라미터는 auto이고, _small_table_threshold 파라미터 값은 352임
_small_table_threshold(352) 보다 test_emp 테이블의 블록(50029)이 더 많은 상태임
버퍼캐시 플러쉬
1
2
3
|
SQL> alter system flush buffer_cache;
System altered.
|
test_emp 테이블 조회 후 direct path read 사용량 확인
1
2
3
4
5
6
7
8
9
10
11
12
|
SQL> select /*+ test1 */ count(*) from test_emp;
COUNT(*)
----------
8108730
select sql_id, substr(sql_text, 1, 30) "sql_text", direct_reads, buffer_gets from v$sql
where sql_text like '%test1%';
SQL_ID sql_text DIRECT_READS BUFFER_GETS
------------- ------------------------------------------------------------ ------------ -----------
4f4cymkj342j2 select /*+ test1 */ count(*) f 50029 50049
|
DIRECT_READS 값이 50029임(Direct Path I/O가 작동함)
버퍼캐시 플러쉬
1
2
3
|
SQL> alter system flush buffer_cache;
System altered.
|
테스트2. _small_table_threshold 값보다 테이블의 블록이 적은 경우
_small_table_threshold 파라미터 50030으로 변경
1
2
3
|
SQL> alter session set "_small_table_threshold" = 50030;
Session altered.
|
현재 _serial_direct_read 파라미터는 auto이고, _small_table_threshold 파라미터 값은 50030임
_small_table_threshold(50030) 보다 test_emp 테이블의 블록(50029)이 더 적은 상태임
test_emp 테이블 조회 후 direct path read 사용량 확인
1
2
3
4
5
6
7
8
9
10
11
12
|
SQL> select /*+ test2 */ count(*) from test_emp;
COUNT(*)
----------
8108730
select sql_id, substr(sql_text, 1, 30) "sql_text", direct_reads, buffer_gets from v$sql
where sql_text like '%test2%';
SQL_ID sql_text DIRECT_READS BUFFER_GETS
------------- ------------------------------------------------------------ ------------ -----------
a0m87v5jnf0u4 select /*+ test2 */ count(*) f 0 50110
|
DIRECT_READS 값이 0임(Direct Path I/O가 미작동함)
테스트3. _serial_direct_read 값이 never인 경우
_small_table_threshold 파라미터 초기값으로 변경 및 _serial_direct_read 파라미터 never 으로 변경
1
2
3
|
SQL>
alter session set "_small_table_threshold" = 352;
alter session set "_serial_direct_read" = never;
|
버퍼캐시 플러쉬
1
2
3
|
SQL> alter system flush buffer_cache;
System altered.
|
test_emp 테이블 조회 후 direct path read 사용량 확인
1
2
3
4
5
6
7
8
9
10
11
12
|
SQL> select /*+ test3 */ count(*) from test_emp;
COUNT(*)
----------
8108730
select sql_id, substr(sql_text, 1, 30) "sql_text", direct_reads, buffer_gets from v$sql
where sql_text like '%test3%';
SQL_ID sql_text DIRECT_READS BUFFER_GETS
------------- ------------------------------------------------------------ ------------ -----------
29xpytcp085n9 select /*+ test3 */ count(*) f 0 50042
|
DIRECT_READS 값이 0임(Direct Path I/O가 미작동함)
_small_table_threshold 값이 352임에도 _serial_direct_read 값이 never로 설정되어 있어서 Direct Path I/O가 미동작함
주의사항
이처럼_serial_direct_read 파라미터가 auto로 설정되어 있을때 테이블 블록이 _small_table_threshold 값보다 큰 경우 Direct Path I/O가 동작함
이 말은 쿼리에서 특정 테이블이 FULL 하는 실행계획으로 풀릴 때 쿼리 수행시마다 캐싱(버퍼캐시)을 전혀 사용하지 않고 매번 Direct Path I/O를 한다는 뜻임
쿼리가 자주 수행되는 경우라면 성능 저하를 겪을 수 있음
참고 : _small_table_threshold 파라미터값 조정을 통한 시스템 성능 향상 사례 ( https://cafe.naver.com/dbian/1673 )
또한 block cleanout 관련 이슈도 발생할 수 있음
참고 : Direct path read 와 block cleanout 그리고 latch: row cache objects ( https://engineering-skcc.github.io/oracle%20tuning/adaptive-direct-path-load/ )
결론 :
_serial_direct_read 파라미터가 auto일때
_small_table_threshold 값보다 대상 테이블의 블록이 더 많은 경우 Direct Path I/O 가 동작함
_small_table_threshold 값보다 대상 테이블의 블록이 더 적은 경우 Direct Path I/O 가 미동작함
_serial_direct_read 파라미터가 never 일때 Direct Path I/O 가 미동작함
direct path read 이벤트가 많이 발생하는 경우 반복적으로 direct path read를 사용하는 FULL 스캔 쿼리가 없는지
, _small_table_threshold 파라미터 값은 적절한지 확인이 필요함
block cleanout 이슈가 발생할 경우 원인 분석 후 변경이 많이 발생되는 테이블들을 대상으로
주기적으로 수동 cleanout 시키는 방법을 고려할 수 있음(non-direct path read 방식으로 FULL 스캔 수행)
참조 :
https://bae9086.tistory.com/418
https://dataonair.or.kr/db-tech-reference/d-lounge/technical-data/?mod=document&uid=235913
https://scidb.tistory.com/entry/Full-Table-Scan-의-비밀
https://scidb.tistory.com/entry/Oracle11g-에서-Full-Table-Scan의-성능-향상
https://cafe.naver.com/dbian/1673
https://engineering-skcc.github.io/oracle%20tuning/adaptive-direct-path-load/
오라클 성능 고도화 원리와 해법 1 456p
https://positivemh.tistory.com/150
https://www.dba-oracle.com/t_large_small_table_threshold.htm
https://www.dba-oracle.com/t_very_large_object_threshold.htm
https://avdeo.com/tag/_small_table_threshold/
https://oracle-tech.blogspot.com/2014/04/smalltablethreshold-and-direct-path.html
https://energ.tistory.com/entry/Serial-Direct-Scan-기능-소개
'ORACLE > Performance Tuning ' 카테고리의 다른 글
오라클 19c 다른 세션 트레이스 설정 (0) | 2024.04.06 |
---|---|
오라클 19c latch: cache buffers chains 이벤트 발생시 핫블록 찾기 (0) | 2024.03.28 |
오라클 19c group by 컬럼 나열 및 집계함수 적용 성능 비교 (0) | 2024.03.19 |
오라클 19c Prefetch, Batch I/O, Table access by rowid batched 설명 (0) | 2024.02.29 |
오라클 19c sqlplus의 statementcache(애플리케이션 커서 캐시) (0) | 2024.02.24 |