使用SQL tuning advisor(STA)自动优化SQL

      Oracle 10g之后的优化器支持两种模式,一个是normal模式,一个是tuning模式。在大多数情况下,优化器处于normal模式。基于CBO的normal模式只考虑很小部分的执行计划集合用于选择哪个执行计划,因为它需要在尽可能短的时间,通常是几秒或毫秒级来对当前的SQL语句进行解析并生成执行计划。因此并不能保证SQL语句每次都是使用最佳的执行计划。而tuning模式则将高负载的SQL语句直接扔给优化器,优化器来自动对其进行详细的分析,调试并给出建议,这就是Oracle 提供的Automatic Tuning Optimizer,即自动调整优化器。Oracle 自动调整优化器通过SQL调优建议器(SQL tuning advisor)来体现。

 

1、SQL tuning的基本步骤
     a、鉴别需要调整的高负载SQL或者Top SQL
     b、寻找可改进的执行计划
     c、实施能够改进的执行计划以提高SQL效率
  
2、如何tuning SQL
     a、检查是否为优化器设置了合理的参数(optimizer_mode,optimizer_index_caching,optimizer_index_cost_adj,以及相关cache size)
     b、检查SQL语句所涉及的对象是否存在过时的统计信息或者倾斜列是否缺少直方图等
     c、通过添加提示来引导SQL语句使用正确的访问路径,以及连接方式等
     d、重构等价的SQL语句以使得SQL更高效(如最小化基表及中间结果集,避免列运算,列上的函数,null值,不等运算使得索引失效)
     e、添加合理的索引或物化视图以及移除冗余索引,分散I/O等
  
3、Automatic Tuning Optimizer 做什么?
     a、分析统计信息
         优化器执行计划产生期间记录当前SQL语句涉及对象的统计信息的类型以及哪些被使用或哪些是需要的
         当统计信息记录完成后自动调整优化器会比对与查询相关的这些对象的统计信息是否可用或过时或非均衡列缺少直方图等
         针对上述的操作之后得到哪些对象没有统计信息以及哪些对象缺少统计信息以及额外的统计信息用于生成report     
     b、分析访问路径
         优化器会分析当前SQL所使用的访问路径是否合理,也就是分析基于表的访问方式,如全表扫描,索引扫描等
         自动调整优化器会基于谓词尝试假设性的推断来创建合理的索引,也就是建议通过添加或修改相应的索引来提高性能
     c、SQL结构分析
         优化器会建议对于一些具有较大影响的SQL语句作结构性调整及转换(基于内部规则),如未嵌套的子查询,重写物化视图,视图合并等
         基于语法以及语义结构的分析与调整,如谓词列上的运算,UNION与UNION ALL的使用,NOT IN, NOT EXIST之间替换等
         对中间结果集以及连接方式等实现一些预估的分析
     d、SQL profiling
         SQL profiling 内置于优化器,就是一个剖析工具,基于上述得到的信息对当前的SQL进行剖析,以检查出导致性能糟糕的故障点
         所有上述分析得到的结果以及辅助信息最后以sql profile的形式表现出来,供用户来判断是否接受
         当用户接受这些profile,下次处于normal模式时,相同的sql语句会使用这个profile
         可以对profile进行启用,停用,以及修改,因此即使表发生较大的变化,profile依旧能使得SQL受益

 

4、Automatic Tuning Optimizer与SQL tuning advisor结构图 

 

5、STA可tuning的方式
     STA提供OEM图形界面以及API方式进行tuning,本文主要描述API即dbms_sqltune.create_tuning_task方式
     下面是可被create_tuning_task接受的API方式
       a、直接提供SQL语句文本
       b、引用共享池中的SQL语句(sql_id)
       c、引用awr自动工作负载中的SQL语句(sql_id)
       d、建议SQL调优集(批量tuning)
  
6、演示SQL tuning 

--环境
scott@ORA11G> select * from v$version where rownum<2;

BANNER
--------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - Production

--创建演示表 
scott@ORA11G> CREATE TABLE t
  2  NOLOGGING
  3  AS
  4     SELECT *
  5       FROM dba_source,
  6            (    SELECT *
  7                   FROM DUAL
  8             CONNECT BY ROWNUM < 5);

Table created.

