阿里云-云小站(无限量代金券发放中)
【腾讯云】云服务器、云数据库、COS、CDN、短信等热卖云产品特惠抢购

Automatic的SQL Profile来稳定执行计划

145次阅读
没有评论

共计 14915 个字符,预计需要花费 38 分钟才能阅读完成。

我们都希望 Oracle 数据库的执行的 SQL,CBO 都能够产生正确的执行计划,但是事实上由于各种原因(例如 SQL 所对应的对应的统计信息不准确,或者 CBO 内部一些计算公式的缺陷等),导致了 CBO 会产生效率不高的,甚至是错误的执行计划。特别是 CBO 对目标 SQL 所产生的初始执行计划是正确的,后来由于各种原因(比如统计信息的变更),导致了 CBO 重新产生了一个错误的执行计划,这种执行计划的改变往往会导致目标 SQL 执行时间呈一个数量级的递增,而且通常会给我们造成一个困惑,一条 SQL 原本可以正常的运行,但是为什么会突然变得很慢?其实这种 SQL 执行效率突然的衰减往往是因为目标 SQL 执行计划的改变。这时候我们可以使用 SQL_Profile 或者 SPM 来解决执行计划变更的问题,用他们来调整稳定目标的 SQL 执行计划。下面进行一个 Automatic 的 SQL Profile 来稳定执行计划的实验。

1. 创建一个测试表并插入数据,并创建相对应的索引
SQL> create table t1(n number);

 Table created.

 SQL> declare
  2  begin
  3  for i in 1 .. 10000
  4  loop
  5  insert into t1 values(i);
  6  commit;
  7  end loop;
  8  end;
  9  /

 PL/SQL procedure successfully completed.

 SQL> select count(*) from t1;

  COUNT(*)
 ———-
      10000

 SQL> create index idx_t1 on t1(n);

 Index created.

 2. 对表 T1 收集统计信息

SQL> exec dbms_stats.gather_table_stats(ownname =>’SYS’,tabname =>’T1′,method_opt =>’for all columns size 1′,CASCADE =>true);

 PL/SQL procedure successfully completed.

 3. 使用 hint 强制不使用索引,来模拟那些执行计划错误的 SQL,并查看执行计划。
SQL> select /*+ no_index(t1 idx_t1) */ * from t1 where n=1;

 N
 ———-
 1

 SQL> select * from table(dbms_xplan.display_cursor(null,null,’advanced’));

 PLAN_TABLE_OUTPUT
 ——————————————————————————–
 SQL_ID 1kg76709mx29d, child number 0
 ————————————-
 select /*+ no_index(t1 idx_t1) */ * from t1 where n=1

 Plan hash value: 3617692013

 ————————————————————————–
 | Id  | Operation  | Name | Rows  | Bytes | Cost (%CPU)| Time |
 ————————————————————————–
 |  0 | SELECT STATEMENT  | | | |    7 (100)| |
 |*  1 |  TABLE ACCESS FULL| T1 |    1 |    4 |    7  (0)| 00:00:01 |

 PLAN_TABLE_OUTPUT
 ——————————————————————————–
 ————————————————————————–

 Query Block Name / Object Alias (identified by operation id):
 ————————————————————-

    1 – SEL$1 / T1@SEL$1

 Outline Data
 ————-

  /*+

 PLAN_TABLE_OUTPUT
 ——————————————————————————–
      BEGIN_OUTLINE_DATA
      IGNORE_OPTIM_EMBEDDED_HINTS
      OPTIMIZER_FEATURES_ENABLE(‘11.2.0.4’)
      DB_VERSION(‘11.2.0.4’)
      ALL_ROWS
      OUTLINE_LEAF(@”SEL$1″)
      FULL(@”SEL$1″ “T1″@”SEL$1”)
      END_OUTLINE_DATA
  */

 Predicate Information (identified by operation id):

 PLAN_TABLE_OUTPUT
 ——————————————————————————–
 —————————————————

    1 – filter(“N”=1)

 Column Projection Information (identified by operation id):
 ———————————————————–

    1 – “N”[NUMBER,22]

 42 rows selected.

