如何控制SQL执行计划之9i篇

对于一个高并发的OLTP系统,SQL执行计划的改变往往意味着灾难。很多因素都可能导致执行计划发生不可预期的改变,比如表结构,索引,统计信息等变化,甚至我们发生过一个小小的grant操作,导致执行计划失效,重新解析后生成了一个不正确的执行计,让整个系统Crash的案例。最近,我们对一个分区表增加分区后,导致了执行计划发生改变,故障再次重演。

如何控制SQL的执行计划就成为了一个课题,之前我曾经写过一篇关于调整SQL执行计划的文章,但是内容比较粗略。因为Oracle提供了很多手段去控制执行计划,所以我打算为每个版本都整理一个最佳实践。

我们设想一个场景,一个SQL本来应该走nested loop join,但是由于某种原因,突然变成了hash join,调整统计信息无效,数据库load不断升高,只留给你很少的时间,怎么办?最直接有效的方法是在SQL上加hints,但是需要程序发布,或者程序根本无法修改。

9i为我们提供了Stored outline,大家都非常熟悉,但是对于上面的场景,还是需要点技巧。方法是:生成两个stored outline,一条是错误的,一条是正确的(加hints),两个然后将其执行计划交换。看下面的步骤:

1.创建两个public stroed outline,第一个是目前运行的,第二个是加了hints.

create or replace outline OUTLN1 on select e.ename from emp e, dept d where e.deptno=d.deptno;
create or replace outline OUTLN2 on select /*+ use_nl(e d)*/ e.ename from emp e, dept d
where e.deptno=d.deptno;

拷贝成为private outline

create or replace private outline PRIV_OUTLN1 from OUTLN1;
create or replace private outline PRIV_OUTLN2 from OUTLN2;

交换两个stored outline的执行计划

update ol$ set ol_name=decode(ol_name,'PRIV_OUTLN1','PRIV_OUTLN2','PRIV_OUTLN2','PRIV_OUTLN1')
where ol_name in ('PRIV_OUTLN1','PRIV_OUTLN2');

设置本session生效,并测试结果

alter session set use_private_outlines=true;

刷新

execute dbms_outln_edit.refresh_private_outline('PRIV_OUTLN1');
execute dbms_outln_edit.refresh_private_outline('PRIV_OUTLN2');

发布到public outline

create or replace outline OUTLN1 from private PRIV_OUTLN1;
create or replace outline OUTLN2 from private PRIV_OUTLN2;

设置使用,调整完毕

alter system set use_stored_outlines=true;

这样操作的原因是因为stroed outline必须依赖SQL的文本匹配,所以,我们利用加hints之后的SQL产生正确的执行计划,并通过交换的方式,让SQL的执行计划变成我们想要的结果。当然,如果你愿意,也可以直接修改ol$hints表的内容,同样可以达到改变执行计划的目的。

–EOF–

6 Responses to “如何控制SQL执行计划之9i篇”


  1. 1 Obuntu

    博主好文~不知10g里面的情况是怎样的呢?

  2. 2 jacky

    这个主题我会写一个系列,后续会包括10g和11g,谢谢关注:)

  3. 3 love2008

    如果目前运行的sql是一个带有绑定变量的sql的话,该如何做哪?

  4. 4 jacky

    绑定变量是一样的过程。

  5. 5 奶妈来了

    居然直接改系统视图

    交换两个stored outline的执行计划

    update ol$ set ol_name=decode(ol_name,’PRIV_OUTLN1′,’PRIV_OUTLN2′,’PRIV_OUTLN2′,’PRIV_OUTLN1′)
    where ol_name in (’PRIV_OUTLN1′,’PRIV_OUTLN2′);

  6. 6 jacky

    这是一种很通常的做法,并没有改系统视图,只是改了private outline,然后再发布到public outline中。

Leave a Reply