1. Call통계
① Parse Call : SQL 처리 루틴 생성 (캐싱 되어 있으면 그것을 사용)
② Execute Call : SQL 실행 요청
③ Fetch Call : 데이터 전송 요청
④ Misses in library cashe during parse : 하드파싱 횟수
2. User Call과 Recursive Call
▶ User Call
- 네트워크를 통해 외부에서 들어오는 Call을 말하며, DBMS입장에서 User Call은 WAS와 API서버를 말함.
- 동시 접속자 수가 적을 때는 잘 느끼지 못하지만, PeakTime에 시스템 장애를 발생시키는 주범
- 집합적 사고를 통해 Loop-Query를 해소하고 One-SQL로 구현해서 해소해야 함
- Array Processing기능 활용
▶ Recursive Call
- SQL파싱과 최적화 과정에서 발생하는 Data Dictionary 조회
- Function, Procedure, Trigger내에서 수행되는 SQL
- 부분범위처리가 가능한 상황에서 제한적으로 사용하거나, 조인 또는 Scalar Subquery 형태로 변환해야 함
3. Database Call 최소화 방안 요약
4. 직접 눈으로 보자!! : 오라클 성능고도화 1권 p366참고하여 테스트 진행
▶ Array Processing 활용 테스트
→ 테스트 Source 테이블 생성
drop table emp purge ;
create table emp
as
select object_id empno, object_name ename, object_type job
, round(dbms_random.value(1000, 5000), -2) sal
, owner deptno, created hiredate
from all_objects , (select rownum rn from dual connect by level <= 15 )
where rownum <= 1000000 ;
→ Target 테이블 생성
drop table emp2 purge ;
create table emp2
as
select * from emp where 1 = 2 ;
→ 테스트 시작 : Arraysize 10으로 Insert 수행
DECLARE
l_fetch_size NUMBER DEFAULT 10; -- 10건씩 Array 처리
CURSOR c IS
SELECT empno, ename, job, sal, deptno, hiredate
FROM emp;
TYPE array_empno IS TABLE OF emp.empno%type;
TYPE array_ename IS TABLE OF emp.ename%type;
TYPE array_job IS TABLE OF emp.job%type;
TYPE array_sal IS TABLE OF emp.sal%type;
TYPE array_deptno IS TABLE OF emp.deptno%type;
TYPE array_hiredate IS TABLE OF emp.hiredate%type;
l_empno array_empno := array_empno ();
l_ename array_ename := array_ename ();
l_job array_job := array_job ();
l_sal array_sal := array_sal ();
l_deptno array_deptno := array_deptno ();
l_hiredate array_hiredate := array_hiredate();
PROCEDURE insert_t( p_empno IN array_empno
, p_ename IN array_ename
, p_job IN array_job
, p_sal IN array_sal
, p_deptno IN array_deptno
, p_hiredate IN array_hiredate ) IS
BEGIN
FORALL i IN p_empno.first..p_empno.last
INSERT INTO emp2
VALUES ( p_empno (i)
, p_ename (i)
, p_job (i)
, p_sal (i)
, p_deptno (i)
, p_hiredate(i) );
EXCEPTION
WHEN others THEN
DBMS_OUTPUT.PUT_LINE(SQLERRM);
RAISE;
END insert_t;
BEGIN
OPEN c;
LOOP
FETCH c BULK COLLECT
INTO l_empno, l_ename, l_job, l_sal, l_deptno, l_hiredate
LIMIT l_fetch_size;
insert_t( l_empno, l_ename, l_job, l_sal, l_deptno, l_hiredate );
EXIT WHEN c%NOTFOUND;
END LOOP;
CLOSE c;
COMMIT;
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
END;
/
→ Arraysize 10의 Trace결과
SELECT EMPNO, ENAME, JOB, SAL, DEPTNO, HIREDATE FROM EMP
Call Count CPU Time Elapsed Time Disk Query Current Rows
------- ------ -------- ------------ ---------- ---------- ---------- ----------
Parse 1 0.000 0.003 0 61 0 0
Execute 1 0.000 0.000 0 0 0 0
Fetch 10001 0.031 0.187 570 10737 0 100000
------- ------ -------- ------------ ---------- ---------- ---------- ----------
Total 10003 0.031 0.190 570 10798 0 100000
INSERT INTO EMP2 VALUES ( :B1 , :B2 , :B3 , :B4 , :B5 , :B6 )
Call Count CPU Time Elapsed Time Disk Query Current Rows
------- ------ -------- ------------ ---------- ---------- ---------- ----------
Parse 1 0.000 0.000 0 0 0 0
Execute 10000 5.016 4.952 0 1824 18585 100000
Fetch 0 0.000 0.000 0 0 0 0
------- ------ -------- ------------ ---------- ---------- ---------- ----------
Total 10001 5.016 4.952 0 1824 18585 100000
→ Arraysize 100으로 Insert 수행
truncate table emp2 ;
DECLARE
l_fetch_size NUMBER DEFAULT 100; -- 100건씩 Array 처리
CURSOR c IS
SELECT empno, ename, job, sal, deptno, hiredate
FROM emp;
TYPE array_empno IS TABLE OF emp.empno%type;
TYPE array_ename IS TABLE OF emp.ename%type;
TYPE array_job IS TABLE OF emp.job%type;
TYPE array_sal IS TABLE OF emp.sal%type;
TYPE array_deptno IS TABLE OF emp.deptno%type;
TYPE array_hiredate IS TABLE OF emp.hiredate%type;
l_empno array_empno := array_empno ();
l_ename array_ename := array_ename ();
l_job array_job := array_job ();
l_sal array_sal := array_sal ();
l_deptno array_deptno := array_deptno ();
l_hiredate array_hiredate := array_hiredate();
PROCEDURE insert_t( p_empno IN array_empno
, p_ename IN array_ename
, p_job IN array_job
, p_sal IN array_sal
, p_deptno IN array_deptno
, p_hiredate IN array_hiredate ) IS
BEGIN
FORALL i IN p_empno.first..p_empno.last
INSERT INTO emp2
VALUES ( p_empno (i)
, p_ename (i)
, p_job (i)
, p_sal (i)
, p_deptno (i)
, p_hiredate(i) );
EXCEPTION
WHEN others THEN
DBMS_OUTPUT.PUT_LINE(SQLERRM);
RAISE;
END insert_t;
BEGIN
OPEN c;
LOOP
FETCH c BULK COLLECT
INTO l_empno, l_ename, l_job, l_sal, l_deptno, l_hiredate
LIMIT l_fetch_size;
insert_t( l_empno, l_ename, l_job, l_sal, l_deptno, l_hiredate );
EXIT WHEN c%NOTFOUND;
END LOOP;
CLOSE c;
COMMIT;
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
END;
/
→ Arraysize 100의 Trace결과
SELECT EMPNO, ENAME, JOB, SAL, DEPTNO, HIREDATE FROM EMP
Call Count CPU Time Elapsed Time Disk Query Current Rows
------- ------ -------- ------------ ---------- ---------- ---------- ----------
Parse 1 0.000 0.000 0 0 0 0
Execute 1 0.000 0.000 0 0 0 0
Fetch 1001 0.047 0.061 0 1806 0 100000
------- ------ -------- ------------ ---------- ---------- ---------- ----------
Total 1003 0.047 0.061 0 1806 0 100000
INSERT INTO EMP2 VALUES ( :B1 , :B2 , :B3 , :B4 , :B5 , :B6 )
Call Count CPU Time Elapsed Time Disk Query Current Rows
------- ------ -------- ------------ ---------- ---------- ---------- ----------
Parse 1 0.000 0.000 0 0 0 0
Execute 1000 0.547 0.595 2 1791 8553 100000
Fetch 0 0.000 0.000 0 0 0 0
------- ------ -------- ------------ ---------- ---------- ---------- ----------
Total 1001 0.547 0.595 2 1791 8553 100000
→ 위 Trace결과의 비교 분석
- Array Processing 활용으로 Fetch Call이 감소 되었음
- Array Processing의 효과를 극대화 하려면 연속된 일련의 처리과정이 모두 Array 단위로 진행되어야 함
- 예를들면, 최적의 Array단위로 앞서 Fetch를 하더라도, Insert단계에서 건건히 처리된다면 그 효과는 크게 반감됨
- 그렇다면 Array단위는 항상 크게 하는것이 좋을까?
→ ArraySize별 Fetch / IO갯수 확인
set autotrace traceonly statistics;
set timing on
set arraysize 2
select * from emp ;
set arraysize 5
select * from emp ;
set arraysize 10
select * from emp ;
set arraysize 15
select * from emp ;
set arraysize 100
select * from emp ;
set arraysize 5000
select * from emp ;
→ ArraySize 별 Fetch Count와 Block IO갯수 변화량
- 대량의 데이터를 Fetch할 때 ArraySize를 크게 설정하면 Fetch Call과 Block IO도 감소
- Fetch사이즈가 작으면 한번의 Fetch 완료 후 다시 데이터를 읽을 때 이 전에 읽었던 블록에 아직 읽어야 할 데이터가 있을 경우 동일 블록을 반복해서 읽기 때문
- ArraySize와 Block IO는 반비례하지만, 일정량이 넘어가면 더이상 감소하지 않음
- 일정수치가 넘어갈 경우 IO감소는 되지 않은 채 한번에 처리해야하는 양이 많아져 메모리부하 발생
'DB' 카테고리의 다른 글
OracleDB IOT로 MariaDB/MySQL 인덱스 이해하기 (0) | 2022.06.19 |
---|---|
Oracle Index Skip Scan의 효용성 (0) | 2022.06.12 |