프린트 하기

OS환경 : Oracle Linux 7.2 (64bit)

 

DB 환경 : Oracle Database 11.2.0.4, 18.0.0.0.0

 

테스트 명 : SQL을 변경하지 않고 실행계획 변경(SPM)

 

참고사항

실행 전 EMP_DEPT.SQL 실행해서 테이블 만들어야함

EMP_DEPT_SQL.txt
다운로드

튜닝대상의 SQL 실행 이외의 단계는 

딕셔너리 접근 권한 또는 DBMS_SQL 패키지 실행 권한있는 유저로 실행해야함.

 

테스트

1. 성능이 나쁜 SQL의 현재 실행계획(PLAN A) SQL 계획 베이스라인에 등록

1-1. SQL 실행(어플리케이션 실행 사용자로 실행)

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
$ sqlplus / as sysdba
SQL> conn jds/jds
Connected.
SQL> select E.empno, e.ename, d.dname
from emp e, dept d
where e.deptno = d.deptno;
 
     EMPNO ENAME      DNAME
---------- ---------- --------------
      7839 KING       ACCOUNTING
      7698 BLAKE      SALES
      7782 CLARK      ACCOUNTING
      7566 JONES      RESEARCH
      7788 SCOTT      RESEARCH
      7902 FORD       RESEARCH
      7369 SMITH      RESEARCH
      7499 ALLEN      SALES
      7521 WARD       SALES
      7654 MARTIN     SALES
      7844 TURNER     SALES
      7876 ADAMS      RESEARCH
      7900 JAMES      SALES
      7934 MILLER     ACCOUNTING
 
14 rows selected.

 

 

1-2. SQL_ID 와 PLAN_HASH_VALUE 확인 

1
2
3
4
5
6
7
$ sqlplus / as sysdba
SQL> select sql_id, plan_hash_value from v$sql
where sql_text like 'select E.empno, e.ename%';
 
SQL_ID          PLAN_HASH_VALUE
------------- ---------------
b28bvnnn551tr       3216042416

 

 

1-3. 커서의 실행계획 확인

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
SQL> select * from table(DBMS_XPLAN.DISPLAY_CURSOR('b28bvnnn551tr'));
 
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID    b28bvnnn551tr, child number 0
-------------------------------------
select E.empno, e.ename, d.dname from emp e, dept d where e.deptno =
d.deptno
 
Plan hash value: 3216042416
 
-------------------------------------------------------------------------------------------
| Id  | Operation             | Name      | Rows  | Bytes | Cost (%CPU)| Time      |
-------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT         |          |      |      |    6 (100)|      |
|   1 |  MERGE JOIN             |          |    14 |   770 |    6  (17)| 00:00:01 |
|   2 |   TABLE ACCESS BY INDEX ROWID| EMP      |    14 |   462 |    2   (0)| 00:00:01 |
|   3 |    INDEX FULL SCAN         | IDX_DEPTNO |    14 |      |    1   (0)| 00:00:01 |
|*  4 |   SORT JOIN             |          |    4 |    88 |    4  (25)| 00:00:01 |
|   5 |    TABLE ACCESS FULL         | DEPT      |    4 |    88 |    3   (0)| 00:00:01 |
-------------------------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   4 - access("E"."DEPTNO"="D"."DEPTNO")
       filter("E"."DEPTNO"="D"."DEPTNO")
 
Note
-----
   - dynamic statistics used: dynamic sampling (level=2)
 
 
28 rows selected.

 

 

1-4. 현재 실행계획을 SQL Plan 베이스라인으로 등록

1
2
3
4
5
6
7
8
9
10
11
12
13
set serveroutput on
declare
 ret number;
 begin
  ret := dbms_spm.load_plans_from_cursor_cache(
         sql_id => 'b28bvnnn551tr',
         plan_hash_value => 3216042416);
  dbms_output.put_line('LOAD PLANS : '|| ret);
end;
/
LOAD PLANS : 1
* 등록한 SQL 계획 베이스라인 수가 리턴값으로 출력됨
PL/SQL procedure successfully completed.

 

 

1-5. 등록한 SQL Plan 베이스라인 확인

