【DB筆試面試608】在Oracle中,如何使用STA來生成SQL Profile?

  • 2019 年 10 月 10 日
  • 筆記

題目部分

在Oracle中,如何使用STA來生成SQL Profile?

答案部分

利用STA對語句進行優化後,STA會對語句進行分析,採用最優的優化策略,並給出優化後的查詢計劃。可以按照STA給出的建議重寫語句。但是,有些情況下,你可能無法重寫語句(比如在生產環境中,SQL語句又在一個包中)。這個時候就可以利用Sql Profile,將優化策略存儲在Profile中,Oracle在構建這條語句的查詢計劃時,就不會使用已有相關統計數據,而使用Profile的策略,生成新的查詢計劃。

第一步:給用戶賦許可權

[ZHLHRSPMDB2:oracle]:/oracle>sqlplus / as sysdba    SQL*Plus: Release 11.2.0.4.0 Production on Wed May 25 16:47:29 2016    Copyright (c) 1982, 2013, Oracle.  All rights reserved.      Connected to:  Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production  With the Partitioning, Real Application Clusters, OLAP, Data Mining  and Real Application Testing options    SYS@dlhr>  SYS@dlhr>  SYS@dlhr>  SYS@dlhr> GRANT CREATE ANY SQL PROFILE TO LHR;    Grant succeeded.    SYS@dlhr> GRANT DROP ANY SQL PROFILE TO LHR;    Grant succeeded.    SYS@dlhr> GRANT ALTER ANY SQL PROFILE TO LHR;    Grant succeeded.    SYS@dlhr> conn lhr/lhr  Connected.  LHR@dlhr>  LHR@dlhr> select * from v$version;    BANNER  --------------------------------------------------------------------------------  Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production  PL/SQL Release 11.2.0.4.0 - Production  CORE    11.2.0.4.0      Production  TNS for IBM/AIX RISC System/6000: Version 11.2.0.4.0 - Production  NLSRTL Version 11.2.0.4.0 - Production    LHR@dlhr> create table lhr.TB_LHR_20160525_01 as select * from dba_objects;    Table created.    LHR@dlhr> create index lhr.TB_LHR_20160525_01_idx on TB_LHR_20160525_01(object_id);    Index created.    LHR@dlhr> exec dbms_stats.gather_table_stats('lhr','TB_LHR_20160525_01',cascade=>true,degree=>4);    PL/SQL procedure successfully completed.    LHR@dlhr> set autot on  LHR@dlhr> select /*+no_index(TB_LHR_20160525_01 TB_LHR_20160525_01_idx)*/count(*) from lhr.TB_LHR_20160525_01 where object_id = 100 ;      COUNT(*)  ----------           1      Execution Plan  ----------------------------------------------------------  Plan hash value: 3612989399    -----------------------------------------------------------------------------------------  | Id  | Operation          | Name               | Rows  | Bytes | Cost (%CPU)| Time     |  -----------------------------------------------------------------------------------------  |   0 | SELECT STATEMENT   |                    |     1 |     5 |   351   (2)| 00:00:05 |  |   1 |  SORT AGGREGATE    |                    |     1 |     5 |            |          |  |*  2 |   TABLE ACCESS FULL| TB_LHR_20160525_01 |     1 |     5 |   351   (2)| 00:00:05 |  -----------------------------------------------------------------------------------------    Predicate Information (identified by operation id):  ---------------------------------------------------       2 - filter("OBJECT_ID"=100)      Statistics  ----------------------------------------------------------            1  recursive calls            0  db block gets         1249  consistent gets            0  physical reads            0  redo size          526  bytes sent via SQL*Net to client          520  bytes received via SQL*Net from client            2  SQL*Net roundtrips to/from client            0  sorts (memory)            0  sorts (disk)            1  rows processed  LHR@dlhr> set autot off  LHR@dlhr> SELECT v.SQL_ID, v.SQL_TEXT FROM v$sql v WHERE v.SQL_TEXT like '%no_index(TB_LHR_20160525_01%' and v.SQL_TEXT not like '%v$sql%' ;    SQL_ID  -------------  SQL_TEXT  ------------------------------------------------------------------------------------------------------------------------------------  7jt1btjkcczb8  select /*+no_index(TB_LHR_20160525_01 TB_LHR_20160525_01_idx)*/count(*) from lhr.TB_LHR_20160525_01 where object_id = 100    7suktf0w95cry  EXPLAIN PLAN SET STATEMENT_ID='PLUS150249' FOR select /*+no_index(TB_LHR_20160525_01 TB_LHR_20160525_01_idx)*/count(*) from lhr.TB_L  HR_20160525_01 where object_id = 100  

