Oracle手工调用STA优化指定SQL
环境构造
10:50:58 SYS@zkm(1)> create table scott.zkm as select * from dba_objects; Table created. Elapsed: 00:00:01.28 10:51:19 SYS@zkm(1)> set autotrace traceonly 10:52:37 SYS@zkm(1)> create index scott.idx_object_id on scott.zkm(object_id) online; Index created. Elapsed: 00:00:01.00
目标SQL
select /*+ full(a) */ * from scott.zkm a where object_id=1000;
由于hint的强制关系,该SQL会执行全表扫描,如下:
10:54:24 SYS@zkm(1)> select /*+ full(a) */ * from scott.zkm a where object_id=1000; Elapsed: 00:00:00.02 Execution Plan ---------------------------------------------------------- Plan hash value: 1571665327 -------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | -------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 207 | 366 (1)| 00:00:05 | |* 1 | TABLE ACCESS FULL| ZKM | 1 | 207 | 366 (1)| 00:00:05 | -------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter("OBJECT_ID"=1000) Note ----- - dynamic sampling used for this statement (level=2) Statistics ---------------------------------------------------------- 9 recursive calls 0 db block gets 1391 consistent gets 1313 physical reads 0 redo size 1628 bytes sent via SQL*Net to client 523 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 1 rows processed
查询SQL ID。
col sql_id for a15 set line 500 col sql_text for a100 select sql_id,sql_text from v$sql where sql_text like 'select /*+ full(a) */ * from scott.zkm a where object_id=1000';
10:56:56 SYS@zkm(27)> col sql_id for a15 10:57:08 SYS@zkm(27)> set line 500 10:57:11 SYS@zkm(27)> col sql_text for a100 10:57:26 SYS@zkm(27)> select sql_id,sql_text from v$sql where sql_text like 'select /*+ full(a) */ * from scott.zkm a where object_id=1000'; SQL_ID SQL_TEXT --------------- ---------------------------------------------------------------------------------------------------- 9ajk015s54vpv select /*+ full(a) */ * from scott.zkm a where object_id=1000
跑STA对SQL进行分析。
DECLARE a_tuning_task VARCHAR2(30); BEGIN a_tuning_task := dbms_sqltune.create_tuning_task(sql_id => '9ajk015s54vpv', task_name => 'sql_profile_test_SQLID'); dbms_sqltune.execute_tuning_task(a_tuning_task); END; /
10:58:40 SYS@zkm(27)> DECLARE 10:58:41 2 a_tuning_task VARCHAR2(30); 10:58:41 3 BEGIN 10:58:41 4 a_tuning_task := dbms_sqltune.create_tuning_task(sql_id => '9ajk015s54vpv', 10:58:41 5 task_name => 'sql_profile_test_SQLID'); 10:58:41 6 dbms_sqltune.execute_tuning_task(a_tuning_task); 10:58:41 7 END; 10:58:41 8 / PL/SQL procedure successfully completed. Elapsed: 00:00:02.56
或者:
DECLARE a_tuning_task VARCHAR2(30); BEGIN a_tuning_task := dbms_sqltune.create_tuning_task(sql_text => 'select /*+ full(a) */ * from scott.zkm a where object_id=1000', task_name => 'sql_profile_test_SQLID'); dbms_sqltune.execute_tuning_task(a_tuning_task); END; /
使用PLSQL工具查看优化建议报告(用sqlplus格式会乱)。
SELECT DBMS_SQLTUNE.REPORT_TUNING_TASK( 'sql_profile_test_SQLID') from DUAL;
GENERAL INFORMATION SECTION ------------------------------------------------------------------------------- Tuning Task Name : sql_profile_test_SQLID Tuning Task Owner : SYS Workload Type : Single SQL Statement Scope : COMPREHENSIVE Time Limit(seconds): 1800 Completion Status : COMPLETED Started at : 09/01/2020 10:58:42 Completed at : 09/01/2020 10:58:44 ------------------------------------------------------------------------------- Schema Name: SYS SQL ID : 9ajk015s54vpv SQL Text : select /*+ full(a) */ * from scott.zkm a where object_id=1000 ------------------------------------------------------------------------------- FINDINGS SECTION (2 findings) ------------------------------------------------------------------------------- 1- Statistics Finding --------------------- Table "SCOTT"."ZKM" was not analyzed. Recommendation -------------- - Consider collecting optimizer statistics for this table. execute dbms_stats.gather_table_stats(ownname => 'SCOTT', tabname => 'ZKM', estimate_percent => DBMS_STATS.AUTO_SAMPLE_SIZE, method_opt => 'FOR ALL COLUMNS SIZE AUTO'); Rationale --------- The optimizer requires up-to-date statistics for the table in order to select a good execution plan. 2- SQL Profile Finding (see explain plans section below) -------------------------------------------------------- A potentially better execution plan was found for this statement. Recommendation (estimated benefit: 99.77%) ------------------------------------------ - Consider accepting the recommended SQL profile. execute dbms_sqltune.accept_sql_profile(task_name => 'sql_profile_test_SQLID', task_owner => 'SYS', replace => TRUE); Validation results ------------------ The SQL profile was tested by executing both its plan and the original plan and measuring their respective execution statistics. A plan may have been only partially executed if the other could be run to completion in less time. Original Plan With SQL Profile % Improved ------------- ---------------- ---------- Completion Status: COMPLETE COMPLETE Elapsed Time (s): .002195 .000006 99.72 % CPU Time (s): .002092 .000006 99.71 % User I/O Time (s): 0 0 Buffer Gets: 1318 3 99.77 % Physical Read Requests: 0 0 Physical Write Requests: 0 0 Physical Read Bytes: 0 0 Physical Write Bytes: 0 0 Rows Processed: 1 1 Fetches: 1 1 Executions: 1 1 Notes ----- 1. Statistics for the original plan were averaged over 10 executions. 2. Statistics for the SQL profile plan were averaged over 10 executions. ------------------------------------------------------------------------------- EXPLAIN PLANS SECTION ------------------------------------------------------------------------------- 1- Original With Adjusted Cost ------------------------------ Plan hash value: 1571665327 -------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | -------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 2 | 206 | 366 (1)| 00:00:05 | |* 1 | TABLE ACCESS FULL| ZKM | 2 | 206 | 366 (1)| 00:00:05 | -------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter("OBJECT_ID"=1000) 2- Using SQL Profile -------------------- Plan hash value: 3532417104 --------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 2 | 206 | 2 (0)| 00:00:01 | | 1 | TABLE ACCESS BY INDEX ROWID| ZKM | 2 | 206 | 2 (0)| 00:00:01 | |* 2 | INDEX RANGE SCAN | IDX_OBJECT_ID | 2 | | 1 (0)| 00:00:01 | --------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("OBJECT_ID"=1000) -------------------------------------------------------------------------------
根据优化报告,
第一个建议是收集统计信息:
1- Statistics Finding --------------------- Table "SCOTT"."ZKM" was not analyzed. Recommendation -------------- - Consider collecting optimizer statistics for this table. execute dbms_stats.gather_table_stats(ownname => 'SCOTT', tabname => 'ZKM', estimate_percent => DBMS_STATS.AUTO_SAMPLE_SIZE, method_opt => 'FOR ALL COLUMNS SIZE AUTO'); Rationale --------- The optimizer requires up-to-date statistics for the table in order to select a good execution plan.
第二个建议你使用sql profile固定执行计划,是使用索引的执行计划:
2- SQL Profile Finding (see explain plans section below) -------------------------------------------------------- A potentially better execution plan was found for this statement. Recommendation (estimated benefit: 99.77%) ------------------------------------------ - Consider accepting the recommended SQL profile. execute dbms_sqltune.accept_sql_profile(task_name => 'sql_profile_test_SQLID', task_owner => 'SYS', replace => TRUE); 2- Using SQL Profile -------------------- Plan hash value: 3532417104 --------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 2 | 206 | 2 (0)| 00:00:01 | | 1 | TABLE ACCESS BY INDEX ROWID| ZKM | 2 | 206 | 2 (0)| 00:00:01 | |* 2 | INDEX RANGE SCAN | IDX_OBJECT_ID | 2 | | 1 (0)| 00:00:01 | --------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("OBJECT_ID"=1000) -------------------------------------------------------------------------------
执行建议
-
execute dbms_stats.gather_table_stats(ownname => 'SCOTT', tabname => 'ZKM', estimate_percent => DBMS_STATS.AUTO_SAMPLE_SIZE,method_opt => 'FOR ALL COLUMNS SIZE AUTO');
-
execute dbms_sqltune.accept_sql_profile(task_name => 'sql_profile_test_SQLID', task_owner => 'SYS', replace => TRUE);
11:27:16 SYS@zkm(23)> execute dbms_stats.gather_table_stats(ownname => 'SCOTT', tabname => 'ZKM', estimate_percent => DBMS_STATS.AUTO_SAMPLE_SIZE,method_opt => 'FOR ALL COLUMNS SIZE AUTO'); PL/SQL procedure successfully completed. Elapsed: 00:00:00.49 11:27:31 SYS@zkm(23)> execute dbms_sqltune.accept_sql_profile(task_name => 'sql_profile_test_SQLID', task_owner => 'SYS', replace => TRUE); PL/SQL procedure successfully completed. Elapsed: 00:00:00.21
再次执行SQL,已经使用了索引:
11:28:23 SYS@zkm(1)> select /*+ full(a) */ * from scott.zkm a where object_id=1000; Elapsed: 00:00:00.03 Execution Plan ---------------------------------------------------------- Plan hash value: 3532417104 --------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 2 | 196 | 2 (0)| 00:00:01 | | 1 | TABLE ACCESS BY INDEX ROWID| ZKM | 2 | 196 | 2 (0)| 00:00:01 | |* 2 | INDEX RANGE SCAN | IDX_OBJECT_ID | 2 | | 1 (0)| 00:00:01 | --------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("OBJECT_ID"=1000) Note ----- - SQL profile "SYS_SQLPROF_017447b4e5170000" used for this statement Statistics ---------------------------------------------------------- 36 recursive calls 0 db block gets 15 consistent gets 1 physical reads 0 redo size 1631 bytes sent via SQL*Net to client 523 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 1 sorts (memory) 0 sorts (disk) 1 rows processed
从这里可以看出,SQL profile的优先级高于HINT。
回退
若是发现应用建议后,没有改善情况或者情况恶化,可以如下回退:
col name for a35 col sql_text for a100 set line 500 select name,SQL_TEXT,STATUS from dba_sql_profiles;
11:33:54 SYS@zkm(27)> col name for a35 11:34:00 SYS@zkm(27)> col sql_text for a100 11:34:06 SYS@zkm(27)> set line 500 11:34:08 SYS@zkm(27)> select name,SQL_TEXT,STATUS from dba_sql_profiles; NAME SQL_TEXT STATUS ----------------------------------- ---------------------------------------------------------------------------------------------------- ------------------------ SYS_SQLPROF_017447b4e5170000 select /*+ full(a) */ * from scott.zkm a where object_id=1000 ENABLED Elapsed: 00:00:00.00 11:34:39 SYS@zkm(27)> execute dbms_sqltune.drop_sql_profile('SYS_SQLPROF_017447b4e5170000'); PL/SQL procedure successfully completed. Elapsed: 00:00:00.01 11:34:52 SYS@zkm(1)> select /*+ full(a) */ * from scott.zkm a where object_id=1000; Elapsed: 00:00:00.01 Execution Plan ---------------------------------------------------------- Plan hash value: 1571665327 -------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | -------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 98 | 366 (1)| 00:00:05 | |* 1 | TABLE ACCESS FULL| ZKM | 1 | 98 | 366 (1)| 00:00:05 | -------------------------------------------------------------------------- Predicate Information (identified by operation id):
exec dbms_sqltune.execute_tuning_task('SYS_AUTO_SQL_TUNING_TASK'); select dbms_sqltune.report_tuning_task('SYS_AUTO_SQL_TUNING_TASK') from dual;
--------------------------------------------------- 1 - filter("OBJECT_ID"=1000) Statistics ---------------------------------------------------------- 5 recursive calls 0 db block gets 1319 consistent gets 0 physical reads 0 redo size 1628 bytes sent via SQL*Net to client 523 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 1 rows processed
删除STA任务
最后记得删除STA任务。
11:34:53 SYS@zkm(1)> exec dbms_sqltune.drop_TUNING_TASK( 'sql_profile_test_SQLID'); PL/SQL procedure successfully completed. Elapsed: 00:00:00.05
其他
对于系统每日自动运行的STA任务,如果不需要可以禁用,必要时可以手工运行。
exec dbms_sqltune.execute_tuning_task('SYS_AUTO_SQL_TUNING_TASK'); select dbms_sqltune.report_tuning_task('SYS_AUTO_SQL_TUNING_TASK') from dual;