从上面的内容我们不难发现,这条 sql 语句所走的是全表扫描,但是这显然是个错误的执行计划,正确的执行计划,我们应该是走索引。

 我们现在使用 SQL Tuning Advisor 来尝试对这条 SQL 进行通过产生 Automatic 类型的 SQL Profile

 4. 创建一个名为 my_sql_tuning_task2 的自动调整任务

SQL> declare
  2  my_task_name varchar2(30);
  3  my_sqltext CLOB;
  4  BEGIN
  5  my_sqltext :=’select /*+ no_index(t1 idx_t1) */ * from t1 where n=1′;
  6  my_task_name := dbms_sqltune.create_tuning_task(
  7  sql_text => my_sqltext,
  8  user_name => ‘SYS’, 
  9  scope => ‘COMPREHENSIVE’,
  10 time_limit => 60,
  11 task_name => ‘my_sql_tuning_task_2’,
  12 description =>’TASK to tune a query on table t1′);
  13 END;
  14  /
 
 PL/SQL procedure successfully completed.

然后执行上述自动调整任务

SQL> begin
  2  dbms_sqltune.execute_tuning_task(task_name => ‘my_sql_tuning_task_2’);
  3  end;
  4  /

 PL/SQL procedure successfully completed.

然后我们就可以使用 DBMS_SQLTUNE.REPORT_TUNING_TASK 来查看上述自动调整任务的调整结果:
SQL> set long 9000
 SQL> set longchunksize 1000
 SQL> set linesize 800
 SQL> select dbms_sqltune.report_tuning_task(‘my_sql_tuning_task_2’) from dual;

 DBMS_SQLTUNE.REPORT_TUNING_TASK(‘MY_SQL_TUNING_TASK_2’)
 ————————————————————————————————-
 Tuning Task Name  : my_sql_tuning_task_2
 Tuning Task Owner  : SYS
 Workload Type  : Single SQL Statement
 Scope  : COMPREHENSIVE
 Time Limit(seconds): 60
 Completion Status  : COMPLETED
 Started at  : 04/13/2016 23:08:28
 Completed at  : 04/13/2016 23:08:28

 DBMS_SQLTUNE.REPORT_TUNING_TASK(‘MY_SQL_TUNING_TASK_2’)
 —————————————————————————————————
 Schema Name: SYS
 SQL ID  : 4bh6sn1zvpgq7
 SQL Text  : select /*+ no_index(t1 idx_t1) */ * from t1 where n=1

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

 1- SQL Profile Finding (see explain plans section below)
 ——————————————————–

 DBMS_SQLTUNE.REPORT_TUNING_TASK(‘MY_SQL_TUNING_TASK_2’)
 —————————————————————————–

  Recommendation (estimated benefit: 90.91%)
  ——————————————
  – Consider accepting the recommended SQL profile.
    execute dbms_sqltune.accept_sql_profile(task_name =>
    ‘my_sql_tuning_task_2’, task_owner => ‘SYS’, replace => TRUE);

  Validation results
  ——————
  The SQL profile was tested by executing both its plan and the original plan

 DBMS_SQLTUNE.REPORT_TUNING_TASK(‘MY_SQL_TUNING_TASK_2’)

 DBMS_SQLTUNE.REPORT_TUNING_TASK(‘MY_SQL_TUNING_TASK_2’)
 ————————————————————————————————
  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.

 DBMS_SQLTUNE.REPORT_TUNING_TASK(‘MY_SQL_TUNING_TASK_2’)
 ————————————————————————————-

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

 1- Original With Adjusted Cost
 ——————————
 Plan hash value: 3617692013

 ————————————————————————–
 | Id  | Operation  | Name | Rows  | Bytes | Cost (%CPU)| Time |

 DBMS_SQLTUNE.REPORT_TUNING_TASK(‘MY_SQL_TUNING_TASK_2’)
 ———————————————————————————————
 |  0 | SELECT STATEMENT  | |    1 |    4 |    7  (0)| 00:00:01 |
 |*  1 |  TABLE ACCESS FULL| T1 |    1 |    4 |    7  (0)| 00:00:01 |
 ————————————————————————–

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

    1 – filter(“N”=1)

 2- Using SQL Profile

 DBMS_SQLTUNE.REPORT_TUNING_TASK(‘MY_SQL_TUNING_TASK_2’)
 ————————————————————————————————-
 Plan hash value: 1369807930

 —————————————————————————
 | Id  | Operation | Name  | Rows  | Bytes | Cost (%CPU)| Time  |
 —————————————————————————
 |  0 | SELECT STATEMENT |  | 1 | 4 | 1  (0)| 00:00:01 |
 |*  1 |  INDEX RANGE SCAN| IDX_T1 | 1 | 4 | 1  (0)| 00:00:01 |
 —————————————————————————

 Predicate Information (identified by operation id):

 DBMS_SQLTUNE.REPORT_TUNING_TASK(‘MY_SQL_TUNING_TASK_2’)
 ————————————————————————————————

    1 – access(“N”=1)

 ——————————————————————————-