第二步:創建、執行優化任務

LHR@dlhr> DECLARE    2      my_task_name VARCHAR2(30);    3      my_sqltext       CLOB;    4  BEGIN    5      my_sqltext := 'select /*+no_index(TB_LHR_20160525_01 TB_LHR_20160525_01_idx)*/count(*) from lhr.TB_LHR_20160525_01 where object_id = 100';    6      my_task_name := DBMS_SQLTUNE.CREATE_TUNING_TASK(    7                              sql_text          => my_sqltext,    8                              user_name       => 'LHR',    9                              scope           => 'COMPREHENSIVE',   10                              time_limit    => 60,   11                              task_name       => 'sql_profile_test',   12                              description => 'Task to tune a query on a specified table');   13      DBMS_SQLTUNE.EXECUTE_TUNING_TASK( task_name => 'sql_profile_test');   14  END;   15  /    PL/SQL procedure successfully completed.  

或者也可以使用sqlid來生成優化任務,如下:

LHR@dlhr> DECLARE    2    a_tuning_task VARCHAR2(30);    3  BEGIN    4    a_tuning_task := dbms_sqltune.create_tuning_task(sql_id    => '7jt1btjkcczb8',    5                                                     task_name => 'sql_profile_test_SQLID');    6    dbms_sqltune.execute_tuning_task(a_tuning_task);    7  END;    8  /    PL/SQL procedure successfully completed.  

第三步:查看優化建議

LHR@dlhr> set autot off  LHR@dlhr> set long 10000  LHR@dlhr> set longchunksize 1000  LHR@dlhr> set linesize 100  LHR@dlhr> SELECT DBMS_SQLTUNE.REPORT_TUNING_TASK( 'sql_profile_test') from DUAL;    DBMS_SQLTUNE.REPORT_TUNING_TASK('SQL_PROFILE_TEST')  ----------------------------------------------------------------------------------------------------  GENERAL INFORMATION SECTION  -------------------------------------------------------------------------------  Tuning Task Name   : sql_profile_test  Tuning Task Owner  : LHR  Workload Type      : Single SQL Statement  Scope              : COMPREHENSIVE  Time Limit(seconds): 60  Completion Status  : COMPLETED  Started at         : 05/25/2016 16:58:31  Completed at       : 05/25/2016 16:58:32    -------------------------------------------------------------------------------  Schema Name: LHR  SQL ID     : 9kzm8scz6t92z  SQL Text   : select /*+no_index(TB_LHR_20160525_01               TB_LHR_20160525_01_idx)*/count(*) from lhr.TB_LHR_20160525_01               where object_id = 100    -------------------------------------------------------------------------------  FINDINGS SECTION (1 finding)  -------------------------------------------------------------------------------    1- SQL Profile Finding (see explain plans section below)  --------------------------------------------------------    A potentially better execution plan was found for this statement.      Recommendation (estimated benefit: 99.83%)    ------------------------------------------    - Consider accepting the recommended SQL profile.      execute dbms_sqltune.accept_sql_profile(task_name => 'sql_profile_test',              task_owner => 'LHR', 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):             .006278            .00004      99.36 %    CPU Time (s):                 .003397           .000021      99.38 %    User I/O Time (s):                  0                 0    Buffer Gets:                     1249                 2      99.83 %    Physical Read Requests:             0                 0    Physical Write Requests:            0                 0    DBMS_SQLTUNE.REPORT_TUNING_TASK('SQL_PROFILE_TEST')  ----------------------------------------------------------------------------------------------------    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: 3612989399    -----------------------------------------------------------------------------------------  | Id  | Operation          | Name               | Rows  | Bytes | Cost (%CPU)| Time     |  -----------------------------------------------------------------------------------------  |   0 | SELECT STATEMENT   |                    |     1 |     5 |   351   (2)| 00:00:05 |  |   1 |  SORT AGGREGATE    |                    |     1 |     5 |            |          |  |*  2 |   TABLE ACCESS FULL| TB_LHR_20160525_01 |     1 |     5 |   351   (2)| 00:00:05 |  -----------------------------------------------------------------------------------------    Predicate Information (identified by operation id):  ---------------------------------------------------       2 - filter("OBJECT_ID"=100)    2- Using SQL Profile  --------------------  Plan hash value: 661515879    --------------------------------------------------------------------------------------------  | Id  | Operation         | Name                   | Rows  | Bytes | Cost (%CPU)| Time     |  --------------------------------------------------------------------------------------------  |   0 | SELECT STATEMENT  |                        |     1 |     5 |     1   (0)| 00:00:01 |  |   1 |  SORT AGGREGATE   |                        |     1 |     5 |            |          |  |*  2 |   INDEX RANGE SCAN| TB_LHR_20160525_01_IDX |     1 |     5 |     1   (0)| 00:00:01 |  --------------------------------------------------------------------------------------------    Predicate Information (identified by operation id):  ---------------------------------------------------      DBMS_SQLTUNE.REPORT_TUNING_TASK('SQL_PROFILE_TEST')  ----------------------------------------------------------------------------------------------------     2 - access("OBJECT_ID"=100)    -------------------------------------------------------------------------------  

