表字段顺序对效率的影响

在做表结构设计的时候,我们往往不会去考虑字段的摆放顺序。但是,实际上字段的摆放顺序对数据库操作的性能是有影响的。

越靠后的字段效率越低:比如4AAAA6BBBBBB2CC,当要扫描到2CC时,数据库并不知道C的位置,如果头占了10个长度,那么C的位移就是 10+(4+1)+(6+1)+1=23.每个字段没有直接的位移地址的,既然没有23的位移,ORACLE只有通过前面的头,以及A B的位置来推算C的位移。那么越往后面的字段,推算位移次数越多,CPU计算的次数也越多。

越靠后的字段操作开销越大:刚刚说了越往后面的字段,推算位移次数越多,CPU计算的次数也越多,这就意味着CPU的开销就越大。

第一步,创建一个有250个字段的表

set serveroutput on
set echo on
declare
v_sql varchar2(32767);
begin
 v_sql:='create table t(';
 for i in 1..250
  loop
   v_sql:=v_sql||'n'||i||' number,';
  end loop;
  v_sql:=v_sql||'r_pad varchar2(1000))';
  execute immediate v_sql;
end;
/

第二步,往这个表中插入10000行数据

declare
v_sql varchar2(4000);
begin
 v_sql:='insert into t select ';
 for i in 1..250
  loop
   v_sql:=v_sql||'0,';
  end loop;
  v_sql:=v_sql||' null from dual connect by level <=10000';
  execute immediate v_sql;
  commit;
end;
/

第三步,收集统计信息

exec dbms_stats.gather_table_stats(user,'t');

第四步,执行SQL,查出COUNT每个字段的耗时

set serveroutput on size 1000000
declare
 v_dummy PLS_INTEGER;
 v_start PLS_INTEGER;
 v_stop PLS_INTEGER;
 v_sql VARCHAR2(100);
BEGIN
 v_start :=dbms_utility.get_time;
 for j in 1..20
  loop
   execute immediate 'select count(*) from t ' into v_dummy;
  end loop;
  v_stop:= dbms_utility.get_time;
  dbms_output.put_line('COUNT* 20次的时间是:'|| to_char((v_stop-v_start)*10,'999')||'毫秒'); 
 for i in 1..250
  loop
   v_sql :='select count(n'||i||') from t';
   v_start :=dbms_utility.get_time;
   for j in 1..20
    loop
     execute immediate v_sql into v_dummy;
    end loop;
    v_stop:=dbms_utility.get_time;
    dbms_output.put_line('count'||i||'列20次的时间是:'||to_char((v_stop-v_start)*10,'999')||'毫秒');
   end loop;
end;
/

以下是输出的列数据统计20次的耗时,这下一目了然了,印象深刻了,常用的字段要往前摆了。COUNT(*)不需要考虑字段的顺序与位置,所以效率当然是最快的。