从上面的调整结果,我们可以看到,他已经为我们目标 SQL 找到了更好的执行计划,并且也完成了针对该 SQL 的 Automatic 类型的 SQL Profile,如果我们使用    execute dbms_sqltune.accept_sql_profile(task_name =>  ‘my_sql_tuning_task_2’, task_owner => ‘SYS’, replace => TRUE);
相应时间将会有 89.9% 的改善,逻辑读将会有 90.9 % 的改善,并且接受后将会有全表扫描改变为 IDX_T1 的索引范围扫描。

 然后我们按照 oracle 提示接受这个 SQL profile,并重新查看执行计划
SQL>  execute dbms_sqltune.accept_sql_profile(task_name =>  ‘my_sql_tuning_task_2’, task_owner => ‘SYS’, replace => TRUE);

 PL/SQL procedure successfully completed.

 

SQL> select /*+ no_index(t1 idx_t1) */ * from t1 where n=1;

 N
 ———-
 1

 SQL>  select * from table(dbms_xplan.display_cursor(null,null,’advanced’));

 PLAN_TABLE_OUTPUT
 —————————————————————————————————
 SQL_ID 1kg76709mx29d, child number 0
 ————————————-
 select /*+ no_index(t1 idx_t1) */ * from t1 where n=1

 Plan hash value: 1369807930

 —————————————————————————
 | Id  | Operation | Name  | Rows  | Bytes | Cost (%CPU)| Time  |
 —————————————————————————
 |  0 | SELECT STATEMENT |  |  |  | 1 (100)|  |
 |*  1 |  INDEX RANGE SCAN| IDX_T1 | 1 | 4 | 1  (0)| 00:00:01 |

 PLAN_TABLE_OUTPUT
 —————————————————————————

 Query Block Name / Object Alias (identified by operation id):
 ————————————————————-

    1 – SEL$1 / T1@SEL$1

 Outline Data
 ————-

  /*+

 PLAN_TABLE_OUTPUT
 ———————————————————————————————————–
      BEGIN_OUTLINE_DATA
      IGNORE_OPTIM_EMBEDDED_HINTS
      OPTIMIZER_FEATURES_ENABLE(‘11.2.0.4’)
      DB_VERSION(‘11.2.0.4’)
      ALL_ROWS
      OUTLINE_LEAF(@”SEL$1″)
      INDEX(@”SEL$1″ “T1″@”SEL$1” (“T1″.”N”))
      END_OUTLINE_DATA
  */

 Predicate Information (identified by operation id):

 PLAN_TABLE_OUTPUT
 ————————————————

    1 – access(“N”=1)

 Column Projection Information (identified by operation id):
 ———————————————————–

    1 – “N”[NUMBER,22]

 Note
 —–

 PLAN_TABLE_OUTPUT
 ————————————————-
    – SQL profile SYS_SQLPROF_0154103a51870000 used for this statement

 46 rows selected.

 