這裡可以看到,在優化建議中給出了新的查詢計劃。現在,我們決定接受這個建議,並且不重寫語句。

第四步:接受profile

LHR@dlhr> set autot on  LHR@dlhr> select /*+no_index(TB_LHR_20160525_01 TB_LHR_20160525_01_idx)*/count(*) from lhr.TB_LHR_20160525_01 where object_id = 100 ;      COUNT(*)  ----------           1      Execution Plan  ----------------------------------------------------------    Plan hash value: 3612989399    -----------------------------------------------------------------------------------------  | Id  | Operation          | Name               | Rows  | Bytes | Cost (%CPU)| Time     |  -----------------------------------------------------------------------------------------  |   0 | SELECT STATEMENT   |                    |     1 |     5 |   351   (2)| 00:00:05 |  |   1 |  SORT AGGREGATE    |                    |     1 |     5 |            |          |  |*  2 |   TABLE ACCESS FULL| TB_LHR_20160525_01 |     1 |     5 |   351   (2)| 00:00:05 |  -----------------------------------------------------------------------------------------    Predicate Information (identified by operation id):  ---------------------------------------------------       2 - filter("OBJECT_ID"=100)      Statistics  ----------------------------------------------------------            0  recursive calls            0  db block gets         1249  consistent gets            0  physical reads            0  redo size          526  bytes sent via SQL*Net to client          520  bytes received via SQL*Net from client            2  SQL*Net roundtrips to/from client            0  sorts (memory)            0  sorts (disk)            1  rows processed  LHR@dlhr> execute dbms_sqltune.accept_sql_profile(task_name =>'sql_profile_test_SQLID', task_owner => 'LHR', replace => TRUE);    PL/SQL procedure successfully completed.    LHR@dlhr> set autot off  LHR@dlhr>  SELECT e.task_name, b.name, d.sql_text, extractvalue(value(h), '.') as hints    2     FROM dba_sql_profiles d,    3           dba_advisor_tasks e,    4          SYS.SQLOBJ$DATA A,    5          SYS.SQLOBJ$ B,    6          TABLE(XMLSEQUENCE(EXTRACT(XMLTYPE(A.COMP_DATA),    7                                    '/outline_data/hint'))) h    8    where a.signature = b.signature    9      and a.category = b.category   10      and a.obj_type = b.obj_type   11      and a.plan_id = b.plan_id   12      and a.signature = d.signature   13      and d.task_id=e.task_id   14      and d.name = 'SYS_SQLPROF_0154e728ad3f0000'   15     ;    TASK_NAME                      NAME  ------------------------------ ------------------------------  SQL_TEXT  ----------------------------------------------------------------------------------------------------  HINTS  ----------------------------------------------------------------------------------------------------  sql_profile_test               SYS_SQLPROF_0154e728ad3f0000  select /*+no_index(TB_LHR_20160525_01 TB_LHR_20160525_01_idx)*/count(*) from lhr.TB_LHR_20160525_01  where object_id = 100  OPTIMIZER_FEATURES_ENABLE(default)    sql_profile_test               SYS_SQLPROF_0154e728ad3f0000  select /*+no_index(TB_LHR_20160525_01 TB_LHR_20160525_01_idx)*/count(*) from lhr.TB_LHR_20160525_01  where object_id = 100  IGNORE_OPTIM_EMBEDDED_HINTS  