COUNT* 20次的时间是: 30毫秒
count1列20次的时间是: 30毫秒
count2列20次的时间是: 30毫秒
count3列20次的时间是: 30毫秒
count4列20次的时间是: 30毫秒
count5列20次的时间是: 20毫秒
count6列20次的时间是: 30毫秒
count7列20次的时间是: 20毫秒
count8列20次的时间是: 20毫秒
count9列20次的时间是: 20毫秒
count10列20次的时间是: 30毫秒
count11列20次的时间是: 20毫秒
count12列20次的时间是: 30毫秒
count13列20次的时间是: 20毫秒
count14列20次的时间是: 30毫秒
count15列20次的时间是: 20毫秒
count16列20次的时间是: 30毫秒
count17列20次的时间是: 30毫秒
count18列20次的时间是: 20毫秒
count19列20次的时间是: 30毫秒
count20列20次的时间是: 30毫秒
count21列20次的时间是: 30毫秒
count22列20次的时间是: 30毫秒
count23列20次的时间是: 30毫秒
count24列20次的时间是: 30毫秒
count25列20次的时间是: 30毫秒
count26列20次的时间是: 40毫秒
count27列20次的时间是: 30毫秒
count28列20次的时间是: 30毫秒
count29列20次的时间是: 30毫秒
count30列20次的时间是: 40毫秒
count31列20次的时间是: 30毫秒
count32列20次的时间是: 40毫秒
count33列20次的时间是: 30毫秒
count34列20次的时间是: 40毫秒
count35列20次的时间是: 40毫秒
count36列20次的时间是: 30毫秒
count37列20次的时间是: 40毫秒
count38列20次的时间是: 40毫秒
count39列20次的时间是: 40毫秒
count40列20次的时间是: 40毫秒
count41列20次的时间是: 40毫秒
count42列20次的时间是: 40毫秒
count43列20次的时间是: 40毫秒
count44列20次的时间是: 40毫秒
count45列20次的时间是: 40毫秒
count46列20次的时间是: 50毫秒
count47列20次的时间是: 40毫秒
count48列20次的时间是: 50毫秒
count49列20次的时间是: 40毫秒
count50列20次的时间是: 40毫秒
count51列20次的时间是: 50毫秒
count52列20次的时间是: 50毫秒
count53列20次的时间是: 40毫秒
count54列20次的时间是: 50毫秒
count55列20次的时间是: 50毫秒
count56列20次的时间是: 40毫秒
count57列20次的时间是: 50毫秒
count58列20次的时间是: 50毫秒
count59列20次的时间是: 50毫秒
count60列20次的时间是: 50毫秒
count61列20次的时间是: 50毫秒
count62列20次的时间是: 50毫秒
count63列20次的时间是: 50毫秒
count64列20次的时间是: 60毫秒
count65列20次的时间是: 50毫秒
count66列20次的时间是: 50毫秒
count67列20次的时间是: 60毫秒
count68列20次的时间是: 60毫秒
count69列20次的时间是: 50毫秒
count70列20次的时间是: 60毫秒
count71列20次的时间是: 60毫秒
count72列20次的时间是: 60毫秒
count73列20次的时间是: 60毫秒
count74列20次的时间是: 50毫秒
count75列20次的时间是: 60毫秒
count76列20次的时间是: 70毫秒
count77列20次的时间是: 60毫秒
count78列20次的时间是: 60毫秒
count79列20次的时间是: 60毫秒
count80列20次的时间是: 60毫秒
count81列20次的时间是: 70毫秒
count82列20次的时间是: 60毫秒
count83列20次的时间是: 70毫秒
count84列20次的时间是: 60毫秒
count85列20次的时间是: 70毫秒
count86列20次的时间是: 60毫秒
count87列20次的时间是: 70毫秒
count88列20次的时间是: 70毫秒
count89列20次的时间是: 60毫秒
count90列20次的时间是: 70毫秒
count91列20次的时间是: 70毫秒
count92列20次的时间是: 70毫秒
count93列20次的时间是: 70毫秒
count94列20次的时间是: 70毫秒
count95列20次的时间是: 70毫秒
count96列20次的时间是: 70毫秒
count97列20次的时间是: 80毫秒
count98列20次的时间是: 70毫秒
count99列20次的时间是: 70毫秒
count100列20次的时间是: 70毫秒
count101列20次的时间是: 80毫秒
count102列20次的时间是: 70毫秒
count103列20次的时间是: 80毫秒
count104列20次的时间是: 80毫秒
count105列20次的时间是: 70毫秒
count106列20次的时间是: 80毫秒
count107列20次的时间是: 80毫秒
count108列20次的时间是: 70毫秒
count109列20次的时间是: 80毫秒
count110列20次的时间是: 80毫秒
count111列20次的时间是: 80毫秒
count112列20次的时间是: 80毫秒
count113列20次的时间是: 80毫秒
count114列20次的时间是: 80毫秒
count115列20次的时间是: 80毫秒
count116列20次的时间是: 80毫秒
count117列20次的时间是: 90毫秒
count118列20次的时间是: 80毫秒
count119列20次的时间是: 80毫秒
count120列20次的时间是: 80毫秒
count121列20次的时间是: 90毫秒
count122列20次的时间是: 80毫秒
count123列20次的时间是: 90毫秒
count124列20次的时间是: 80毫秒
count125列20次的时间是: 90毫秒
count126列20次的时间是: 90毫秒
count127列20次的时间是: 90毫秒
count128列20次的时间是: 80毫秒
count129列20次的时间是: 90毫秒
count130列20次的时间是: 90毫秒
count131列20次的时间是: 90毫秒
count132列20次的时间是: 90毫秒
count133列20次的时间是: 90毫秒
count134列20次的时间是: 90毫秒
count135列20次的时间是: 90毫秒
count136列20次的时间是: 100毫秒
count137列20次的时间是: 90毫秒
count138列20次的时间是: 90毫秒
count139列20次的时间是: 100毫秒
count140列20次的时间是: 90毫秒
count141列20次的时间是: 100毫秒
count142列20次的时间是: 90毫秒
count143列20次的时间是: 100毫秒
count144列20次的时间是: 100毫秒
count145列20次的时间是: 90毫秒
count146列20次的时间是: 100毫秒
count147列20次的时间是: 100毫秒
count148列20次的时间是: 100毫秒
count149列20次的时间是: 120毫秒
count150列20次的时间是: 110毫秒
count151列20次的时间是: 100毫秒
count152列20次的时间是: 100毫秒
count153列20次的时间是: 100毫秒
count154列20次的时间是: 100毫秒
count155列20次的时间是: 110毫秒
count156列20次的时间是: 100毫秒
count157列20次的时间是: 110毫秒
count158列20次的时间是: 100毫秒
count159列20次的时间是: 110毫秒
count160列20次的时间是: 110毫秒
count161列20次的时间是: 100毫秒
count162列20次的时间是: 110毫秒
count163列20次的时间是: 110毫秒
count164列20次的时间是: 110毫秒
count165列20次的时间是: 110毫秒
count166列20次的时间是: 110毫秒
count167列20次的时间是: 110毫秒
count168列20次的时间是: 110毫秒
count169列20次的时间是: 110毫秒
count170列20次的时间是: 120毫秒
count171列20次的时间是: 110毫秒
count172列20次的时间是: 110毫秒
count173列20次的时间是: 120毫秒
count174列20次的时间是: 110毫秒
count175列20次的时间是: 110毫秒
count176列20次的时间是: 120毫秒
count177列20次的时间是: 120毫秒
count178列20次的时间是: 110毫秒
count179列20次的时间是: 120毫秒
count180列20次的时间是: 120毫秒
count181列20次的时间是: 120毫秒
count182列20次的时间是: 120毫秒
count183列20次的时间是: 120毫秒
count184列20次的时间是: 120毫秒
count185列20次的时间是: 120毫秒
count186列20次的时间是: 120毫秒
count187列20次的时间是: 120毫秒
count188列20次的时间是: 120毫秒
count189列20次的时间是: 130毫秒
count190列20次的时间是: 120毫秒
count191列20次的时间是: 120毫秒
count192列20次的时间是: 130毫秒
count193列20次的时间是: 130毫秒
count194列20次的时间是: 120毫秒
count195列20次的时间是: 130毫秒
count196列20次的时间是: 120毫秒
count197列20次的时间是: 130毫秒
count198列20次的时间是: 130毫秒
count199列20次的时间是: 130毫秒
count200列20次的时间是: 130毫秒
count201列20次的时间是: 130毫秒
count202列20次的时间是: 130毫秒
count203列20次的时间是: 130毫秒
count204列20次的时间是: 130毫秒
count205列20次的时间是: 130毫秒
count206列20次的时间是: 130毫秒
count207列20次的时间是: 140毫秒
count208列20次的时间是: 130毫秒
count209列20次的时间是: 130毫秒
count210列20次的时间是: 140毫秒
count211列20次的时间是: 130毫秒
count212列20次的时间是: 140毫秒
count213列20次的时间是: 130毫秒
count214列20次的时间是: 140毫秒
count215列20次的时间是: 140毫秒
count216列20次的时间是: 140毫秒
count217列20次的时间是: 140毫秒
count218列20次的时间是: 140毫秒
count219列20次的时间是: 140毫秒
count220列20次的时间是: 140毫秒
count221列20次的时间是: 140毫秒
count222列20次的时间是: 140毫秒
count223列20次的时间是: 140毫秒
count224列20次的时间是: 140毫秒
count225列20次的时间是: 140毫秒
count226列20次的时间是: 150毫秒
count227列20次的时间是: 140毫秒
count228列20次的时间是: 140毫秒
count229列20次的时间是: 150毫秒
count230列20次的时间是: 140毫秒
count231列20次的时间是: 150毫秒
count232列20次的时间是: 140毫秒
count233列20次的时间是: 150毫秒
count234列20次的时间是: 150毫秒
count235列20次的时间是: 150毫秒
count236列20次的时间是: 140毫秒
count237列20次的时间是: 150毫秒
count238列20次的时间是: 150毫秒
count239列20次的时间是: 150毫秒
count240列20次的时间是: 150毫秒
count241列20次的时间是: 150毫秒
count242列20次的时间是: 150毫秒
count243列20次的时间是: 150毫秒
count244列20次的时间是: 150毫秒
count245列20次的时间是: 160毫秒
count246列20次的时间是: 150毫秒
count247列20次的时间是: 150毫秒
count248列20次的时间是: 150毫秒
count249列20次的时间是: 160毫秒
count250列20次的时间是: 150毫秒
Posted in SQL TUNING | Leave a comment

