# DBMS_XPLAN
[실행 계획 예측]
DBMS_XPLAN Package 는 Oracle 9i 에서 소개되었으며 Version Up 에 따라 기능이 점점 확장되고 있다.
Package 이름이 암시하는 것처럼, 이 Package 의 기본 목적은 실행 계획의 예측을 보여주는 것이다.
- 기본적인 사용법
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | SCOTT@ora10g>drop table t1 purge;Table dropped.SCOTT@ora10g>create table t1(c1 int, c2 char(10));Table created.SCOTT@ora10g>insert into t1 2 select level, 'dummy' 3 from dual 4 connect by level commit;Commit complete.SCOTT@ora10g>create index t1_n1 on t1(c1);Index created. |
EXPLAIN PLAN 명령과 DBMS_XPLAN.DISPLAY 호출의 조합으로 실행 계획을 보여준다.
가장 기본적인 방법은 다음과 같다.
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 | SCOTT@ora10g>explain plan for 2 select * 3 from t1 4 where c1 = 1 and c2 = 'dummy' 5 ;Explained.SCOTT@ora10g>select * from table(dbms_xplan.display);--------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|--------------------------------------------------------------------------| 0 | SELECT STATEMENT | | 1 | 25 | 2 (0)||* 1 | TABLE ACCESS BY INDEX ROWID| T1 | 1 | 25 | 2 (0)||* 2 | INDEX RANGE SCAN | T1_N1 | 1 | | 1 (0)|--------------------------------------------------------------------------Predicate Information (identified by operation id):--------------------------------------------------- 1 - filter("C2"='dummy') 2 - access("C1"=1)Note----- - dynamic sampling used for this statement18 rows selected. |
# DBMS_XPLAN.DISPLAY Function 은 기본적을 다음과 같은 세 가지 종류의 값을 보여준다.
* Row Source Operation 을 보여준다.
위의 예에서는 Index t1_n1 을 INDEX RANGE SCAN 으로 Access 하는 실행 계획이 수립될 것임을 보여준다.
* Predicate 정보를 보여준다.
2번 Operation, 즉 Index t1_n1 에 대한 Range Scan 단계에서는 access("C1"=1) Predicate 가 사용되었다.
Index Access 를 통해서 걸러진 Data 는 1번 단계, 즉 TABLE ACCESS BY INDEX ROWID Operation 에서 filter("C2"='dummy') Predicate 를 이용해 다시 Filtering 된다.
Predicate 정보는 이를 이해하지 못하면 Execution Plan 의 절반 밖에 이해하지 못한다고 할 정도로 중요한 정보이다.
* Note 정보를 통해 부가적으로 필요한 정보를 제공한다.
이 예제에서는 Dynamic Sampling 이 사용되었음을 알려 준다.
Oracle 10g 에서는 통계 정보가 없는 Table 에 대해서 Dynamic Sampling 을 수행한다.
# Access Predicate 와 Filter Predicate 의 차이는 다음과 같다.
* Access Predicate 는 Access Type을 결정하는데 사용되는 Predicate(조건)를 의미한다.
더 정확하게 말하면 실제 Block 을 읽기 전에 어떤 방법으로 Block 을 읽을 것인가를 결정한다는 의미이다.
따라서 Index Lookup 이나 Join 등은 Access Predicate 로 표현된다.
* Filter Predicate 는 실제 Block 을 읽은 후 Data 를 걸러 내기 위해 사용되는 Predicate(조건)을 의미한다.
# Access Predicate 와 Filter Predicate 가 표현되는 방식에 대한 정확한 이해는 실행 계획 해석에 있어 필수적인 지식이다.
다음 예제를 보면 Access Predicate 와 Filter Predicate 가 어떻게 표현되는지 잘 알 수 있다.
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 | SCOTT@ora10g>create table t1(c1 int, c2 int);Table created.SCOTT@ora10g>create table t2(c1 int, c2 int);Table created.SCOTT@ora10g>create index t1_n1 on t1(c1);Index created.SCOTT@ora10g>create index t1_n2 on t2(c1);Index created.SCOTT@ora10g>insert into t1 select level, level 2 from dual 3 connect by level insert into t2 select level, level 2 from dual 3 connect by level explain plan for 2 select /*+ use_nl(t1 t2) */ 3 * 4 from t1, t2 5 where t1.c1 = t2.c1 6 and t1.c2 = 1 7 ;Explained.SCOTT@ora10g>select * from table(dbms_xplan.display);--------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|--------------------------------------------------------------------------| 0 | SELECT STATEMENT | | 1 | 52 | 5 (0)|| 1 | TABLE ACCESS BY INDEX ROWID| T2 | 1 | 26 | 2 (0)|| 2 | NESTED LOOPS | | 1 | 52 | 5 (0)||* 3 | TABLE ACCESS FULL | T1 | 1 | 26 | 3 (0)||* 4 | INDEX RANGE SCAN | T1_N2 | 1 | | 1 (0)|--------------------------------------------------------------------------Predicate Information (identified by operation id):--------------------------------------------------- 3 - filter("T1"."C2"=1) 4 - access("T1"."C1"="T2"."C1") |
반면 Hash Join 에서는 Join 단계(1번)에서 Access Predicate 정보가 출력된다.
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 | SCOTT@ora10g>explain plan for 2 select /*+ use_hash(t1 t2) */ 3 * 4 from t1, t2 5 where t1.c1 = t2.c1 6 and t1.c2 = 1 7 ;Explained.SCOTT@ora10g>select * from table(dbms_xplan.display);----------------------------------------------------------------| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|----------------------------------------------------------------| 0 | SELECT STATEMENT | | 1 | 52 | 7 (15)||* 1 | HASH JOIN | | 1 | 52 | 7 (15)||* 2 | TABLE ACCESS FULL| T1 | 1 | 26 | 3 (0)|| 3 | TABLE ACCESS FULL| T2 | 1000 | 26000 | 3 (0)|----------------------------------------------------------------Predicate Information (identified by operation id):--------------------------------------------------- 1 - access("T1"."C1"="T2"."C1") 2 - filter("T1"."C2"=1) |
왜 Nested Loops Join 과 Hash Join 에서 Access Predicate 가 표현된느 방식의 차이가 발생하는가?
두 Join 의 동작 방식의 차이에서 비롯된다.
Nested Loops Join 은 선행 Table 에서 Key 값을 하나씩 읽으면서 후행 Table 에서 Key 값에 해당하는 값을 읽어 들이는 방식이다.
따라서 실제 Join 은 후행 Table 에 대한 Access 에서 발생한다.
따라서 후행 Table 을 읽는 단계가 Access Predicate 가 된다.
반면에 Hash Join 은 선행 Table 을 먼저 Build 한 후, 후행 Table 과 한번에 Join 하는 방식이다.
따라서 실제 Join 이 발생하는 Hash Join 단계가 Access Predicate 로 표현된다.
DBMS_XPLAN.DISPLAY Function 이 제공하는 Parameter 들을 잘 이용하면 더 많은 종류의 정보를 추출할 수 있다.
예를 들어 다음 2개의 문장은 동일한 결과를 Return 한다.
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 | SCOTT@ora10g>explain plan forselect * 2 3 from t1 4 where c1 = 1 and c2 = 'dummy' 5 ;Explained.SCOTT@ora10g>select * from table(dbms_xplan.display);---------------------------------------------------------------| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|---------------------------------------------------------------| 0 | SELECT STATEMENT | | 1 | 26 | 3 (0)||* 1 | TABLE ACCESS FULL| T1 | 1 | 26 | 3 (0)|---------------------------------------------------------------Predicate Information (identified by operation id):--------------------------------------------------- 1 - filter("C1"=1 AND "C2"=TO_NUMBER('dummy'))SCOTT@ora10g>explain plan forselect * 2 3 from t1 4 where c1 = 1 and c2 = 'dummy' 5 ;Explained.SCOTT@ora10g>select * from table(dbms_xplan.display('plan_table', null, 'typical',null));---------------------------------------------------------------| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|---------------------------------------------------------------| 0 | SELECT STATEMENT | | 1 | 26 | 3 (0)||* 1 | TABLE ACCESS FULL| T1 | 1 | 26 | 3 (0)|---------------------------------------------------------------Predicate Information (identified by operation id):--------------------------------------------------- 1 - filter("C1"=1 AND "C2"=TO_NUMBER('dummy')) |
EXPLAIN PLAN 수행 시 Statement ID 를 부여한 경우에는 다음과 같이 ID 값을 지정해 주어야 한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | SCOTT@ora10g>explain planset statement_id = 'test' for 2 3 select * 4 from t1 5 where c1 = 1 and c2 = 'dummy' 6 ;Explained.SCOTT@ora10g>select * from table(dbms_xplan.display('plan_table', 'test', 'typical',null));---------------------------------------------------------------| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|---------------------------------------------------------------| 0 | SELECT STATEMENT | | 1 | 26 | 3 (0)||* 1 | TABLE ACCESS FULL| T1 | 1 | 26 | 3 (0)|---------------------------------------------------------------Predicate Information (identified by operation id):--------------------------------------------------- 1 - filter("C1"=1 AND "C2"=TO_NUMBER('dummy')) |
# 다양한 출력 Format
DBMS_XPLAN.DISPLAY Function 은 Basic, Typical, Serial, All 네 가지의 출력 Format 을 제공하며 기본값은 Typical 이다.
더불어 Outline, Advanced 라는 문서화되지 않은 Format 도 제공한다.
각 Format 의 출력 결과와 의미는 다음과 같다.
Basic Format 은 실행 계획의 단계별 Operation 과 Object 이름만을 보여주는 말 그대로 매우 기본적인 Format 이다.
실제로 사용할 경우는 없을 것이다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | SCOTT@ora10g>explain plan forselect * 2 3 from t1 4 where c1 = 1 and c2 = 'dummy' 5 ;Explained.SCOTT@ora10g>select * from table(dbms_xplan.display('plan_table',null,'basic'));----------------------------------| Id | Operation | Name |----------------------------------| 0 | SELECT STATEMENT | || 1 | TABLE ACCESS FULL| T1 |---------------------------------- |
Typical Format 이 가장 일반적인 용도로 고안되었다.
단계별 Operation 과 Object 명, 예측 Row 수와 Result Set 의 크기(Bytes), Cost 와 예측 실행 시간등의 정보를 보여준다.
무엇보다 Predicate 정보를 보여준다는 사실이 중요하다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | SCOTT@ora10g>explain plan forselect * 2 3 from t1 4 where c1 = 1 and c2 = 'dummy' 5 ;Explained.SCOTT@ora10g>select * from table(dbms_xplan.display('plan_table',null,'typical'));--------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|--------------------------------------------------------------------------| 0 | SELECT STATEMENT | | 1 | 26 | 5 (0)||* 1 | TABLE ACCESS BY INDEX ROWID| T1 | 1 | 26 | 5 (0)||* 2 | INDEX RANGE SCAN | T1_N1 | 1 | | 1 (0)|--------------------------------------------------------------------------Predicate Information (identified by operation id):--------------------------------------------------- 1 - filter("C2"='dummy') 2 - access("C1"=1) |
Predicate 정보가 왜 그렇게 중요한가?
실행 계획이 복잡해지면 Where 절의 특정 조건이 실행 계획의 각 단계에서 정확하게 어떻게 사용되었는지가 매우 중요해진다.
Query Transformation 이라는 특별한 과정 때문에 Predicate 의 변형이 발생할 때는 이 정보가 특히 중요해진다.
All Format 은 실행 계획을 분석하는데 있어서 없어서는 안될 중요한 두 가지 정보를 추가적으로 제공한다.
첫째, Query Block 정보를 제공한다.
Oracle 은 우리가 수행 요청한 SQL 문장을 Query Block 이라는 단위로 나눈다.
Query Block 은 Transformation 및 Optimization 의 기본 단위가 된다.
아래 예제에서는 SWL$1 이라는 암호화 같은 Query Block 명이 사용된 것을 확인할 수 있다.
Query Block 명은 Inline View 와 Subquery 가 많이 사용되는 복잡한 Query 를 해석할 때 특히 유용하다.
둘째, Column Projection 정보를 제공한다.
Column Projection 이란 실행 계획의 특정 단계에서 어떤 Column 을 추출하는가를 의미한다.
아래 예제에서는 2번 단게에서는 Index t1_n1 으로부터 ROWID 와 Column c1 을 추출하며, 1번 단계에서는 Column c1, c2 를 추출한다는 것을 알 수 있다.
Column Projection 정보 또한 특별한 유형의 Query Transformation 을 Troubleshooting 할 때 유용한 정보가 된다.
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 55 56 57 58 59 | SCOTT@ora10g>create table t1(c1 int, c2 char(10));Table created.SCOTT@ora10g>insert into t1select level, 'dummy'from dualconnect by level <= 10000;10000 rows created.SCOTT@ora10g>commit;Commit complete.SCOTT@ora10g>create index t1_n1 on t1(c1);Index created.SCOTT@ora10g>explain plan forselect *from t1where c1 = 1 and c2 = 'dummy';Explained.SCOTT@ora10g>select * from table(dbms_xplan.display('plan_table',null,'all'));-------------------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |-------------------------------------------------------------------------------------| 0 | SELECT STATEMENT | | 1 | 25 | 2 (0)| 00:00:01 ||* 1 | TABLE ACCESS BY INDEX ROWID| T1 | 1 | 25 | 2 (0)| 00:00:01 ||* 2 | INDEX RANGE SCAN | T1_N1 | 1 | | 1 (0)| 00:00:01 |-------------------------------------------------------------------------------------Query Block Name / Object Alias (identified by operation id):------------------------------------------------------------- 1 - SEL$1 / T1@SEL$1 2 - SEL$1 / T1@SEL$1Predicate Information (identified by operation id):--------------------------------------------------- 1 - filter("C2"='dummy') 2 - access("C1"=1)Column Projection Information (identified by operation id):----------------------------------------------------------- 1 - "C1"[NUMBER,22], "C2"[CHARACTER,10] 2 - "T1".ROWID[ROWID,10], "C1"[NUMBER,22]Note----- - dynamic sampling used for this statement |
QB_NAME Hint 를 사용하면 Query Block 명을 직접 조작할 수 있다.
복잡한 Query 의 실행 계획을 해석할 때 매우 유용한 기능이다.
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 | SCOTT@ora10g>explain plan forselect /*+ qb_name(x) */*from t1where c1 = 1 and c2 = 'dummy';Explained.SCOTT@ora10g>select * from table(dbms_xplan.display('plan_table',null,'all'));-------------------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |-------------------------------------------------------------------------------------| 0 | SELECT STATEMENT | | 1 | 25 | 2 (0)| 00:00:01 ||* 1 | TABLE ACCESS BY INDEX ROWID| T1 | 1 | 25 | 2 (0)| 00:00:01 ||* 2 | INDEX RANGE SCAN | T1_N1 | 1 | | 1 (0)| 00:00:01 |-------------------------------------------------------------------------------------Query Block Name / Object Alias (identified by operation id):------------------------------------------------------------- 1 - X / T1@X 2 - X / T1@XPredicate Information (identified by operation id):--------------------------------------------------- 1 - filter("C2"='dummy') 2 - access("C1"=1)Column Projection Information (identified by operation id):----------------------------------------------------------- 1 - "C1"[NUMBER,22], "C2"[CHARACTER,10] 2 - "T1".ROWID[ROWID,10], "C1"[NUMBER,22]Note----- - dynamic sampling used for this statement |
Outline Format 은 매우 재미있는 추가적인 정보를 제공한다.
Outline 은 실행 계획을 수립하는데 필요한 Hint 들의 목록을 의미한다.
특정 실행 계획을 재현하기 위해 어떤 Hint 가 필요한지 확인하기 위한 용도로 사용 가능하다.
다음 예제에서는 INDEX Hint 와 ALL_ROWS Hint 가 사용된 것을 알 수 있다.
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 | SCOTT@ora10g>explain plan forselect *from t1where c1 = 1 and c2 = 'dummy';Explained.SCOTT@ora10g>select * from table(dbms_xplan.display('plan_table',null,'outline'));-------------------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |-------------------------------------------------------------------------------------| 0 | SELECT STATEMENT | | 1 | 25 | 2 (0)| 00:00:01 ||* 1 | TABLE ACCESS BY INDEX ROWID| T1 | 1 | 25 | 2 (0)| 00:00:01 ||* 2 | INDEX RANGE SCAN | T1_N1 | 1 | | 1 (0)| 00:00:01 |-------------------------------------------------------------------------------------Outline Data------------- /*+ BEGIN_OUTLINE_DATA INDEX(@"SEL$1" "T1"@"SEL$1" ("T1"."C1")) OUTLINE_LEAF(@"SEL$1") ALL_ROWS OPTIMIZER_FEATURES_ENABLE('10.2.0.2') IGNORE_OPTIM_EMBEDDED_HINTS END_OUTLINE_DATA */Predicate Information (identified by operation id):--------------------------------------------------- 1 - filter("C2"='dummy') 2 - access("C1"=1)Note----- - dynamic sampling used for this statement |
Advanced Format 은 All Format 과 Outline Format 을 합친 것과 같다.
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 | SCOTT@ora10g>explain plan forselect * from t1where c1 = 1 and c2 = 'dummy';Explained.SCOTT@ora10g>select * from table(dbms_xplan.display('plan_table',null,'advanced'));-------------------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |-------------------------------------------------------------------------------------| 0 | SELECT STATEMENT | | 1 | 25 | 2 (0)| 00:00:01 ||* 1 | TABLE ACCESS BY INDEX ROWID| T1 | 1 | 25 | 2 (0)| 00:00:01 ||* 2 | INDEX RANGE SCAN | T1_N1 | 1 | | 1 (0)| 00:00:01 |-------------------------------------------------------------------------------------Query Block Name / Object Alias (identified by operation id):------------------------------------------------------------- 1 - SEL$1 / T1@SEL$1 2 - SEL$1 / T1@SEL$1Outline Data------------- /*+ BEGIN_OUTLINE_DATA INDEX(@"SEL$1" "T1"@"SEL$1" ("T1"."C1")) OUTLINE_LEAF(@"SEL$1") ALL_ROWS OPTIMIZER_FEATURES_ENABLE('10.2.0.2') IGNORE_OPTIM_EMBEDDED_HINTS END_OUTLINE_DATA */Predicate Information (identified by operation id):--------------------------------------------------- 1 - filter("C2"='dummy') 2 - access("C1"=1)Column Projection Information (identified by operation id):----------------------------------------------------------- 1 - "C1"[NUMBER,22], "C2"[CHARACTER,10] 2 - "T1".ROWID[ROWID,10], "C1"[NUMBER,22]Note----- - dynamic sampling used for this statement |
출처 : http://develop.sunshiny.co.kr/827
원본 : Optimizing Oracle Optimizer - 조동욱
'Oracle > DBA' 카테고리의 다른 글
| [Oracle] Merge 구문 1 (0) | 2015.11.12 |
|---|---|
| [펌] [ORACLE] SYS_CONTEXT 및 USERENV 정리 (0) | 2015.09.03 |
| 특정 컬럼만 Masking (0) | 2015.09.03 |
| [펌]Index Access and Filter (0) | 2015.08.28 |
| DBMS_RANDOM (0) | 2015.08.18 |
