说明由于生产环境执行的sql变化较快,版本发布比较频繁,造成sql的执行计划不是很稳定,经常会有一些性能很差的sql出现

对于这些sql,我们可以使用sql_plan_baseline对执行计划进行绑定,从而使执行计划固定下来


注意:前提是sql最好使用绑定变量,就算有的没有绑定变量,确定字段的值不会改变才行,因为是针对sql_id进行的绑定

        如果sql文本改变,绑定也就无意义了  

 

解决办法: a. 通过  alter system set cursor_sharing=FORCE;   让系统自动绑定变量

                   (在11.2.0.3版本中测试,经绑定后v$SQL_PLAN执行计划变成了新的执行计划,但是通过set autot on 查看执行计划未改变)

                 b.文本方式,可能通过 sqlprofile 的方式,如自动方式就可以用 force_match=>true


方案一:如果查询的SQL有多个执行计划,其中有合适的执行计划,则可以直接绑定

1、查询每个执行计划消耗的资源情况

set lines 150 pages 999;
col p_user for 99999 ;
col execs for 9999999 ;
select a.INSTANCE_NUMBER inst_id,to_char(b.END_INTERVAL_TIME,'yyyymmdd hh24:mi:ss') time,plan_hash_value,buffer_gets_delta/executions_delta get_exec, disk_reads_delta/executions_delta read_exec,cpu_time_delta/executions_delta/1000 cpu_exec_ms ,elapsed_time_delta/executions_delta/1000 elaps_exec_ms ,parsing_schema_id p_user,ROWS_PROCESSED_delta/executions_delta rows_exec,EXECUTIONS_DELTA execs
from dba_hist_sqlstat a, dba_hist_snapshot b
where a.sql_id='&sql_id'
and a.snap_id = b.snap_id
and a.instance_number = b.instance_number
and b.END_INTERVAL_TIME between sysdate - 3 and sysdate
and executions_delta>0 order by 2,1;

SELECT DISTINCT(PLAN_HASH_VALUE) FROM V$SQL_PLAN t WHERE SQL_ID = '&sql_id';


2、固定执行计划为 

SQL> select sql_handle, plan_name, accepted, fixed,optimizer_cost from dba_sql_plan_baselines ;


declare
l_pls number;
begin
l_pls := DBMS_SPM.LOAD_PLANS_FROM_CURSOR_CACHE(sql_id=>'&sql_id',plan_hash_value=>&plan_hash,fixed =>'YES',enabled=>'YES');
end;
/


SQL> select sql_handle, plan_name, accepted, fixed,optimizer_cost from dba_sql_plan_baselines

SQL_HANDLE PLAN_NAME ACC FIX OPTIMIZER_COST
------------------------------ ------------------------------ --- --- --------------
QL_368f6240d3b95ad6 SQL_PLAN_3d3v2839vkqqqa3c44420 YES YES 34




3、删除固定执行计划


declare 
xx PLS_INTEGER;
BEGIN
xx :=dbms_spm.drop_sql_plan_baseline(sql_handle=>'SQL_9ca68721a7101b8e',plan_name=>null);
END;
/



方案二:如果没有合适的执行计划,就要通过自己分析,运用一些hint让sql产生比较好的执行计划

              或者其它类似SQL 有好的执行计划


1、让原来执行效率低的SQL 生成 HANDLE 

declare
l_pls number;
begin
l_pls := DBMS_SPM.LOAD_PLANS_FROM_CURSOR_CACHE(sql_id => '&bad_sql_id',
plan_hash_value => &bad_sql_plan_hash_value,
enabled => 'NO'); # 注意这里是NO
end;
/



2.  查询刚刚生成的 handle 

select sql_handle, plan_name, origin, enabled, accepted,fixed,creator,optimizer_cost,sql_text 
from dba_sql_plan_baselines where origin = 'MANUAL-LOAD' and ENABLED='NO' order by created desc ;


3.  用新的SQL_ID  和 新的  sql_plan_hash_value ,绑定老的  sql_handle ,以固定旧的SQL的执行计划


declare
l_pls number;
begin
l_pls := DBMS_SPM.load_plans_from_cursor_cache(sql_id => '5qp9tjw81a5un', -- new_SQL_ID'
plan_hash_value => 1175071277, --new_plan_hash_value
sql_handle => 'SQL_9ca68721a7101b8e', --OLD_handle
fixed =>'YES'
);
end;
/



以上是从内存中获得较优的执行计划 ,当然也可以从AWR  和 sqlset 优化集中获取 :例从SQLSET中获取

DECLARE
my_plans PLS_INTEGER;
BEGIN
my_plans := DBMS_SPM.LOAD_PLANS_FROM_SQLSET( sqlset_name >'STS_PS',basic_filter=>'sql_id=''bxajng3zk2vn1''');
END;
/