11G新特性:sql plan baseline

ORACLE11G推出了SQL PLAN BASELINE,它将存储提纲取而代之。它们都是用来稳定SQL执行计划的,但是SQL计划基线优势在于:存储大纲得优选一种执行计划进行稳定,但是SQL基线是根据当时的环境生成相应的SQL基线,稳定SQL功能时,不光是匹配文本和HASH标签,还要匹配环境,再选择出成本最低的SQL基线。

注意:一条SQL,存储大纲只有一个。但是,SQL基线可以有多个,如果有多个可用的SQL基线,查询优化器会选择代价最低的那个。

使用PLAN BASELINE的过程如下图(摘自ORACLE SQL PLAN BASELINE白皮书):

1. 首先,不管是否存在baseline,oracle都会正常进行硬解析或者软解析,为SQL生成一个执行计划。 由于ACS和bind peeking的作用,存在baseline的SQL有可能在这时生成一个不同于baseline的执行计划。

2. 如果baseline不存在,就按生成的计划执行。如果baseline存在,那么要查看history里是否有这个计划,如果没有,就将这个计划插入,并标记为ENABLED,NON-ACCEPTED.

3. 在baseline中查看是否有FIXED=YES的计划存在,如果存在,执行FIXED的计划,如果存在多个FIXED的计划,根据dba_sql_plan_baselines视图的统计信息重新计算cost,选择cost小的那个。