1
2
3
4
5
6
7
8
9
10
SQL> col PLAN_NAME for a40
SQL> select sql_handle, plan_name, to_char(created, 'YYYY/MM/DD HH24:MI:SS') created, accepted, fixed
from dba_sql_plan_baselines
where sql_text like 'select E.empno, e.ename%';
 
SQL_HANDLE               PLAN_NAME                CREATED ACCEPTED FIXED
------------------------------ ---------------------------------------- -------------------
SQL_e18f9b7e02f01359           SQL_PLAN_f33wvgs1g04ute2772e4d        2018/06/28 16:00:07 YES NO
 
1 row selected.

 

 

2. SQL에 힌트를 추가한 성능이 좋은 실행계획(PLAN B) 생성

2-1. 힌트를 추가해서 SQL 실행(어플리케이션 실행 사용자로 실행)

1
2
3
4
SQL> select /*+FULL(e) FULL(d) LEADING(d) USE_HASH(e) */
E.empno, e.ename, d.dname
from emp e, dept d
where e.deptno = d.deptno;

 

 

2-2. SQL_ID 와 PLAN_HASH_VALUE 확인 

1
2
3
4
5
6
SQL> select sql_id, plan_hash_value from v$sql
where sql_text like 'select /*+FULL(e) FULL(d) LEADING(d) USE_HASH(e) */%';
 
SQL_ID          PLAN_HASH_VALUE
------------- ---------------
f126r9pddmv2r        615168685

 

 

2-3. 커서의 실행계획 확인

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
SQL> select * from table(DBMS_XPLAN.DISPLAY_CURSOR('f126r9pddmv2r'));
 
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID    f126r9pddmv2r, child number 0
-------------------------------------
select /*+FULL(e) FULL(d) LEADING(d) USE_HASH(e) */ E.empno, e.ename,
d.dname from emp e, dept d where e.deptno = d.deptno
 
Plan hash value: 615168685
 
---------------------------------------------------------------------------
| Id  | Operation       | Name | Rows  | Bytes | Cost (%CPU)| Time      |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |      |      |    6 (100)|      |
|*  1 |  HASH JOIN       |      |    14 |   770 |    6   (0)| 00:00:01 |
|   2 |   TABLE ACCESS FULL| DEPT |    4 |    88 |    3   (0)| 00:00:01 |
|   3 |   TABLE ACCESS FULL| EMP  |    14 |   462 |    3   (0)| 00:00:01 |
---------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   1 - access("E"."DEPTNO"="D"."DEPTNO")
 
Note
-----
   - dynamic statistics used: dynamic sampling (level=2)
 
 
25 rows selected.

 

 

3. 원래 실행계획(PLAN A)을 성능이 좋은 실행계획(PLAN B)로 교체

3-1. 원래 SQL의 SQL_HANDLE을 지정해서, 성능이 좋은 실행계획을 SQL 계획 베이스라인으로 등록

(2-2에서 확인한 SQL_ID, PLAN_HASH_VALUE와 1-5에서 확인한 SQL_HANDLE을 사용)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
set serveroutput on
declare
 ret number;
 begin
  ret := dbms_spm.load_plans_from_cursor_cache(
         sql_id => 'f126r9pddmv2r',
         plan_hash_value => 615168685,
         sql_handle => 'SQL_e18f9b7e02f01359');
  dbms_output.put_line('LOAD PLANS : '|| ret);
end;
/
LOAD PLANS : 1
 
PL/SQL procedure successfully completed.

 

 

3-2. 등록한 SQL Plan 베이스라인 확인

1
2
3
4
5
6
7
8
9
10
11
SQL> col PLAN_NAME for a40
SQL> select sql_handle, plan_name, to_char(created, 'YYYY/MM/DD HH24:MI:SS') created, accepted, fixed
from dba_sql_plan_baselines
where sql_text like 'select E.empno, e.ename%';
 
SQL_HANDLE               PLAN_NAME                CREATED ACCEPTED FIXED
------------------------------ ---------------------------------------- -------------------
SQL_e18f9b7e02f01359           SQL_PLAN_f33wvgs1g04ut8447c07a        2018/06/28 16:09:17 YES NO
SQL_e18f9b7e02f01359           SQL_PLAN_f33wvgs1g04ute2772e4d        2018/06/28 16:00:07 YES NO
 