我们可以看到 Note 部分 SQL profile SYS_SQLPROF_0154103a51870000 used for this statement,这说明我们刚才接受的 SQL Profile 已经生效了,这同时也说明 Automatic 类型的 SQL Profile 确实可以再不改变目标 SQL 的 SQl 文本的情况下更改其执行计划

 接下来我们尝试将 where 的条件从 n = 1 改变为 n =2, 并查看执行计划 SQL>  select /*+ no_index(t1 idx_t1) */ * from t1 where n=2;

 N
 ———-
 2

 SQL>  select * from table(dbms_xplan.display_cursor(null,null,’advanced’));

 PLAN_TABLE_OUTPUT
 ——————————————————————————–
 SQL_ID 36wrvgrswajnh, child number 0
 ————————————-
  select /*+ no_index(t1 idx_t1) */ * from t1 where n=2

 Plan hash value: 3617692013

 ————————————————————————–
 | Id  | Operation  | Name | Rows  | Bytes | Cost (%CPU)| Time |
 ————————————————————————–
 |  0 | SELECT STATEMENT  | | | |    7 (100)| |
 |*  1 |  TABLE ACCESS FULL| T1 |    1 |    4 |    7  (0)| 00:00:01 |

 PLAN_TABLE_OUTPUT
 ——————————————————————————–

 Query Block Name / Object Alias (identified by operation id):
 ————————————————————-

    1 – SEL$1 / T1@SEL$1

 Outline Data
 ————-

  /*+

 PLAN_TABLE_OUTPUT
 ——————————————————————————–
      BEGIN_OUTLINE_DATA
      IGNORE_OPTIM_EMBEDDED_HINTS
      OPTIMIZER_FEATURES_ENABLE(‘11.2.0.4’)
      DB_VERSION(‘11.2.0.4’)
      ALL_ROWS
      OUTLINE_LEAF(@”SEL$1″)
      FULL(@”SEL$1″ “T1″@”SEL$1”)
      END_OUTLINE_DATA
  */

 Predicate Information (identified by operation id):

 PLAN_TABLE_OUTPUT
 ——————————————————————————–

    1 – filter(“N”=2)

 Column Projection Information (identified by operation id):
 ———————————————————–

    1 – “N”[NUMBER,22]

 42 rows selected.

我们发现还是走了全表扫描,要想使上面的 SQL_PROFILE 生效,我们需要加上 FORCE_MATCH=TRUE,true 的含义,就是 where 条件中值发生变化,但是 SQL_Profile 仍然有效

SQL> execute dbms_sqltune.accept_sql_profile(task_name =>  ‘my_sql_tuning_task_2’, task_owner => ‘SYS’, replace => TRUE,force_match => true);

 PL/SQL procedure successfully completed.

再次查看相对应的执行计划