4. 如果FIXED的计划不存在,就选择ACCEPTED的计划执行。 如果存在多个ACCEPTED的计划,根据dba_sql_plan_baselines视图的统计信息重新计算cost,选择cost小的那个。

如果要使用SQL PLAN BASELINE,则需要使得参数optimizer_use_sql_plan_baselines为TRUE。

我们收集SQL BASELINE的方法有三种:

1、通过参数optimizer_capture_sql_plan_baselines打开系统自动收集

ALTER SESSION SET optimizer_capture_sql_plan_baselines=TRUE;

当自动捕获开启后,查询优化器为每条重复执行过(就是至少执行过两次)的SQL语句存储一个新的SQL计划基线。

2、从library cache中获取

DECLARE
ret PLS_INTEGER;
BEGIN
ret := dbms_spm.load_plans_from_cursor_cache(sql_id => '&sql_id',
plan_hash_value => NULL);
dbms_output.put_line(ret || ' SQL plan baseline(s) created');
END;
/

 

3、从SQL调优集加载

dbms_psm.load_plans_from_sqlset(
sqlset_name => 'test_sqlset',
sqlset_owner => user
);

 

当执行一个使用了BASELINE的SQL的时候,我看到数据库中做了以下动作:

1、通过签名和分类去查找执行计划等信息,可能会返回多个PLAN,查询优化器会选择成本最低的个

SELECT obj_type, plan_id, description, creator, origin, created, last_modified FROM sqlobj$auxdata WHERE signature = :1 AND category = :2

2、将新的执行计划信息MERGE到BASELINE的数据字典中去

MERGE INTO sqlobj$,sqlobj$auxdata

3、这个也不明白为什么

DELETE FROM sql$ WHERE signature = :1

INSERT INTO sql$ (signature, inuse_features, flags, spare1, spare2)

 

每当SQL产生新的执行计划时,Oracle都会将其记录下来,但是这个执行计划并不会马上生效,而是等待用户去确认,这样大大降低了系统的风险。

Oracle通过Evolving SQL plan baseline来对新的执行计划进行演化,通过evolve_sql_plan_baseline这个函数,可以对不同的plan进行比较,

其实就是让优化器直接执行SQL的不同plan,以此来选择最优的plan,并且可以给出详细的报告。

SET SERVEROUTPUT ON
SELECT dbms_spm.evolve_sql_plan_baseline(
sql_handle => '&sql_handle',
plan_name => '',
time_limit => 10,
verify => 'yes',
commit => 'yes'
)
FROM dual;

time_limit——演化可以持续的时间,以分钟为单位。这个参数的值可以为自然数或常量dbms_spm. auto_limit和dbms_spm.no_limit

verify——如果设置为yes(为默认值),将执行SQL语句以验证性能。如果设置为no,则无需验证,直接接受SQL计划基线

commit——如果设置为yes(为默认值),数据字典将按照演化的结果进行修改。如果设置为no,假设参数verify被设置为yes,将执行验证操作但不会修改数据字典。

 

用户可以通过alter_sql_plan_baseline更改当前生效的plan。

declare
ret PLS_INTEGER;
BEGIN
ret := dbms_spm.alter_sql_plan_baseline(sql_handle => '&sql_handle',
plan_name => '&plan_name',
attribute_name => 'enabled',
attribute_value =>'no');
end;
/

 

用户可以通过drop_sql_plan_baseline去删除baseline

DECLARE
ret PLS_INTEGER;
BEGIN
ret := dbms_spm.drop_sql_plan_baseline('&sql_handle');
END;
/

BASELINE信息,可以通过视图dba_sql_plan_baselines获知。

 

存储信息:

SPM存放在SYSAUX表空间中,默认占用空间不超过SYSAUX表空间的10%,最高不能超过50%.可以通过DBMS_SPM.CONFIGURE(‘space_budget_percent’,30)修改此限制。

当然保存的时间也是有限制的,当PLAN在很长一段时间没有被使用的话,将会根据保留策略进行清除,ORACLE提供的保留时间在5-523周之间。可能通过DBMS_SPM.CONFIGURE( ‘plan_retention_weeks’,105) 来修改保留策略

SELECT PARAMETER_NAME, PARAMETER_VALUE FROM DBA_SQL_MANAGEMENT_CONFIG; --可通过此SQL查看当前配置

 

迁移SQL BASELINE的过程:

⊙ 用过程dbms_spm.create_stgtab_baseline创建一张舞台表。

⊙ 通过函数dbms_spm.pack_stgtab_baseline将SQL计划基线从数据字典复制到舞台表。

⊙ 通过函数dbms_spm.unpack_stgtab_baseline将SQL计划基线从舞台表复制到数据字典,unpack stgtab是解压舞台表的意思,并存入基线的数据字典中。

 

注意在OUTLINE与BASELINE共存的时候,当USE_STORED_OUTLINES=TRUE,而使用的CATEGORY为DEFAULT值的情况下,ORACLE会优先选择OUTLINE。

 

 

 

 

 

Posted in NEW FEATURE, SQL TUNING | Leave a comment