2 rows selected.

 

 

3-3. 원래 실행계획(PLAN A) 제거(CREATED, PLAN_NAME으로 구분)

1
2
3
4
5
6
7
8
9
10
11
12
13
set serveroutput on
declare
 ret number;
 begin
  ret := dbms_spm.drop_sql_plan_baseline(
         sql_handle => 'SQL_e18f9b7e02f01359',
         plan_name => 'SQL_PLAN_f33wvgs1g04ute2772e4d');
  dbms_output.put_line('DROP PLANS : '|| ret);
end;
/
DROP PLANS : 1
 
PL/SQL procedure successfully completed.

 

 

3-4. 원래 실행계획(PLAN A) 제거되었는지 확인

1
2
3
4
5
6
7
8
9
10
SQL> col PLAN_NAME for a40
SQL> select sql_handle, plan_name, to_char(created, 'YYYY/MM/DD HH24:MI:SS') created, accepted, fixed
from dba_sql_plan_baselines
where sql_text like 'select E.empno, e.ename%';
 
SQL_HANDLE               PLAN_NAME                CREATED ACCEPTED FIXED
------------------------------ ---------------------------------------- -------------------
SQL_e18f9b7e02f01359           SQL_PLAN_f33wvgs1g04ut8447c07a        2018/06/28 16:09:17 YES NO
 
1 row selected.

 

 

3-5. 이후 같은 쿼리 실행시 베이스라인등록한 실행계획으로 가는지 확인

3-5-1. 쿼리 실행

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
SQL> select E.empno, e.ename, d.dname
from emp e, dept d
where e.deptno = d.deptno;
 
     EMPNO ENAME      DNAME
---------- ---------- --------------
      7839 KING       ACCOUNTING
      7698 BLAKE      SALES
      7782 CLARK      ACCOUNTING
      7566 JONES      RESEARCH
      7788 SCOTT      RESEARCH
      7902 FORD       RESEARCH
      7369 SMITH      RESEARCH
      7499 ALLEN      SALES
      7521 WARD       SALES
      7654 MARTIN     SALES
      7844 TURNER     SALES
      7876 ADAMS      RESEARCH
      7900 JAMES      SALES
      7934 MILLER     ACCOUNTING
 
14 rows selected.

 

 

3-5-2. SQL Plan 베이스라인 확인

1
2
3
4
5
6
7
8
SQL> select sql_handle, plan_name, to_char(created, 'YYYY/MM/DD HH24:MI:SS') created, accepted
from dba_sql_plan_baselines
where sql_text like 'select E.empno, e.ename%';
 
SQL_HANDLE               PLAN_NAME                CREATED         ACCEPTED FIXED
------------------------------ ---------------------------------------- ------------------- ---
SQL_e18f9b7e02f01359           SQL_PLAN_f33wvgs1g04ut8447c07a        2018/06/28 16:09:17 YES NO
SQL_e18f9b7e02f01359           SQL_PLAN_f33wvgs1g04ute2772e4d        2018/06/28 16:19:12 NO NO

계획이력이 하나 추가 되었지만 ACCEPTED가 YES인(승인된) 실행계획만 사용됨

정확하게는 fix되어있는 실행계획만 사용됨

YES 실행계획이 여러개인 경우 fix로 고정해주는 방법이 있음

 

 

3-5-3. SQL Plan 베이스라인 fix

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
SQL>
declare
 ret number;
 begin
  ret := dbms_spm.alter_sql_plan_baseline(
         sql_handle => 'SQL_e18f9b7e02f01359',
         plan_name => 'SQL_PLAN_f33wvgs1g04ut8447c07a',
         attribute_name => 'fixed',
         attribute_value => 'YES');
  dbms_output.put_line('PLANS ALTERED: ' || ret);
END;
/
 
PLANS ALTERED: 1
 
PL/SQL procedure successfully completed.

alter_sql_plan_baseline 설명

함수를 사용해 baseline 속성 값을 변경할 수 있음
attribute_name 속성