--执行SQL 语句
scott@ORA11G> SELECT COUNT (*)
  2    FROM t a
  3   WHERE a.ROWID > (SELECT MIN (b.ROWID)
  4                      FROM t b
  5                     WHERE a.owner = b.owner AND a.name = b.name AND a.TYPE = b.TYPE AND a.line = b.line);

  COUNT(*)
----------
   1872756

1 row selected.

--开始SQL自动调整并报告结果
--脚本tune_last_sql.sql中包含了创建调优任务、开始执行调优、以及报告调优成果。脚本内容见文章尾部
scott@ORA11G> @tune_last_sql

RECS
-----------------------------------------------------------------------------------------
GENERAL INFORMATION SECTION
-------------------------------------------------------------------------------
Tuning Task Name   : TASK_833
Tuning Task Owner  : SCOTT
Workload Type      : Single SQL Statement
Scope              : COMPREHENSIVE
Time Limit(seconds): 1800
Completion Status  : COMPLETED
Started at         : 05/22/2013 15:06:06
Completed at       : 05/22/2013 15:07:17

-------------------------------------------------------------------------------
Schema Name: SCOTT
SQL ID     : 44tg722u0ypqh
SQL Text   : SELECT COUNT (*)
               FROM t a
              WHERE a.ROWID > (SELECT MIN (b.ROWID)
                                 FROM t b
                                WHERE a.owner = b.owner AND a.name = b.name
             AND a.TYPE = b.TYPE AND a.line = b.line)

-------------------------------------------------------------------------------
FINDINGS SECTION (1 finding)
-------------------------------------------------------------------------------

1- Statistics Finding
---------------------
  Table "SCOTT"."T" was not analyzed.

  Recommendation
  --------------
  - Consider collecting optimizer statistics for this table.
    execute dbms_stats.gather_table_stats(ownname => 'SCOTT', tabname => 'T',
            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.

-------------------------------------------------------------------------------
EXPLAIN PLANS SECTION
-------------------------------------------------------------------------------

1- Original
-----------
Plan hash value: 1985065416

-----------------------------------------------------------------------------------------
| Id  | Operation             | Name    | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |
-----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |         |     1 |   134 |       | 42648   (1)| 00:08:32 |
|   1 |  SORT AGGREGATE       |         |     1 |   134 |       |            |          |
|*  2 |   HASH JOIN           |         |   129K|    16M|   195M| 42648   (1)| 00:08:32 |
|   3 |    TABLE ACCESS FULL  | T       |  2590K|   165M|       | 11596   (1)| 00:02:20 |
|   4 |    VIEW               | VW_SQ_1 |  2590K|   165M|       | 11674   (1)| 00:02:21 |
|   5 |     HASH GROUP BY     |         |  2590K|   165M|       | 11674   (1)| 00:02:21 |
|   6 |      TABLE ACCESS FULL| T       |  2590K|   165M|       | 11596   (1)| 00:02:20 |
-----------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - access("A"."OWNER"="ITEM_1" AND "A"."NAME"="ITEM_2" AND
              "A"."TYPE"="ITEM_3" AND "A"."LINE"="ITEM_4")
       filter("A".ROWID>"MIN(B.ROWID)")

--上面的report总共分为3个部分,分别是SQL调优的基本信息、SQL调优的建议findings、以及SQL对应的执行计划部分
--在基本信息部分包含了SQL调优的任务名称,状态,执行,完成时间,对应的SQL完整语句等
--在finding部分则给出本次调优所得到的成果,如本次是提示缺少统计信息
--在执行计划部分则给出了当前SQL语句的执行计划以及谓词信息

-->接下来根据建议来收集统计信息
scott@ORA11G> BEGIN
  2     DBMS_STATS.gather_table_stats (ownname            => 'SCOTT',
  3                                    tabname            => 'T',
  4                                    estimate_percent   => DBMS_STATS.auto_sample_size,
  5                                    method_opt         => 'FOR ALL COLUMNS SIZE AUTO');
  6  END;
  7  /

PL/SQL procedure successfully completed.

-->对原SQL语句增加order提示并执行
scott@ORA11G> SELECT /*+ ordered */COUNT (*)
  2    FROM t a
  3   WHERE a.ROWID > (SELECT MIN (b.ROWID)
  4                      FROM t b
  5                     WHERE a.owner = b.owner AND a.name = b.name AND a.TYPE = b.TYPE AND a.line = b.line);

  COUNT(*)
----------
   1872756

1 row selected.

--再次调优SQL语句
scott@ORA11G> @tune_last_sql

RECS
-----------------------------------------------------------------------------------------------
GENERAL INFORMATION SECTION
-------------------------------------------------------------------------------
Tuning Task Name   : TASK_849
Tuning Task Owner  : SCOTT
Workload Type      : Single SQL Statement
Scope              : COMPREHENSIVE
Time Limit(seconds): 1800
Completion Status  : COMPLETED
Started at         : 05/22/2013 21:26:07
Completed at       : 05/22/2013 21:26:42

-------------------------------------------------------------------------------
Schema Name: SCOTT
SQL ID     : fsp3852n56gf8
SQL Text   : SELECT /*+ ordered */COUNT (*)
             FROM t a
             WHERE a.ROWID > (SELECT MIN (b.ROWID) from t b
             WHERE a.owner = b.owner AND a.name = b.name AND a.TYPE = b.TYPE
             AND a.line = b.line)

-------------------------------------------------------------------------------
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: 67.95%)
  ------------------------------------------
  - Consider accepting the recommended SQL profile.
    execute dbms_sqltune.accept_sql_profile(task_name => 'TASK_849',
            task_owner => 'SCOTT', replace => TRUE);