在這裡用了包DBMS_SQLTUNE的另一個函數:ACCEPT_SQL_PROFILE。其中,參數task_name即我們創建的優化建議任務的名稱,name是profile的名字,可以是任意合法名稱。此外這個函數還有其他一些函數,下面是這個函數的原型:

DBMS_SQLTUNE.ACCEPT_SQL_PROFILE (     task_name    IN  VARCHAR2,     object_id    IN  NUMBER   := NULL,     name         IN  VARCHAR2 := NULL,     description  IN  VARCHAR2 := NULL,     category     IN  VARCHAR2 := NULL;     task_owner   IN VARCHAR2  := NULL,     replace      IN BOOLEAN   := FALSE,     force_match  IN BOOLEAN   := FALSE)   RETURN VARCHAR2;  

Description是profile的描述資訊;task_owner是優化建議任務的所有者;replace為TRUE時,如果這個profile已經存在,就代替它;force_match為TURE時,表示與語句強制匹配,即強制使用綁定變數,和系統參數cursor_sharing設置為FORCE時類似,為FALSE時,與cursor_sharing設置為EXACT時類似,即完全匹配。

這裡要特別提到的是category這個參數,你可以通過設置這個參數,制定特定會話使用這個profile。在10g中,每個會話都有一個新參數SQLTUNE_CATEGORY,他的默認值是DEFAULT。而我們在調用這個函數時,如果沒有指定這個參數,那它的值也是DEFAULT,而如果我們給這個profile指定了一個其它的CATEGORY值,如FOR_TUNING,那麼只有會話參SQLTUNE_CATEGORY也為FOR_TUNING時,才會使用這個porfile。為什麼說這個參數很有用呢?試想一個這樣的環境:你在一個生產系統上利用STA調優一條語句,STA已經給出了優化建議,但是你又不敢貿然實施它給出的建議(畢竟它只是機器嘛,不能完全信任),你就可以創建一個有特殊CATEGORY的profile,然後在你自己的會話中制定SQLTUNE_CATEGORY為這個特殊的CATEGORY,那就既可以看優化建議的實際效果又不影響生產環境。

此外可以通過視圖DBA_SQL_PROFILES來查看已經創建的profile。

第五步:查看profile的效果

LHR@dlhr> set autot on  LHR@dlhr> select /*+no_index(TB_LHR_20160525_01 TB_LHR_20160525_01_idx)*/count(*) from lhr.TB_LHR_20160525_01 where object_id = 100 ;      COUNT(*)  ----------           1      Execution Plan  ----------------------------------------------------------  Plan hash value: 661515879    --------------------------------------------------------------------------------------------  | Id  | Operation         | Name                   | Rows  | Bytes | Cost (%CPU)| Time     |  --------------------------------------------------------------------------------------------  |   0 | SELECT STATEMENT  |                        |     1 |     5 |     1   (0)| 00:00:01 |  |   1 |  SORT AGGREGATE   |                        |     1 |     5 |            |          |  |*  2 |   INDEX RANGE SCAN| TB_LHR_20160525_01_IDX |     1 |     5 |     1   (0)| 00:00:01 |  --------------------------------------------------------------------------------------------    Predicate Information (identified by operation id):  ---------------------------------------------------       2 - access("OBJECT_ID"=100)    Note  -----     - SQL profile "SYS_SQLPROF_0154e728ad3f0000" used for this statement      Statistics  ----------------------------------------------------------            1  recursive calls            0  db block gets            2  consistent gets            0  physical reads            0  redo size          526  bytes sent via SQL*Net to client          520  bytes received via SQL*Net from client            2  SQL*Net roundtrips to/from client            0  sorts (memory)            0  sorts (disk)            1  rows processed  

從NOTE部分可以看到,語句採用了profile中的數據,創建了新的查詢計劃。並且在查詢計劃中還有一些附加資訊,表明這個語句是採用了「SYS_SQLPROF_0154e728ad3f0000」這個Profile,而不是根據對象上面的統計數據來生成的查詢計劃。

但上述方法主要是依賴sql tuning advisor,如果它無法生成你想要的執行計劃.你還可以通過手動的方式,通過sql profile把hint加進去.複雜的SQL的hint可以採用腳本coe_xfr_sql_profile.sql來產生原語句的outline data和加hint語句的outline data,然後替換對應的SYS.SQLPROF_ATTR,最後執行生成的sql就可以了。

使用PLSQL DEVELOPER 11查看執行計劃,如下圖,新版本的好處:

本文選自《Oracle程式設計師面試筆試寶典》,作者:李華榮。