2. - 1일차 : Tuning 도구
- D/B Tuning이 어려운 이유
- Oracle D/B 구조
- Tuning Tools : AutoTrace, SQL*Trace, V$SQLAREA
- 2일차 : SQL Tuning #1 : Optimizer
- Rule-based Optimizer
- Cost-based Optimizer
- 통계정보, Histogram
- Hint
- 3일차 : SQL Tuning #2 : Index, Join
- Index
- Join
- 4일차 : Server Tuning
- Shared pool tuning
- Data buffer cache tuning
* NoSQL 소개
3. Optimizer
Execution Plan 을 결정하는 Algorithm
Rule-based optimizer
- 융통성 없는 녀석. 원리 원칙대로만 수행
- v6까지의 default
Cost-based optimizer
- 유도리있게 통계정보를 보고 잴 빠른 경우를 찾는
다고는 하지만, 실상은 무식하게 모든 경우의 수를 시도
- v7 이후 지금까지 default
5. Optimizer Mode
Mode description
RULE Rule-based 로 적용 (나머지는 모두 Cost-based)
CHOOSE
모든 Data를 빨리 조회하기를 원할 경우
ALL_ROWS
FIRST_ROWS
존재 유무 확인이 중요한 경우 (일단 1줄의 결과를 가장 빨리 조회)
FIRST_ROWS_1
FIRST_ROWS_10
각각의 숫자만큼의 부분범위 처리 결과를 빠르게 처리
FIRST_ROWS_100
FIRST_ROWS_1000
FIRST_ROWS_10000
6. SQL Processing Overview
1. Query 실행
2. Parser
3. Optimizer (Rule or Cost)
4. Optimal Plan : 최적 경로를 위한 경우의 수들
5. Execution Plan : Optimal Plan 중 최적의 Plan
6. Sql Execution Engine
7. 실행 결과 Fetch
7. Rule-based Optimizer
우선 순위 높은 규칙으로 무조건 무조건이야~
1. ROWID에 의한 Single Record Scan
2. Cluster-Join에 의한 Single Record Scan
3. Unique-Key, Primary-Key를 사용한 Hash-Cluster Key에 의한 Single Record Scan
4. Unique-Key, Primary-Key에 의해 생성된 Index를 통한 Equal Scan
5. Cluster-Join
6. Hash-Cluster Key
7. Indexed Cluster-Key
8. Composite Index
9. Single Column Index에 의한 Equal Scan
10.Indexed Column에 대한 Limited Range Scan (BETWEEN, LIKE, < , > ...)
11.Indexed Column에 대한 Unlimited Range Scan
12.Sort-Merge Join
13.Indexed Column에 대한 MIN, MAX
14.Indexed Column에 대한 ORDER BY
15.Full-Table Scan
8. Rule-based Optimizer의 예외조건
9. Single Column Index에 의한 Equal Scan 인 조건이 여러 개라면 ???
-> 모두 다 실행
11. Indexed Column에 대한 Unlimited Range Scan 인 조건이 여러 개라면 ???
-> 나중에 만들어진 Index 선택 (가장 최근 것이 최신 유행이라 판단)
* 하나의 Column에 여러 가지 조건이 있으면 ???
-> 가장 안 좋은 조건으로 검색
* WHERE의 AND 조건들 : WHERE에서 가까운 것 (왼쪽) 부터 검색
* WHERE의 OR 조건들 ( = IN ) : WHERE에서 먼 것 (오른쪽) 부터 검색
IN은 해당 항목들의 OR로 Optimizer에서 해석함
* SELECT … WHERE ( A OR B OR C ) AND D
-> SELECT … WHERE C AND D , SELECT … WHERE B AND D , SELECT … WHERE A AND D
9. Cost-based Optimizer
1. Query에서 사용된 Table, Index의 통계정보를 수집
- 통계정보가 지금이랑은 좀마니 다른 옜날꺼라면… X망….
- 만약 통계정보가 없다면… default 통계값으로 그냥 계산 (당연히 현실과는 차이가…)
2. 통계값을 기반으로 가능한 Optimal Plan 들의 Cost를 계산
- 만약 경우의 수가 어마무시하게 많다면…
다하긴 귀찮고… 몇 개만 해보고 ….
비스무리 한건 대충 넘기고…
몇 개 해보다가 값이 대충 거서 거라면 그 중 잴 나은 녀석으로…
3. 가장 Cost가 작은 Plan으로 선택
10. Cost-based Optimizer 구조
1. Query Transformer
- Parsing된 SQL을 변경 (애매모호하거나, 잘못 작성된 SQL문)
ex) hiredate = ‘2015-06-19’ => hiredate TO_DATE(‘2015-06-19’)
2. Estimator
- Cost 만 계산
- I/O Cost : 통계정보를 이용하여 Disk I/O의 Cost를 계산 ( 90 ~ 95%)
- CPU Cost : CPU 상태에 따른 Cost를 계산 (5 ~ 10% , 10g 이후)
3. Plan Generator
- 가장 Cost가 적은 Query + Estimate에 대한 Execution Plan을 생성
11. 통계정보
ANALYZE TABLE [table_name] COMPUTE STATISTICS
ANALYZE INDEX [index_name] COMPUTE STATISTICS
- Table의 통계정보를 생성하면 Index도 같이 생성됨
단, 그 이후에 생성한 Index에 대해서는 별도로 생성 해야함
9i : User가 직접 수집 (ANALYZE TABLE …)
DBMS_JOB + DBMS_STAT 를 이용하여 작업이 가능
10g : Server가 자동으로 수집해 주지만,
우리가 필요한 시점에 안되었을 수도 있고,
너무 예전의 통계정보 일 수도 있고,
많은 DML 문 실행 직후에는 현실과 다른 정보 일수도 있다.
필요하다면 User가 직접 수집해주어야 한다.
11g : 통계정보의 지연 적용이 가능
통계정보 수집 -> Pending Area에 저장됨
User는 Pending Area와 예전 통계 정보 중 뭐가 더 유리한지 검토 후
Pending Area의 정보를 통계정보로 반영시키거나 삭제 가능
12. Cost-based Optimizer 문제점
1. Data 분포도 문제 : 30% ( 모든 Data의 분포도가 동일하다고 가정 -> Histogram 적용 )
2. 잘못된 통계정보로 분석 : 25%
3. Join 순서 및 Join 방법의 선택 : 20% ( : 경우의 수가 너무 많으면 대충 하다가 cut-off)
4. 잘못된 Index 선택 : 20%
5. 너무 많은 Table Join : < 5%
6. init.ora 의 잘못된 설정값 : < 5%
13. Histogram
ANALYZE TABLE [table_name] COMPUTE STATISTICS
FOR TABLE
FOR COLUMNS [column_name] SIZE [n]
FOR ALL INDEXED COLUMNS
Distinct data 수 ( 1 ~ 254 : default 75 )
(USER_TAB_COLUMNS 의 NUM_DISTINCT)
Histogram이 필요없는 경우
- Bind 변수 사용
- 균등하게 분포된 경우
- Column 값이 Unique한 경우 (물론 이러면 균등하니깐…)
- Rule-based 이거나 Hint 절을 사용할 경우 (물론 이러면 Cost-based로 Optimizer가 계산을 안하니깐…)
14. Hint
대부분의 SQL문 (95%이상)은 Optimizer가 해결해준다.
But, 나머지 5% 이하의 SQL문이 문제가 된다면 ???
-> 사람이 직접하자. 뭐로 ? HINT로 !
HINT를 줬는데도 그렇게 안한다면 ?
- 오타가 있을 경우
- Rule-based 전용 HINT와 Cost-based 전용 HINT를 같이 쓴 경우
- 사용된 HINT 중 최우선 순위 HINT가 나머지를 무시하는 경우
15. Hint
1. RULE
Rule-based Optimizer를 적용
SELECT /*+RULE*/ ENAME FROM BIG_EMP
WHERE DEPTNO = 20 AND EMPNO BETWEEN 100 AND 200 ORDER BY ENAME;
2. FIRST_ROWS
Cost-based Optimizer로 조건을 만족하는 첫 번째 행을 가장 빠르게 검색
SELECT /*+FIRST_ROWS*/ ENAME FROM BIG_EMP WHERE EMPNO = 7934 ORDER BY ENAME;
16. Hint
3. ALL_ROWS
Cost-based Optimizer로 모든 행을 가장 빠르게 검색
SELECT /*+ALL_ROWS*/ ENAME FROM BIG_EMP
WHERE DEPTNO = 20 AND EMPNO BETWEEN 100 AND 200 ORDER BY ENAME;
4. FULL
Full Table Scan 으로 검색
SELECT /*+FULL(BIG_EMP)*/ ENAME FROM BIG_EMP
WHERE DEPTNO = 20 AND EMPNO BETWEEN 100 AND 200 ORDER BY ENAME;
17. Hint
5. ROWID
ROWID로 직접 검색
6. INDEX
원하는 INDEX를 반드시 사용하여 검색
SELECT /*+ROWID(EMP)*/ * FROM EMP WHERE ROWID = 'AAASbSAABAAAVypAAA';
SELECT /*+INDEX(EMP EMP_DEPTNO)*/ ENAME FROM EMP WHERE DEPTNO = 10;
18. Hint
7. NO_INDEX
해당 INDEX를 사용하지 않고 검색
8. INDEX_DESC
원하는 INDEX를 내림차순으로 검색
SELECT /*+NO_INDEX(EMP EMP_DEPTNO)*/ ENAME FROM EMP WHERE DEPTNO = 10;
SELECT /*+INDEX_DESC(EMP EMP_DEPTNO)*/ ENAME FROM EMP WHERE DEPTNO = 10;
19. Hint
9. INDEX_ASC
원하는 INDEX를 오름차순으로 검색
10. INDEX_FFS
INDEX FAST FULL SCAN을 이용하여 INDEX만을 검색
SELECT /*+INDEX_ASC(EMP EMP_DEPTNO)*/ ENAME FROM EMP WHERE DEPTNO = 10;
SELECT /*+INDEX_FFS(EMP EMP_EMPNO)*/ EMPNO FROM EMP WHERE EMPNO > 200;
20. Hint
11. USE_CONCAT
OR문의 검색 조건을 각각 분리하여 검색한 후 CONCATENATION으로 결합
12. NO_EXPAND
OR절로 분리된 동일한 실행계획을 하나의 실행계획으로 반복 수행
SELECT /*+RULE USE_CONCAT*/ * FROM EMP WHERE EMPNO > 50 OR SAL < 50000;
SELECT /*+INDEX(EMP EMP_EMPNO) NO_EXPAND*/ * FROM EMP WHERE EMPNO = 7369 OR EMPNO = 7934;
21. Hint
13. NO_MERGE
MERGE JOIN을 사용하지 않고, Sub-query를 VIEW 구조로 변형한 다음 검색
14. MERGE
Sub-query를 VIEW 구조로 변형하지 않고 MERGE JOIN으로 검색
SELECT /*+NO_MERGE(V)*/ E1.ENAME, E1.SAL, V.AVG_SAL
FROM EMP E1, (SELECT DEPTNO, AVG(SAL) AVG_SAL FROM EMP E2 GROUP BY DEPTNO) V
WHERE E1.DEPTNO = V.DEPTNO AND E1.SAL > V.AVG_SAL;
SELECT /*+MERGE(V)*/ E1.ENAME, E1.SAL, V.AVG_SAL
FROM EMP E1, (SELECT DEPTNO, AVG(SAL) AVG_SAL FROM EMP E2 GROUP BY DEPTNO) V
WHERE E1.DEPTNO = V.DEPTNO AND E1.SAL > V.AVG_SAL;
22. Hint
15. LEADING
가장 먼저 JOIN에 참여하는 Driving Table을 지정하여 검색
SELECT /*+LEADING(DEPT)*/ EMP.EMPNO, DEPT.DEPTNO
FROM EMP, DEPT
WHERE EMP.DEPTNO = DEPT.DEPTNO;