SQL> select /*+ no_index(t1 idx_t1) */ * from t1 where n=2;

 N
 ———-
 2

 SQL> select * from table(dbms_xplan.display_cursor(null,null,’advanced’));

 PLAN_TABLE_OUTPUT
 ——————————————————————————–
 SQL_ID c4j6hxkqudj1s, child number 0
 ————————————-
 select /*+ no_index(t1 idx_t1) */ * from t1 where n=2

 Plan hash value: 1369807930

 —————————————————————————
 | Id  | Operation | Name  | Rows  | Bytes | Cost (%CPU)| Time  |
 —————————————————————————
 |  0 | SELECT STATEMENT |  |  |  | 1 (100)|  |
 |*  1 |  INDEX RANGE SCAN| IDX_T1 | 1 | 4 | 1  (0)| 00:00:01 |

 PLAN_TABLE_OUTPUT
 ——————————————————————————–

 Query Block Name / Object Alias (identified by operation id):
 ————————————————————-

    1 – SEL$1 / T1@SEL$1

 Outline Data
 ————-

  /*+

 PLAN_TABLE_OUTPUT
 ——————————————————————————–
      BEGIN_OUTLINE_DATA
      IGNORE_OPTIM_EMBEDDED_HINTS
      OPTIMIZER_FEATURES_ENABLE(‘11.2.0.4’)
      DB_VERSION(‘11.2.0.4’)
      ALL_ROWS
      OUTLINE_LEAF(@”SEL$1″)
      INDEX(@”SEL$1″ “T1″@”SEL$1” (“T1″.”N”))
      END_OUTLINE_DATA
  */

 Predicate Information (identified by operation id):

 PLAN_TABLE_OUTPUT
 ——————————————————————————–

    1 – access(“N”=2)

 Column Projection Information (identified by operation id):
 ———————————————————–

    1 – “N”[NUMBER,22]

 Note
 —–

 PLAN_TABLE_OUTPUT
 ———————————————
    – SQL profile SYS_SQLPROF_015410470fa40001 used for this statement

 46 rows selected.

 

这是我们可以发现这次的执行计划走的是索引,为了再次验证新生成的 SQL_Profile 对其他值也有效,我们再次尝试 n =3

 SQL> select /*+ no_index(t1 idx_t1) */ * from t1 where n=3;

 N
 ———-
 3

 SQL> select * from table(dbms_xplan.display_cursor(null,null,’advanced’));

 PLAN_TABLE_OUTPUT
 ——————————————————————————–
 SQL_ID 0zz8t0qnm15hj, child number 0
 ————————————-
 select /*+ no_index(t1 idx_t1) */ * from t1 where n=3

 Plan hash value: 1369807930

 —————————————————————————
 | Id  | Operation | Name  | Rows  | Bytes | Cost (%CPU)| Time  |
 —————————————————————————
 |  0 | SELECT STATEMENT |  |  |  | 1 (100)|  |
 |*  1 |  INDEX RANGE SCAN| IDX_T1 | 1 | 4 | 1  (0)| 00:00:01 |

 PLAN_TABLE_OUTPUT
 ——————————————————————————–
 —————————————————————————

 Query Block Name / Object Alias (identified by operation id):
 ————————————————————-

    1 – SEL$1 / T1@SEL$1

 Outline Data
 ————-

  /*+

 PLAN_TABLE_OUTPUT
 ——————————————————————————–
      BEGIN_OUTLINE_DATA
      IGNORE_OPTIM_EMBEDDED_HINTS
      OPTIMIZER_FEATURES_ENABLE(‘11.2.0.4’)
      DB_VERSION(‘11.2.0.4’)
      ALL_ROWS
      OUTLINE_LEAF(@”SEL$1″)
      INDEX(@”SEL$1″ “T1″@”SEL$1” (“T1″.”N”))
      END_OUTLINE_DATA
  */

 Predicate Information (identified by operation id):

 PLAN_TABLE_OUTPUT
 ——————————————————————————–
 —————————————————

    1 – access(“N”=3)

 Column Projection Information (identified by operation id):
 ———————————————————–

    1 – “N”[NUMBER,22]

 Note
 —–

 PLAN_TABLE_OUTPUT
 ——————————————————————————–
    – SQL profile SYS_SQLPROF_015410470fa40001 used for this statement

 46 rows selected.

结论是仍旧有效。

–End.

本文永久更新链接地址 :http://www.linuxidc.com/Linux/2016-04/130334.htm

正文完
星哥说事-微信公众号
post-qrcode
 0
星锅
版权声明:本站原创文章,由 星锅 于2022-01-22发表,共计14915字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
【腾讯云】推广者专属福利,新客户无门槛领取总价值高达2860元代金券,每种代金券限量500张,先到先得。
阿里云-最新活动爆款每日限量供应
评论(没有评论)
验证码
【腾讯云】云服务器、云数据库、COS、CDN、短信等云产品特惠热卖中