-------------------------------------------------------------------------------
EXPLAIN PLANS SECTION
-------------------------------------------------------------------------------

1- Original With Adjusted Cost
------------------------------
Plan hash value: 2929971977

--------------------------------------------------------------------------------------------
| Id  | Operation              | Name      | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT       |           |     1 |       |       |   218K  (1)| 00:43:47 |
|   1 |  SORT AGGREGATE        |           |     1 |       |       |            |          |
|   2 |   VIEW                 | VM_NWVW_2 |   551K|       |       |   218K  (1)| 00:43:47 |
|*  3 |    FILTER              |           |       |       |       |            |          |
|   4 |     HASH GROUP BY      |           |   551K|    51M|  1197M|   218K  (1)| 00:43:47 |
|*  5 |      HASH JOIN         |           |    11M|  1031M|   145M| 37646   (1)| 00:07:32 |
|   6 |       TABLE ACCESS FULL| T         |  2497K|   116M|       | 11596   (1)| 00:02:20 |
|   7 |       TABLE ACCESS FULL| T         |  2497K|   116M|       | 11596   (1)| 00:02:20 |
--------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   3 - filter("A".ROWID>MIN("B".ROWID))
   5 - access("A"."OWNER"="B"."OWNER" AND "A"."NAME"="B"."NAME" AND
              "A"."TYPE"="B"."TYPE" AND "A"."LINE"="B"."LINE")

2- Using SQL Profile
--------------------
Plan hash value: 1985065416

-----------------------------------------------------------------------------------------
| Id  | Operation             | Name    | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |
-----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |         |     1 |   116 |       | 70117   (1)| 00:14:02 |
|   1 |  SORT AGGREGATE       |         |     1 |   116 |       |            |          |
|*  2 |   HASH JOIN           |         |  2025K|   224M|   145M| 70117   (1)| 00:14:02 |
|   3 |    TABLE ACCESS FULL  | T       |  2497K|   116M|       | 11596   (1)| 00:02:20 |
|   4 |    VIEW               | VW_SQ_1 |  2497K|   159M|       | 41851   (1)| 00:08:23 |
|   5 |     HASH GROUP BY     |         |  2497K|   116M|   153M| 41851   (1)| 00:08:23 |
|   6 |      TABLE ACCESS FULL| T       |  2497K|   116M|       | 11596   (1)| 00:02:20 |
-----------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - access("A"."OWNER"="ITEM_1" AND "A"."NAME"="ITEM_2" AND
              "A"."TYPE"="ITEM_3" AND "A"."LINE"="ITEM_4")
       filter("A".ROWID>"MIN(B.ROWID)")

-------------------------------------------------------------------------------

--针对上述的SQL语句,SQL调优器找到了一个更为高效的执行计划,并提示我们接受该执行计划,如下
--A potentially better execution plan was found for this statement.
--Recommendation (estimated benefit: 67.95%)
--Consider accepting the recommended SQL profile

--Author : Robinson
--Blog   : http://blog.csdn.net/robinson_0612