enabled : accepted가 yes 일 경우에 옵티마이저가 선택할지(YES) 안할지(NO) 결정함
fixed : 플랜을 Fix할때 사용, yes일 경우 새로운 실행계획이 들어와도 사용하지 않음

fixed yes 가 2개 이상이면 optimizer가 cost 낮은플랜을 선택함

 

 

3-5-4. SQL Plan 베이스라인 확인

1
2
3
4
5
6
7
8
SQL> select sql_handle, plan_name, to_char(created, 'YYYY/MM/DD HH24:MI:SS') created, accepted
from dba_sql_plan_baselines
where sql_text like 'select E.empno, e.ename%';
 
SQL_HANDLE               PLAN_NAME                CREATED         ACCEPTED FIXED
------------------------------ ---------------------------------------- ------------------- ---
SQL_e18f9b7e02f01359           SQL_PLAN_f33wvgs1g04ut8447c07a        2018/06/28 16:09:17 YES YES 
SQL_e18f9b7e02f01359           SQL_PLAN_f33wvgs1g04ute2772e4d        2018/06/28 16:19:12 NO NO

FIXED 컬럼이 YES 로 변경됨

 

 

3-5-5. AUTOTRACE ON 후 SQL 실행(어플리케이션 실행 사용자로 실행)

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
60
61
62
63
64
$ sqlplus / as sysdba
SQL> conn jds/jds
Connected.
SQL> set autot on
SQL> select E.empno, e.ename, d.dname
from emp e, dept d
where e.deptno = d.deptno;
 
     EMPNO ENAME      DNAME
---------- ---------- --------------
      7839 KING       ACCOUNTING
      7698 BLAKE      SALES
      7782 CLARK      ACCOUNTING
      7566 JONES      RESEARCH
      7788 SCOTT      RESEARCH
      7902 FORD       RESEARCH
      7369 SMITH      RESEARCH
      7499 ALLEN      SALES
      7521 WARD       SALES
      7654 MARTIN     SALES
      7844 TURNER     SALES
      7876 ADAMS      RESEARCH
      7900 JAMES      SALES
      7934 MILLER     ACCOUNTING
 
14 rows selected.
 
Execution Plan
----------------------------------------------------------
Plan hash value: 615168685
 
---------------------------------------------------------------------------
| Id  | Operation       | Name | Rows  | Bytes | Cost (%CPU)| Time      |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |    14 |   770 |    6   (0)| 00:00:01 |
|*  1 |  HASH JOIN       |      |    14 |   770 |    6   (0)| 00:00:01 |
|   2 |   TABLE ACCESS FULL| DEPT |    4 |    88 |    3   (0)| 00:00:01 |
|   3 |   TABLE ACCESS FULL| EMP  |    14 |   462 |    3   (0)| 00:00:01 |
---------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   1 - access("E"."DEPTNO"="D"."DEPTNO")
 
Note
-----
   - dynamic statistics used: dynamic sampling (level=2)
   - SQL plan baseline "SQL_PLAN_f33wvgs1g04ut8447c07a" used for this statement
 
 
Statistics
----------------------------------------------------------
    107  recursive calls
     30  db block gets
     64  consistent gets
      0  physical reads
       9188  redo size
       1013  bytes sent via SQL*Net to client
    623  bytes received via SQL*Net from client
      2  SQL*Net roundtrips to/from client
      6  sorts (memory)
      0  sorts (disk)
     14  rows processed

 

#Note에 SQL plan baseline에 있는 "SQL_PLAN_f33wvgs1g04ut8447c07a" 가 사용되었다고 표시됨

 

 

테스트 복사 붙여넣기용

SPM테스트_.txt
0.01MB

참조 : 오라클레벨업 서적 13챕터

https://yunzero.tistory.com/1

https://docs.oracle.com/en/database/oracle/oracle-database/19/arpls/DBMS_SPM.html#GUID-0E915A6D-F783-4BE6-9E5A-C2B756666341

'ORACLE > Performance Tuning ' 카테고리의 다른 글

enq HW - contention  (0) 2018.12.05
enq UL - contention, PLSQL lock Timer  (0) 2018.12.05
QMON Coordinator 란  (0) 2018.05.30
SQL*Net message from client 란?  (8) 2018.05.30
성능테스트 자료1  (0) 2018.05.29