stored outline

大型系统中,如果需要统计分析大量对象,或者ORACLE环境变化,使得ORACLE查询优化器重新分析。生成新的执行计划可能不如老的执行计划。我们如何能将执行计划回滚回去呢?

ORACLE的stored outline能把之前的执行计划稳定住,不管系统怎么变化,只要语句匹配存储概要的SQL文本,就会应用存储概要指定的提示把执行计划给稳定住。一般在以下两类情况下,非常有用:

1、从基于规则的(rule-based)优化器到基于开销的(cost-based)优化器的迁移。
2、从一个Oracle版本到另一个新版本的升级。

  但是在实践中,存储提纲并不完美,它并不总能提供一个稳定的执行计划,如果是ORACLE11G,还是建议使用SQL计划基线。

要使用存储提纲,必须拥有create any outline,alter any outline,drop any outline的权限。

 

第一,OUTLINE的创建

创建方法有三:

1、自动创建
alter session set CREATE_STORED_OUTLINES=true;
2、使用sqltext创建outline
create or replace [private] outline test_outline for category test on select count(*) from test where object_id<10;
3、使用shared pool cursor创建outline
exec DBMS_OUTLN.create_outline(hash_value => 1881540655,child_number => 0,category => 'TEST');
此方法创建的OUTLINE命名是系统自动命名的,可以通过alter outline … rename to …进行修改。
 
这里我使用手动创建的方法
create or replace outline outline_test for category test on select count(*) from test where object_id<10;
 

 

第二,查看OUTLINE

shujukuai@SHUJUKUAI> select category,sql_text,signature from user_outlines where name='OUTLINE_TEST';
CATEGORY                       SQL_TEXT                                                                         SIGNATURE
------------------     -------------------------------                                          ----------------------------------
 TEST          select count(*) from test where object_id<10       96803CF70C6CCC6DEA85C5E23585A507
 

第三, 修改CATEGORY

exec dbms_outln.update_by_cat(oldcat=>'TEST',newcat=>'DEFAULT');

 

第四,激活OUTLINE

初始化参数create_stored_outlines为TRUE或者指定类别(category)名时,激活指定的OUTLINE。TRUE的情况下激活的是CATEGORY为DEFAULT的

alter outline outline_test enable;
alter session set create_stored_outlines=test;

第五,验证OUTLINE

select count(*) from test where object_id<10;
------------------------------------------------------------------------------
| Id  | Operation         | Name     | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |          |     1 |     5 |     3   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE   |          |     1 |     5 |            |          |
|*  2 |   INDEX RANGE SCAN| IND_TEST |   128 |   640 |     3   (0)| 00:00:01 |
 ------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("OBJECT_ID"<10)
Note
-----   
- outline "OUTLINE_TEST" used for this statement
 

第六,干坏事,直接修改OUTLINE

update outln.ol$hints set hint_text='FULL(@SEL$1 TEST)'  where hint#=1 and  ol_name='OUTLINE_TEST';
........
---------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |     1 |     5 |  4913   (1)| 00:00:59 |
 |   1 |  SORT AGGREGATE    |      |     1 |     5 |            |          |
 |*  2 |   TABLE ACCESS FULL| TEST |   128 |   640 |  4913   (1)| 00:00:59 |
---------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - filter("OBJECT_ID"<10)
Note
 -----   
 - outline "OUTLINE_TEST" used for this statement
 

第七,私有OUTLINE

私有OUTLINE是基于会话级别的,临时的。它的作用在于,我们可以利用它进行测试,测试完成并确认执行计划是OK的,再使用它来创建公有的OUTLINE。

create or replace private outline p_outline_test on select count(*) from test where object_id<10 ;
alter session set use_private_outlines=true;
select count(*) from test where object_id<10 ;
------------------------------------------------------------------------------
| Id  | Operation         | Name     | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------
 |   0 | SELECT STATEMENT  |          |     1 |     5 |     3   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE   |          |     1 |     5 |            |          |
 |*  2 |   INDEX RANGE SCAN| IND_TEST |   128 |   640 |     3   (0)| 00:00:01 |
------------------------------------------------------------------------------
Predicate Information (identified by operation id):
 ---------------------------------------------------
   2 - access("OBJECT_ID"<10)
Note
-----   
 - outline "P_OUTLINE_TEST" used for this statement
  确保了执行计划是正确的。然后我们来创建公有的OUTLINE
create public outline outline_test from private P_OUTLINE_TEST;
 

第八,移动OUTLINE

exp tables=(outln.ol$,outln.ol$hints,outln.ol$nodes) file=outln.dmp query="where category='TEST'"
imp full=y ignore=y file=outln.dmp
 

注意点:

1、OUTLINE覆盖optimizer_features_enable参数

2、OUTLINE覆盖hash_join_enabled=FALSE 设置

3、OUTLINE覆盖query_rewrite_enabled=FALSE设置

OUTLN还有蛮多的BUG:

1、10.2.0.4中的BUG 5211984,会导致无法正常使用存储大纲。