--接受SQL profile
scott@ORA11G> exec DBMS_SQLTUNE.accept_sql_profile (task_name => 'TASK_849', task_owner => 'SCOTT', REPLACE => TRUE);

PL/SQL procedure successfully completed.

--当接受SQL profile后,我们再次来执行原来带order提示的SQL语句
scott@ORA11G> set autot trace exp;
scott@ORA11G> SELECT /*+ ordered */COUNT (*)
  2               FROM t a
  3               WHERE a.ROWID > (SELECT MIN (b.ROWID) from t b
  4               WHERE a.owner = b.owner AND a.name = b.name AND a.TYPE = b.TYPE
  5               AND a.line = b.line);

Execution Plan
----------------------------------------------------------
Plan hash value: 1985065416

-----------------------------------------------------------------------------------------
| Id  | Operation             | Name    | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |
-----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |         |     1 |   116 |       | 70117   (1)| 00:14:02 |
|   1 |  SORT AGGREGATE       |         |     1 |   116 |       |            |          |
|*  2 |   HASH JOIN           |         |  2025K|   224M|   145M| 70117   (1)| 00:14:02 |
|   3 |    TABLE ACCESS FULL  | T       |  2497K|   116M|       | 11596   (1)| 00:02:20 |
|   4 |    VIEW               | VW_SQ_1 |  2497K|   159M|       | 41851   (1)| 00:08:23 |
|   5 |     HASH GROUP BY     |         |  2497K|   116M|   153M| 41851   (1)| 00:08:23 |
|   6 |      TABLE ACCESS FULL| T       |  2497K|   116M|       | 11596   (1)| 00:02:20 |
-----------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - access("A"."OWNER"="ITEM_1" AND "A"."NAME"="ITEM_2" AND
              "A"."TYPE"="ITEM_3" AND "A"."LINE"="ITEM_4")
       filter("A".ROWID>"MIN(B.ROWID)")

Note
-----
   - SQL profile "SYS_SQLPROF_013ecc70b5f70000" used for this statement

scott@ORA11G> set autot off;

--上面的autotrace中,最后一部分表明当前的SQL语句使用了存储的SQL profile的执行计划

7、相关视图
     DBA_ADVISOR_LOG
     DBA_ADVISOR_TASKS
     DBA_ADVISOR_FINDINGS
     DBA_ADVISOR_RECOMMENDATIONS
     DBA_ADVISOR_RATIONALE
     DBA_SQLTUNE_STATISTICS
     DBA_SQLTUNE_BINDS
     DBA_SQLTUNE_PLANS

8、演示用到的脚本

SET ECHO OFF TERMOUT ON FEEDBACK OFF VERIFY OFF  
SET SCAN ON
SET LONG 1000000 LINESIZE 180
COL recs FORMAT a135

VARIABLE tuning_task VARCHAR2(30)

DECLARE
  l_sql_id v$session.prev_sql_id%TYPE;
BEGIN
  SELECT prev_sql_id INTO l_sql_id
  FROM v$session
  WHERE audsid = userenv('SESSIONID');
  
  :tuning_task := dbms_sqltune.create_tuning_task(sql_id => l_sql_id);
  dbms_sqltune.execute_tuning_task(:tuning_task);
END;
/

SELECT dbms_sqltune.report_tuning_task(:tuning_task) as recs 
FROM dual;

SET VERIFY ON FEEDBACK ON 

 Oracle&nbsp;牛鹏社

更多参考

DML Error Logging 特性 

PL/SQL --> 游标

PL/SQL --> 隐式游标(SQL%FOUND)

批量SQL之 FORALL 语句

批量SQL之 BULK COLLECT 子句

PL/SQL 集合的初始化与赋值

PL/SQL 联合数组与嵌套表
PL/SQL 变长数组
PL/SQL --> PL/SQL记录

SQL tuning 步骤

高效SQL语句必杀技

父游标、子游标及共享游标

绑定变量及其优缺点

dbms_xplan之display_cursor函数的使用

dbms_xplan之display函数的使用

执行计划中各字段各模块描述

使用 EXPLAIN PLAN 获取SQL语句执行计划

