SQL Plan Management(SPM) - 1 파라미터의 기능 확인
1. SPM의 목적
실행계획 변경으로 SQL의 성능이 저하되는 것을 방지하고 SQL의 실행계획을 관리하기 위함
2. SPM의 기능
SQL의 실행계획을 관리하고, 새로운 실행 계획이 만들어지더라도 검증이 끝날 때까지 새로운 실행계획을 사용하지 않으며, 현재의 실행계획보다 성능이 비슷하거나 향상될 경우에만 사용할 수 있도록 함
3. SPM 활용도
- 1. Oracle Upgrade
- 2. 신규 Application 이나 Module Deploy
- 3. SQL 성능 튜닝 및 관리
- 4. 통계 수집에 따른 역효과 방지
- 5. 그밖의 어떤 변경으로 SQL 성능이 저하되는 것을 방지
4. SPM사용을 위한 설정 방법
1) 초기 파라미터 설정
1. OPTIMIZER_CAPTURE_SQL_PLAN_BASELINES
SQL 문장을 캡쳐한 후 Baseline에 자동으로 저장하는 기능을 enable 시킨다. 디폴트 값은 false 이다.
DBMS_SPM.CONFIGURE 프로시저를 통해 반복 수행되는 SQL 중 관리 대상으로 선별할 SQL문을 여러 조건으로 선별할 수 있다.
2. OPTIMIZER_USE_SQL_PLAN_BASELINE
옵티마이저가 SQL Plan Baseline게 캡처된 SQL 실혱계획 정보를 사용하여 SQL을 실행할 수 있도록 할 수 있다. 디폴트 값은 true 이다.
2) OPTIMIZER_CAPTURE_SQL_PLAN_BASELINES
캡쳐 기능에 대한 파라미터 사용 내용을 살펴보자
db_mon@testdb> show parameter sql_plan NAME TYPE VALUE ------------------------------------ ----------- ------------------------------ optimizer_capture_sql_plan_baselines boolean FALSE optimizer_use_sql_plan_baselines boolean TRUE db_mon@testdb> select sql_handle 2 ,plan_name 3 ,creator 4 ,origin 5 ,enabled 6 ,accepted 7 ,fixed 8 ,created 9 from dba_sql_plan_baselines 10 where creator = 'HR'; no rows selected db_mon@testdb> alter system set optimizer_capture_sql_plan_baselines=true; System altered. db_mon@testdb>
위와 같이 파라미터 정보를 확인해 보고 HR 기준으로 캡쳐가 된 SQL 문장이 있는지 확인해 봤다.
파라미터의 경우 캡쳐 기능이 설정되어 있지 않아 캡처 기능을 true로 변경해 준다.
HR 기준으로 Baseline에 캡쳐된 SQL 문장은 현재 없다.
HR 계정으로 아래 3가지 SQL을 여러번 실행해 준다.
SQL1) select * from employees where employee_id > 113; SQL2) select * from employees where employee_id < 113; SQL3) select * from employees where employee_id < 103;
HR 계정으로 위의 SQL 3개를 수차례 실행해 본다.
실행 후 캡쳐된 정보가 있는지 확인해 본다.
db_mon@testdb> select sql_handle 2 ,plan_name 3 ,creator 4 ,origin 5 ,enabled 6 ,accepted 7 ,fixed 8 ,created 9 from dba_sql_plan_baselines 10 where creator = 'HR'; SQL_HANDLE PLAN_NAME CREATOR ORIGIN ENA ACC FIX CREATED ------------------------------ ------------------------------ --------------- -------------- --- --- --- -------------------- SQL_35b49a6a18ed94b7 SQL_PLAN_3bd4ud8cfv55re994a4cc HR AUTO-CAPTURE YES YES NO 20200319 16:14:58.000000 SQL_7553934070149a29 SQL_PLAN_7anwm81s196j9e994a4cc HR AUTO-CAPTURE YES YES NO 20200319 16:15:11.000000 SQL_a97c8c83e10b6a14 SQL_PLAN_akz4chghhquhne994a4cc HR AUTO-CAPTURE YES YES NO 20200319 16:15:06.000000 3 rows selected.
위의 내용과 같이 자동 캡쳐 기능을 활성화하는 파라미터를 true로 변경하니 반복 수행되는 SQL에 대해 자동으로 캡쳐된 것을 확인할 수 있다.
이제 자동 캡처 기능에 대해 OPTIMIZER_USE_SQL_PLAN_BASELINES 파라미터의 값은 영향을 미치지 않는 것에 대해 살펴보자
먼저 Baseline에 캡쳐된 정보를 삭제하자
db_mon@testdb> DECLARE 2 L_PLANS_DROPPED PLS_INTEGER; 3 BEGIN 4 FOR C1_REC IN (SELECT SQL_HANDLE, PLAN_NAME 5 FROM DBA_SQL_PLAN_BASELINES 6 WHERE CREATOR = 'HR') 7 LOOP 8 L_PLANS_DROPPED := DBMS_SPM.DROP_SQL_PLAN_BASELINE ( 9 SQL_HANDLE => C1_REC.SQL_HANDLE 10 , PLAN_NAME => C1_REC.PLAN_NAME); 11 DBMS_OUTPUT.PUT_LINE (L_PLANS_DROPPED); 12 END LOOP; 13 END; 14 / 1 1 1 PL/SQL procedure successfully completed. db_mon@testdb> select sql_handle 2 ,plan_name 3 ,creator 4 ,origin 5 ,enabled 6 ,accepted 7 ,fixed 8 ,created 9 from dba_sql_plan_baselines 10 where creator = 'HR'; no rows selected db_mon@testdb>
이제 파라미터 값을 변경하고 확인해 보자
db_mon@testdb> show parameter sql_plan NAME TYPE VALUE ------------------------------------ ----------- ------------------------------ optimizer_capture_sql_plan_baselines boolean TRUE optimizer_use_sql_plan_baselines boolean TRUE db_mon@testdb> alter system set optimizer_use_sql_plan_baselines=false; System altered. db_mon@testdb> show parameter sql_plan NAME TYPE VALUE ------------------------------------ ----------- ------------------------------ optimizer_capture_sql_plan_baselines boolean TRUE optimizer_use_sql_plan_baselines boolean FALSE db_mon@testdb>
위와 같이 마라미터를 바꾸고 위 테스트에서 수행했던 SQL 3가지를 수행해 보자
HR 계정에서 수행하자
SQL1) select * from employees where employee_id > 113; SQL2) select * from employees where employee_id < 113; SQL3) select * from employees where employee_id < 103; db_mon@testdb> select sql_handle ,plan_name 2 3 ,creator 4 ,origin 5 ,enabled 6 ,accepted 7 ,fixed 8 ,created 9 from dba_sql_plan_baselines 10 where creator = 'HR'; SQL_HANDLE PLAN_NAME CREATOR ORIGIN ENA ACC FIX CREATED ------------------------------ ------------------------------ --------------- -------------- --- --- --- -------------------- SQL_35b49a6a18ed94b7 SQL_PLAN_3bd4ud8cfv55re994a4cc HR AUTO-CAPTURE YES YES NO 20200319 16:20:18.000000 SQL_7553934070149a29 SQL_PLAN_7anwm81s196j9e994a4cc HR AUTO-CAPTURE YES YES NO 20200319 16:20:25.000000 SQL_a97c8c83e10b6a14 SQL_PLAN_akz4chghhquhne994a4cc HR AUTO-CAPTURE YES YES NO 20200319 16:20:22.000000 3 rows selected. db_mon@testdb>
위의 내용과 같이 optimizer_use_sql_plan_baselines 파라미터는 캡쳐 액션과는 상관 없으며
optimizer_capture_sql_plan_baselines 파라미터가 실행계획 캡쳐에 대한 기능을 작동하게 만든다.
3) OPTIMIZER_USE_SQL_PLAN_BASELINE
옵티마이저가 SQL Plan Baseline을 사용하도록 하는 파라미터에 대해 살펴보자
먼저 테스트 케이스를 확인해 보자
테스트를 위해 오라클 샘플 스키마의 SH 계정의 CUSTOMERS 테이블을 복제해서 사용하도록 하겠다.
SQL> DROP TABLE TEST_SPM PURGE; Table TEST_SPM이(가) 삭제되었습니다. SQL> CREATE TABLE TEST_SPM 2 AS 3 SELECT * 4 FROM CUSTOMERS 5 WHERE 1=0; Table TEST_SPM이(가) 생성되었습니다. SQL> CREATE INDEX TEST_SPM_LAST_NAME_IDX ON TEST_SPM(CUST_LAST_NAME); Index TEST_SPM_LAST_NAME_IDX이(가) 생성되었습니다.
먼저 Full Table Sacn으로 유도되는 SQL 케이스를 만들도록 하겠다.
SQL> INSERT INTO TEST_SPM 2 SELECT * 3 FROM CUSTOMERS; 55,500개 행 이(가) 삽입되었습니다. SQL> COMMIT; 커밋 완료. SQL> BEGIN 2 DBMS_STATS.GATHER_TABLE_STATS( 3 OWNNAME => 'HR' 4 ,TABNAME => 'TEST_SPM' 5 ,CASCADE => TRUE); 6 END; 7 / PL/SQL 프로시저가 성공적으로 완료되었습니다. SQL> SELECT /* SPM TEST */ 2 CUST_FIRST_NAME, CUST_LAST_NAME 3 FROM TEST_SPM 4 WHERE CUST_LAST_NAME BETWEEN 'Puleo' AND 'Zzz'; . . . SQL_ID 5st43muqadyw2, child number 0 ------------------------------------- SELECT /* SPM TEST */ CUST_FIRST_NAME, CUST_LAST_NAME FROM TEST_SPM WHERE CUST_LAST_NAME BETWEEN 'Puleo' AND 'Zzz' Plan hash value: 1145642998 ------------------------------------------------------------------------------ | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------------------------------------------------------------ | 0 | SELECT STATEMENT | | | | 685 (100)| | |* 1 | TABLE ACCESS FULL| TEST_SPM | 22202 | 325K| 685 (1)| 00:00:01 | ------------------------------------------------------------------------------ Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter(("CUST_LAST_NAME">='Puleo' AND "CUST_LAST_NAME"<='Zzz'))
이제 같은 SQL 문장으로 Index Range Scan 되도록 만들어 보자
SQL> TRUNCATE TABLE TEST_SPM; Table TEST_SPM이(가) 잘렸습니다. SQL> INSERT INTO TEST_SPM 2 SELECT * 3 FROM CUSTOMERS 4 ORDER BY CUST_LAST_NAME; 55,500개 행 이(가) 삽입되었습니다. SQL> COMMIT; 커밋 완료. SQL> BEGIN 2 DBMS_STATS.GATHER_TABLE_STATS( 3 OWNNAME => 'HR' 4 ,TABNAME => 'TEST_SPM' 5 ,CASCADE => TRUE); 6 END; 7 / PL/SQL 프로시저가 성공적으로 완료되었습니다. SQL> SELECT /* SPM TEST */ 2 CUST_FIRST_NAME, CUST_LAST_NAME 3 FROM TEST_SPM 4 WHERE CUST_LAST_NAME BETWEEN 'Puleo' AND 'Zzz'; . . . SQL_ID 5st43muqadyw2, child number 0 ------------------------------------- SELECT /* SPM TEST */ CUST_FIRST_NAME, CUST_LAST_NAME FROM TEST_SPM WHERE CUST_LAST_NAME BETWEEN 'Puleo' AND 'Zzz' Plan hash value: 1874805010 -------------------------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | -------------------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | 465 (100)| | | 1 | TABLE ACCESS BY INDEX ROWID BATCHED| TEST_SPM | 13886 | 203K| 465 (0)| 00:00:01 | |* 2 | INDEX RANGE SCAN | TEST_SPM_LAST_NAME_IDX | 13886 | | 38 (0)| 00:00:01 | -------------------------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("CUST_LAST_NAME">='Puleo' AND "CUST_LAST_NAME"<='Zzz')
먼저 위의 테스트 케이스의 SQL 문장과 두개의 실행계획이 캡쳐가 되도록 해 보자
SQL> alter system set optimizer_capture_sql_plan_baselines=true; System SET이(가) 변경되었습니다. SQL> show parameter sql_plan NAME TYPE VALUE ------------------------------------ ------- ----- optimizer_capture_sql_plan_baselines boolean TRUE optimizer_use_sql_plan_baselines boolean FALSE
이제 위 테스트 경우의 문장을 수행해 자동 캡쳐가 되도록 하자
db_mon@testdb> select sql_handle 2 ,plan_name 3 ,substr(sql_text, 1, 20) 4 ,creator 5 ,origin 6 ,enabled 7 ,accepted 8 ,fixed 9 ,created 10 ,last_modified 11 ,last_executed 12 from dba_sql_plan_baselines 13 where creator = 'HR' 14 and sql_text like '%SPM TEST%'; SQL_HANDLE PLAN_NAME SUBSTR(SQL_TEXT,1,20) CREATOR ORIGIN ENA ACC FIX ------------------------------ ------------------------------ -------------------------------------------------------------------------------- --------------- -------------- --- --- --- CREATED LAST_MODIFIED LAST_EXECUTED -------------------- --------------------------------------------------------------------------- --------------------------------------------------------------------------- SQL_462ec18b331ff374 SQL_PLAN_4cbq1jctjzwvn8b323ba7 SELECT /* SPM TEST * HR AUTO-CAPTURE YES NO NO 20200325 19:43:46.00 20200325 19:43:46.000000 0000 SQL_462ec18b331ff374 SQL_PLAN_4cbq1jctjzwvneb1890ae SELECT /* SPM TEST * HR AUTO-CAPTURE YES YES NO 20200325 17:27:05.00 20200325 17:27:05.000000 20200325 17:27:05.000000 0000 2 rows selected.
위와 같이 두개의 SQL 문장와 해당하는 플랜이 Baseline에 등록이 되었다.
현재 쿼리를 수행할 경우 Index Range Scan을 하는 플랜으로 동작하게 테이블이 구성되어 있다.
즉, 아래와 같다.
SQL> SELECT /* SPM TEST */ 2 CUST_FIRST_NAME, CUST_LAST_NAME 3 FROM TEST_SPM 4 WHERE CUST_LAST_NAME BETWEEN 'Puleo' AND 'Zzz'; . . . SQL> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR()); SQL_ID 5st43muqadyw2, child number 1 ------------------------------------- SELECT /* SPM TEST */ CUST_FIRST_NAME, CUST_LAST_NAME FROM TEST_SPM WHERE CUST_LAST_NAME BETWEEN 'Puleo' AND 'Zzz' Plan hash value: 1874805010 -------------------------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | -------------------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | 461 (100)| | | 1 | TABLE ACCESS BY INDEX ROWID BATCHED| TEST_SPM | 13791 | 202K| 461 (0)| 00:00:01 | |* 2 | INDEX RANGE SCAN | TEST_SPM_LAST_NAME_IDX | 13791 | | 37 (0)| 00:00:01 | -------------------------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("CUST_LAST_NAME">='Puleo' AND "CUST_LAST_NAME"<='Zzz')
여기에서 OPTIMIZER_USE_SQL_PLAN_BASELINES 파라미터의 값을 바꿀 경우
DBA_SQL_PLAN_BASELINES 뷰에서 조회한대로 ACCEPTED 컬럼의 값이 YES인 SQL_HANDLE 이 SQL_462ec18b331ff374 이며
PLAN_NAME이 SQL_PLAN_4cbq1jctjzwvneb1890ae 인 실행계획을 사용하게 될 것이다.
내용을 확인해 보자
SQL> alter system set OPTIMIZER_USE_SQL_PLAN_BASELINES=true; System SET이(가) 변경되었습니다. SQL> SQL> show parameter sql_plan NAME TYPE VALUE ------------------------------------ ------- ----- optimizer_capture_sql_plan_baselines boolean TRUE optimizer_use_sql_plan_baselines boolean TRUE
위와 같이 옵티마이저가 SQL Plan Baseline을 사용할 수 있게 파라미터 값을 변경했다.
이제 같은 쿼리가 Index Range Scan을 사용하는 플랜을 쓰지 않고 Baseline의 정보를 참조해서 Full Table Scan 방식의 플랜을 선택하게 될 것이다.
SQL> SELECT /* SPM TEST */ 2 CUST_FIRST_NAME, CUST_LAST_NAME 3 FROM TEST_SPM 4 WHERE CUST_LAST_NAME BETWEEN 'Puleo' AND 'Zzz'; . . . SQL> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR()); SQL_ID 5st43muqadyw2, child number 2 ------------------------------------- SELECT /* SPM TEST */ CUST_FIRST_NAME, CUST_LAST_NAME FROM TEST_SPM WHERE CUST_LAST_NAME BETWEEN 'Puleo' AND 'Zzz' Plan hash value: 1145642998 ------------------------------------------------------------------------------ | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------------------------------------------------------------ | 0 | SELECT STATEMENT | | | | 685 (100)| | |* 1 | TABLE ACCESS FULL| TEST_SPM | 13791 | 202K| 685 (1)| 00:00:01 | ------------------------------------------------------------------------------ Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter(("CUST_LAST_NAME">='Puleo' AND "CUST_LAST_NAME"<='Zzz')) Note ----- - SQL plan baseline SQL_PLAN_4cbq1jctjzwvneb1890ae used for this statement
마지막 Note를 보면 SQL plan baseline의 SQL_PLAN_4cbq1jctjzwvneb1890ae 플랜 정보를 사용한 것을 알 수 있다.
즉, 마지막으로 테스트한 optimizer_use_sql_plan_baselines 파라미터를 변경해야
옵티마이저가 플랜을 생성할 때 SQL Plan Baseline을 확인한다는 사실을 확인할 수 있다.
5. 정리
SPM 기능을 사용하는데 필요한 파라미터는 아래 두가지 이다.
OPTIMIZER_CAPTURE_SQL_PLAN_BASELINES
OPTIMIZER_USE_SQL_PLAN_BASELINES
OPTIMIZER_CAPTURE_SQL_PLAN_BASELINES의 기능은 SQL Plan을 Baseline에 자동 캡쳐 하는 기능을 enable/disable 하는 것이다.
OPTIMIZER_USE_SQL_PLAN_BASELINES의 기능은 Baseline에 저장된 정보를 옵티마이저가 사용할지 말지를 결정하는 파라미터이다.