2、10GR2之前,CURSOR_SHARING=FORCE或SIMILAR时,存储大纲并不会被创建该存储大纲的SQL所调用。

 相关视图:

上层视图:dba_outlines、dba_outline_hints

底层基表:outln用户下的ol$、ol$hints、ol$nodes

 

Posted in SQL TUNING | Leave a comment

SQL PROFILE

我们经常会碰到一些线上的SQL问题,因为执行计划不对,可能需要添加HINT才能解决。但是添加HINT就意味着需要修改应用代码。一般一个应用代码的修改、测试及发布,可能需要两三个工作日才可完成。咱们数据库可等不起这个时间,必须要在短时间内解决这个SQL问题。这时候,SQL PROFILE就挺身而出了!它可以让DBA在不修改应用代码程序的情况下,修改SQL的执行计划。

来看一下,SQL PROFILE是如何影响SQL执行计划的:

要使用SQL PROFILE,必须要有create any sql profile,drop any sql profile,alter any sql profile的权限。在11G中不建议给ANY  PROFILE的权限,最好是administer sql management object权限

SQL调优建议器的核心接口是通过dbms_sqltune包来提供的。为了开始一个调优任务,则必须要调用函数create_tuning_task.

第一步,创建一个调优任务

SET TERMOUT ON
SET FEEDBACK OFF
SET VERIFY OFF
SET SCAN ON
SET LONG 1000000
SET ECHO ON
VARIABLE tuning_task VARCHAR2(30)
BEGIN     
:tuning_task := dbms_sqltune.create_tuning_task(sql_id => ’09w1j9gt9pnng’);    –SQL_ID需要替换
dbms_sqltune.execute_tuning_task(:tuning_task);
 END;
 /

 第二步,分析调优结果

SELECT dbms_sqltune.report_tuning_task(:tuning_task) FROM dual;
 
……
1- SQL Profile Finding (see explain plans section below)
 ——————————————————–  
A potentially better execution plan was found for this statement.
  Recommendation (estimated benefit: 99.97%)  
——————————————  
- Consider accepting the recommended SQL profile.    
 execute dbms_sqltune.accept_sql_profile(task_name => ‘TASK_23′,             task_owner => ‘SHUJUKUAI’, replace => TRUE);
——————————————————————————-
 EXPLAIN PLANS SECTION -
——————————————————————————
1- Original With Adjusted Cost
——————————
 Plan hash value: 1950795681
—————————————————————————
 | Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
—————————————————————————
 |   0 | SELECT STATEMENT   |      |     1 |     5 |  4913   (1)| 00:00:59 |
 |   1 |  SORT AGGREGATE    |      |     1 |     5 |            |          |
 |*  2 |   TABLE ACCESS FULL| TEST |     1 |     5 |  4913   (1)| 00:00:59 |
—————————————————————————
Predicate Information (identified by operation id):
—————————————————
   2 – filter(“OBJECT_ID”<10)
 
2- Using SQL Profile
 ——————–
Plan hash value: 917719789
——————————————————————————
| Id  | Operation         | Name     | Rows  | Bytes | Cost (%CPU)| Time     |
 ——————————————————————————
 |   0 | SELECT STATEMENT  |          |     1 |     5 |     3   (0)| 00:00:01 |
 |   1 |  SORT AGGREGATE   |          |     1 |     5 |            |          |
|*  2 |   INDEX RANGE SCAN| TEST_IND |     1 |     5 |     3   (0)| 00:00:01 |
 ——————————————————————————
Predicate Information (identified by operation id):
—————————————————
   2 – access(“OBJECT_ID”<10)
——————————————————————————-

从上面的调优结果中看出,ORACLE给了另一个更好的调优建议。但是我们是否需要接受它呢?

第三步,接受此调优建议

DECLARE my_sqlprofile_name VARCHAR2(30);
 BEGIN  my_sqlprofile_name := DBMS_SQLTUNE.ACCEPT_SQL_PROFILE (    
task_name    => ‘TASK_23′,    
 name         => ‘my_sql_profile’,    
category   =>’test’,  –若不设置,则于DEFAULT
 description  => ‘shujukuai test sql profile’,    
 force_match  => TRUE);              –指定文档标准化
END;
/

 注:force_match为true,则SQL文本的空白、大小写和字面 量均不区分;不过,如果SQL语句中出现绑定变更就不能消除字面量影响了。force_match为false,则仅不区分大小写和空白。

当我们的SQL概要被数据库所接受以后,则可以在dba_sql_profiles中查看到对应的信息。

第四步,激活SQL概要

SQL概要的激活是由sqltule_category参数在系统级或会话级来控制的。由于刚刚在接受SQL概要的时候,指定了category为TEST,所以我们需要做以下修改才可能使用它:

ALTER SESSION SET SQLTUNE_CATEGORY=TEST;

第五步,验证

explain plan for select /*+ full(test)*/ count(*) from test where object_id<10;
select * from table(dbms_xplan.display(null,null,’outline’));
 