更优更快 人工智能自动SQL优化----------http://www.sina.com.cn 2001/12/12 17:48 中国电脑教育报文/SQL爱好者  所谓SQL,就是指Structured Query Language(结构化查询语言),它是目前使用最广泛的数据库语言,用来和数据库打交道,从数据库中得到用户需要的数据。但是要想熟练使用SQL语句,也不是一件简单的事,有些语句使用起来也比较麻烦。如果我们对SQL语句进行优化,那么用户使用起来 就会方便许多。  简单来说,SQL语句的优化就是将性能低下的SQL语句转换成达到同样目的的性能优异的SQL语句。人工智能自动SQL优化就是使用人工智能技术,自动SQL语句进行重写,找到性能最好的等效SQL语句。  人工智能自动SQL 优化  随着人工智能技术的发展和在数据库优化领域应用的深入,在20世纪90年代末终于出现了突破性的进展——人工智能自动SQL优化。目前在商用数据库领域LECCO TechnologyLimited(灵高公司)拥有该技术并提供使用该技术的自动优化产品——LECCO SQL Expert,其支持Oracle、Sybase、MS SQLServer和IBMDB2数据库平台。该产品针对数据库应用的开发和维护阶段提供了几个特别的模块:SQL语法优化器、PL/SQL集成化开发调试环境(IDE)、扫描器、数据库监视器等。图1 人工智能自动SQL优化示意图  其核心模块之一“SQL语法优化器”的工作原理大致如下(如图1):  一条源SQL语句输入→“人工智能反馈式搜索引擎”对输入的SQL语句结合检测到的数据库结构和索引进行重写,产生N条等效的SQL语句输出→产生的N条等效SQL语句再送入“人工智能反馈式搜索引擎”进行重写,直至无法产生新的输出或搜索限额满→对 输出的SQL语句进行过滤,选出具有不同执行计划的SQL语句(即不同的执行效率)→对得到的SQL语句进行批量测试,找出性能最好的SQL语句。图2 优化前的SQL语句  自动优化实例  假设我们从源代码中抽取出这条SQL语句(如图2):  SELECTCOUNT(*)FROMEMPLOY-EE WHEREEXISTS(SELECT'X'FROM DEPARTMENTswheresEMP_DEPT=DPT_IDAND DPT_NAME LIKE'AC%')AND EMP_IDIN(SELECT SAL_EMP_IDFROM EMP_SAL_HISTB WHERESAL_SALARY>70000)   按“优化”按钮后,经过十几秒,SQL Expert就完成了优化的过程,从优化细节中可以看到,它在十几秒的时间内重写产生了2267条等价的SQL语句,其中136条SQL语句有不同的执行计划(如图3)。图3 优化结果  接下来我们可以对自动重写产生的136条具有不同执行计划的SQL语句进行批运行测试,以选出性能最佳的等效SQL语句。按下“批运行”按钮,在“终止条件”页选择“最佳运行时间SQL语句”(如图4),按“确定”。图4 测试条件  经过几分钟的测试运行后,我们可以发现SQL124的运行时间和反应时间最短。运行速度约有22.75倍的提升(源SQL语句运行时间为2.73秒,SQL124运行时间为0.12秒,如图5)。图5 测试结果  我们把SQL124放入源代码中,结束一条SQL语句的优化工作。从上例可以看到,LECCO SQL Expert的自动重写技术使原来需要几小时才能完成的SQL语句的优化工作,缩减到几分钟之内就可以完成。数据库管理员和开发人员可以从繁重的SQL语句优化工作中解脱出来。  边做边学式训练  LECCO SQL Expert不仅能够找到最佳的SQL语句,而且提供的“边做边学式训练”还能够教会开发人员和数据库管理员如何写出性能最好的SQL语句。LECCO SQL Expert的“SQL比较器”可以标明源SQL和待选SQL之间的不同之处。LECCO SQL Expert详尽的上下文敏感帮助系统可以指出执行计划的深层含义。图6 源语句与SQL124的比较  以上面优化的结果为例,为了查看源SQL语句和SQL124在写法上的不同,我们可以按下“比较器”按钮,对SQL124和源SQL语句进行比较。如果选择“双向比较”复选框,“SQL比较器”可以将两条互相间的不同之处以蓝色表示。当然,你也可以从 源语句和重写后的SQL语句中任选两条进行比较(如图6)。  从比较的结果可以看到,重写得到的SQL124把第一个Exists改写成了In;在字段DPT_ID上进行了合并空字符串的操作以诱导数据库先执行子查询中的(SELECTDPT_ID||'FROMDEPART-MENTWH
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

清风智语

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值