Outline Data
————-
  /*+       BEGIN_OUTLINE_DATA      
INDEX(@”SEL$1″ “TEST”@”SEL$1” (“TEST”.”OBJECT_ID”))      
OUTLINE_LEAF(@”SEL$1″)      
ALL_ROWS      
DB_VERSION(’11.2.0.2′)      
OPTIMIZER_FEATURES_ENABLE(’11.2.0.2′)      
 IGNORE_OPTIM_EMBEDDED_HINTS      
END_OUTLINE_DATA   */
Predicate Information (identified by operation id):
—————————————————
   2 – access(“OBJECT_ID”<10)
Note
 —–   
 - SQL profile “my_sql_profile” used for this statement
 

第六步,线下模拟删除PROFILE

BEGIN   
DBMS_SQLTUNE.DROP_SQL_PROFILE( name => ‘my_sql_profile’ );  
END;
 /

 第七步,导入SQL PROFILE

 declare  
 v_hints      sys.sqlprof_attr;  
 v_sql_fulltext clob;
 begin  
select SQL_FULLTEXT     into v_sql_fulltext     from v$sqlarea    where sql_id = ‘ar1b2dvf3dayj’;    –替换SQL_ID
 v_hints := sys.sqlprof_attr(‘INDEX(@”SEL$1″ “TEST”@”SEL$1” (“TEST”.”OBJECT_ID”))’,  
‘OUTLINE_LEAF(@”SEL$1″)’,  
 ’ALL_ROWS’,  
 ’IGNORE_OPTIM_EMBEDDED_HINTS’);  
dbms_sqltune.import_sql_profile(                  
 name =>’import_sql_profile’,                  
 sql_text => v_sql_fulltext,                                 
  profile =>  v_hints,                                  
 description => ‘how to use sql_profile’,                                  
force_match => true);
end;
 /
 

第八步,再次验证执行计划

以上简单的八步,就完成了线下的SQL PROFILE产生与验证。剩下的,只需要把第七步再拿到线上执行并验证即可。

虽然SQL PROFILE用法简单,也可以迅速地解决线上SQL问题。但是,把这玩意儿长期放在数据库中,总感觉是一种隐患。我对它的理解,sql profile仅可用于应急,最终还是得由应用程序修改,解决问题后,删除sql profile,这我才可放心。

另外,有一点是需要提醒的,当SQL概要依赖的对象被删除的时候,SQL概要并不会删除。ORACLE为什么不把它级联删除掉呢? 可以想像一下,当删除一个索引的时候,基本可以说明它是没有价值的。但是如索引重新组织或重建后,我们可能还是需要SQL PROFILE的,如果在重建的时候删除了,使用这个profile的SQL语句又杯具了。

 

 

Posted in SQL, SQL TUNING | Tagged , , | Leave a comment

11G新特性:RESULT CACHE

     大家知道机械磁盘的一次IO延时大约是5-7毫秒,而内存IO的延时大约是80纳秒。显然,如果在内存足够大的情况下,大家都愿意在内存中读取数据吧!这时候,也许你会想到data buffer cache,也许你会想到JVM对象缓存等等。

     但是,有没有想过,就算是在buffer cache中100%地命中,逻辑读总避免不了,而逻辑读带来的CPU开销总避免不了。而JVM对象缓存是否合适呢? 这得根据你的表大小及更新程度来决定;很显然,如果表的DML操作很频繁,或者表比较大,这样并不适合使用对象缓存。

而在某些特定场景下,我们是非常推荐使用RESULT CACHE的。如:我的一个表非常大,读写比可能达到了10000:1,但是我每次的查询条件几乎就只有那么几类,只获取表中很少的一部分特定的数据。这时候,我们就可以利用RESULT CACHE了。下面将对RESULT CACHE做一个简单的介绍:

结果缓存将SQL和PLSQL查询的结果集存放在SHARE POOL的RESULT CACHE MEMORY中。当这些查询被重复执行的时候,则直接从CACHE中获取这些结果集。当依赖的数据库对象发生改变以后,这些缓存的结果集将会失效。RESULT CACHE是在实例级被指定的,且可以在不同的实例间使用不同的大小。在RAC环境中,要禁用RESULT CACHE,你必须明确地指定RESULT_CACHE_MAX_SIZE初始化参数为0。

RESULT CACHE可能给我们带来的好处:  减少物理IO 、减少逻辑IO、减少排序、减少CPU开销

RESULT CACHE MEMORY由存放SQL查询结果集的SQL QUERY RESULT CACHE和存放PLSQL查询结果集的 PLSQL FUNCTION RESULT CACHE组成。

RESULT CACHE可分为服务端结果缓存客户端结果缓存

服务端结果缓存:使用SGA的SHARED POOL的RESULT CACHE MEMORY,可被所有的会话访问。

客户端结果缓存:需要OCI,使用的是客户端的缓存。

1、SQL QUERY RESULT CACHE

1、1  RESULT_CACHE_MODE:

MANUAL,你必须使用result_cache提示显示指定。
FORCE,强制使用,如果想不使用,必须显示指定no_result_cache提示。
select /*+ result_cache */ count(*)  from test ;
 

1.2 使用result cache的限制:

字典表和临时表
SEQUENCE的CURRVAL和NEXTVAL、伪列
一些函数:current_date
字典表和临时表
SEQUENCE的CURRVAL和NEXTVAL、伪列
一些函数:current,current_timestamp,local_timestamp,userenv/sys_context,sys_guid,sysdate,sys_timestamp
未明确定义的PLSQL函数
 

1.3 结果缓存参数化:

对于绑定变量等SQL,是参数化的结果缓存。当传入的值与参数值相等的时候,参数化结果缓存可被重用。

 1.4 子查询与视图的结果缓存

子查询的查询结果不会被缓存
视图和INLINE视图的查询结果可以被缓存
在INLINE 视图中添加 RESULT_CACHE HINT来最大可能地重用结果缓存
 

1.5 例子

select count(*) from test where object_id <20;

1.5.1 NO RESULT CACHE

Statistics
———————————————————-          
 1  recursive calls          
0  db block gets         
 21  consistent gets          
 0  physical reads          
 0  redo size        
 527  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
 

1.5.2 USE RESULT CACHE

  Statistics
 ———————————————————-          
0  recursive calls          
 0  db block gets          
 0  consistent gets          
0  physical reads          
0  redo size        
527  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
 

可以看到在使用了RESULT CACHE的情况下,连逻辑读都省了。

 

2  PLSQL FUNCTION RESULT CACHE

必须包含RESULT_CACHE子句。RELIES_ON子句是可选的,但是建议使用.

     CREATE OR REPLACE FUNCTION get_object_name (v_object_id NUMBER)     
RETURN VARCHAR2    
 RESULT_CACHE RELIES_ON (test)     
IS v_object_name VARCHAR2(50);    
  BEGIN       
 SELECT object_name INTO v_object_name        
 FROM test          
WHERE object_id = v_object_id;           
RETURN v_object_name;      
END;    
 /   
       
SELECT get_object_name(1) FROM dual;
 

3、DBMS_RESULT_CACHE包 

ORACLE为管理RESULT CACHE,给我们提供了DBMS_RESULT_CACHE包。

 BYPASS 后面的所有子查询都将绕过结果缓存    
 FLUSH 将缓存结果集刷出内存    
 INVALIDATE 使得所有的缓存结果集失效    
 INVALIDATE_OBJECT 使得指定的缓存结果集失效     
MEMORY_REPORT  要求 SERVEROUTPUT ON,显示出所有的内存中RESULT CACHE信息     
STATUS 返回RESULT CACHE的状态
 

   4、RESULT CACHE的相关参数

    result_cache_max_result:占用内存的最大大小百分比,默认是5%     
    result_cache_max_size:使用内存的最大大小,设为0表示禁用RESULT CACHE     
   result_cache_mode:可设置为AUTO,MANUAL,FORCE    
   result_cache_remote_expiration:基于远程对象结果集的临时有效期,默认为0    
    _result_cache_auto_execution_threshold result cache auto execution threshold     _result_cache_auto_size_threshold result cache auto max size allowed    
    _result_cache_auto_time_distance result cache auto time distance    
    _result_cache_auto_time_threshold result cache auto time threshold    
   _result_cache_block_size result cache block size defaults to 1024 bytes     
   _result_cache_timeout maximum time (sec) a session waits  for a result defaults to 60 seconds         
     

 5、动态性能视图

 V$RESULT_CACHE_STATISTICS 由基表X$QESRCSTA而来,每行数据表示每一项结果缓存的状态信息     
V$RESULT_CACHE_OBJECTS 由基表X$QESRCOBJ而来,每行数据表示结果缓存中的每个对象信息     
V$RESULT_CACHE_MEMORY  由基表X$QESRCMEM而来,每行数据表示RESULT CACHE中每块的使用情况     
V$RESULT_CACHE_DEPENDENCY 由基表X$QESRCDEP而来,每行数据表示结果缓存集与数据字典对象的依赖
 

 6、客户端的结果缓存

    首先,它避免了执行SQL语句时在客户端/服务器端往返所需的开销。其次,失效操作基于一种轮询的机制,因此,数据一致性无法确保。为了尽可能降低轮询开销,客户端每次执行一个数据库调用的时候,都会同时检查缓存的结果集的有效性。

◎ client_result_cache_size指定每个客户端进程可用来为结果缓存分配的最大内存大小(单位为字节)。这个初始化参数是个静态参数,只能在实例级别设置。
◎ client_result_cache_lag指定两次数据库调用之间的最大时间间隔(单位为毫秒)。也就是指定无效数据可以在客户端缓存中保留的最长时间。默认值是3000.静态参数

除了服务器端的配置外,还可以在客户端的sqlnet.ora文件中设置下列参数:

◎ oci_result_cache_max_size
◎ oci_result_cache_max_rset_size
◎ oci_result_cache_max_rset_rows
Posted in NEW FEATURE | Tagged | Leave a comment