二、工作机制 从Oracle 11g开始,由于基线的存在,一条语句的解析过程大概如下:
- SQL语句被硬解析后,CBO(优化器)会产生很多个的执行计划,CBO从中选择一个成本最低执行计划。
- 基于SQL语句的文本形成一个哈希值(signature),通过这个哈希值来检查数据字典中是否存在同样的基线。
- 如果基线存在,优化器会对刚刚产生的执行计划和保存在SQL plan baseline中的执行计划进行比较。
- 如果基线中有与CBO刚产生的执行计划的匹配的SQL执行计划存在,并且被标记为可接受(‘accepted’),则这个CBO生成的执行计划被启用。
- 如果基线中没有匹配的SQ执行计划存在,CBO评估基线中被标记为‘accepted’的的多个执行计划,并选择其中cost最低的执行计划。(注意,一个语句的基线可以有多个执行计划被保存,这是与其他Outline和SQL profiel都不同的地方)
- 如果刚刚硬解析过程中CBO选择的执行计划比保存在基线中的执行计划COST都低,这个新生成的执行计划被标记为‘not-accepted’并保存在基线中。直到这个执行计划被演化且验证后才会被考虑使用,即标记为accepted(演化和验证,可以简单理解为Oracle确认这个执行计划可以带来更好的性能)。
三、基线的一些特点简单归纳如下几个
- 通过OPTIMIZER_USE_SQL_PLAN_BASELINE来控制Oracle是否使用基线,默认值为TRUE,即会自动使用基线。
- 11g中默认是不会自动创建基线
- 与OUTLINE和SQL Profile不同,基线中不存在分类的概念
- 与OUTLINE和SQL Profile不同,每个SQL语句可以有多个基线。Oracle根据制定的规则来判断具体是否哪个基线
- 基线针对RAC中所有的实例都生效
- 基线有两个表示,一个为sql_handle,可以理解为表示语句文本的唯一标识,一个为sql_plan_name可以理解为执行计划的唯一标识
- 不能像sql profile一样通过force_matching属性将字面值不一样的SQL语句使用一个基线应用多个语句。
四、基线的几种状态一个SQL语句对应的基线,我将它们归纳为三种状态
- accepted(可接受),只有这种状态的基线,优化器才会考虑此基线中的执行计划
- no-accepted(不可接受),这种状态的基线,优化器在SQL语句解析期间不会考虑。这种状态的基线必须通过演化和验证通过后,转变为accepted状态后,才会被优化器考虑使用
- fixed为yes(固定),这种状态的基线固有最高优先级!比其他两类基线都要优先考虑
六、演化基线 为了验证基线中一个处于不可接受状态的执行计划是否比一个处于可接受状态的执行计划具有更高的效率,必须通过演化来验证,需要让优化器以不同的执行计划来执行这条SQL语句,观察不可接受状态的执行计划基线是否会带来更好的性能,如果性能确实更高,这个不可接受状态的基线将会转换为可接受状态。演化的方式有两种: 1、手工执行运行 SELECT DBMS_SPM.evolve_sql_plan_baseline(sql_handle => 'SYS_SQL_xxxxxxxxxxxxx') From dual; 还有time_limit/verify/commit几个参数,可以参考文档 2、调优包实现基线的自动演化,可以理解为,启动一个调度任务,周期性的检查是否有不可接受状态的基线可以被演化
七、修改基线 可以通过dbms_spm.alter_sql_plan_baseline包来修改基线的一些属性,主要有如下几个属性
- ENABLED :设置该属性的值为NO告诉Oracle 11g临时禁用某个计划,一个SQL计划必须同时标记为ENABLED和ACCEPTED,否则CBO将忽略它
- FIXED:设置为YES,那个计划将是优化器唯一的选择[最高优先级],即使如果某个计划可能拥有更低的成本。这让DBA可以撤销SMB的默认行为,对于转换一个存储概要进入一稳定的SQL计划基线特别有用,注意当一个新计划被添加到被标记为FIXED的SQL计划基线,该新计划不能被利用除非它申明为FIXED状态
- AUTOPURG:设置这个属性的值为NO告诉Oracle 11g无限期保留它,从而不用担心SMB的自动清除机制
- plan_name : 改变SQL plan 名字
- description : 改变SQL plan描述
八、迁移基线dbms_spm提供了多个过程来在数据库之间迁移SQL计划基线
- create_stgtab_baseline创建一个计划基线保存表
- pack_stgtab_baseline将基线从数据字典复制到第一步的表中
- unpack_stgtab_baseline将基线从保存表中复制到迁移数据库的数据字典中
九、删除基线
- 可以通过dbms_SPM.drop_sql_plan_baseline包来手工删除数据字典里的基线
- 为使用的基线,fixed为no的基线,将在一定的保留期后自动删除(可查看dba_sql_management_config视图)
十、将一个SQL语句固定为我们期望的执行计划我一般通过如下几步实现(仅供参考)1、为这个SQL语句创建基线2、给这个SQL语句添加hint赖宇星,确保SQL语句添加hint后的执行计划与我们期望一样3、将第2步产生的执行计划,添加到第一步创建的基线中(注意,前面已经说过,一个SQL语句可以有多个基线!)4、删除基线中第1步创建的那个执行计划(这样,我们就可以确保基线中只有我们期望的执行计划,即保存第2步SQL语句的执行计划)5、验证是否生效后续有示例,加深理解!
十一、示例(将一个SQL语句固定为我们期望的执行计划) 首先运行两个结构相同的语句,下面的实验通过SQL计划基线,将一个语句的执行计划通过另一个语句的执行计划来固定 SQL> select sql_handle,plan_name,dbms_lob.substr(sql_text,60,1) sql_text,ACCEPTED from dba_sql_plan_baselines;未选定行SQL> alter system flush shared_pool;系统已更改。SQL> select /* outlinetest2 */ /*+ full(dh_stat) */ * from dh_stat where id=711; ID NAME TYPE---------- ------------------------------ --------------- 711 I_STREAMS_PROCESS_PARAMS1 INDEXSQL> select /* outlinetest3 */ /*+ index(dh_stat) */ * from dh_stat where id=711; ID NAME TYPE---------- ------------------------------ --------------- 711 I_STREAMS_PROCESS_PARAMS1 INDEX
SQL> select sql_text,sql_id,hash_value,child_number,plan_hash_value,to_char(LAST_ACTIVE_TIME,'hh24:mi:ss') time 2 from v$sql a where sql_text like '%outlinetest%' and sql_text not like '%v$sql%';SQL_TEXT SQL_ID HASH_VALUE CHILD_NUMBER PLAN_HASH_VALUE TIME------------------------------------------------------- ------------- ---------- ------------ --------------- --------select /* outlinetest2 */ /*+ full(dh_stat) */ * from d 4vaj9fgjysy9c 3823925548 0 1845196118 12:27:31h_stat where id=711select /* outlinetest3 */ /*+ index(dh_stat) */ * from fm35jcmypb3qu 4250242778 0 2780970545 12:27:41dh_stat where id=711
SQL> select * from table(dbms_xplan.display_cursor('4vaj9fgjysy9c','',''));PLAN_TABLE_OUTPUT-----------------------------------------------------------------------------------------------------------------------------------------------SQL_ID 4vaj9fgjysy9c, child number 0-------------------------------------select /* outlinetest2 */ /*+ full(dh_stat) */ * from dh_stat whereid=711Plan hash value: 1845196118-----------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |-----------------------------------------------------------------------------| 0 | SELECT STATEMENT | | | | 124 (100)| ||* 1 | TABLE ACCESS FULL| DH_STAT | 1 | 38 | 124 (1)| 00:00:02 |-----------------------------------------------------------------------------Predicate Information (identified by operation id):--------------------------------------------------- 1 - filter("ID"=711)已选择19行。
SQL> select * from table(dbms_xplan.display_cursor('fm35jcmypb3qu','',''));PLAN_TABLE_OUTPUT-----------------------------------------------------------------------------------------------------------------------------------------------SQL_ID fm35jcmypb3qu, child number 0-------------------------------------select /* outlinetest3 */ /*+ index(dh_stat) */ * from dh_stat whereid=711Plan hash value: 2780970545---------------------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |---------------------------------------------------------------------------------------| 0 | SELECT STATEMENT | | | | 2 (100)| || 1 | TABLE ACCESS BY INDEX ROWID| DH_STAT | 1 | 38 | 2 (0)| 00:00:01 ||* 2 | INDEX RANGE SCAN | IND_1 | 1 | | 1 (0)| 00:00:01 |---------------------------------------------------------------------------------------Predicate Information (identified by operation id):--------------------------------------------------- 2 - access("ID"=711)已选择20行。
SQL> DECLARE 2 k1 pls_integer; 3 begin 4 k1 := DBMS_SPM.LOAD_PLANS_FROM_CURSOR_CACHE ( 5 sql_id=>'4vaj9fgjysy9c', 6 plan_hash_value=>1845196118 7 ); 8 end; 9 /PL/SQL 过程已成功完成。
SQL> select sql_handle,plan_name,dbms_lob.substr(sql_text,60,1) sql_text,ACCEPTED from dba_sql_plan_baselines;
SQL_HANDLE PLAN_NAME SQL_TEXT ACC------------------------------ ------------------------------ ------------------------------------------------------- ---SYS_SQL_11bcd50cd51504e9 SQL_PLAN_13g6p1maja17934f41c8d select /* outlinetest2 */ /*+ full(dh_stat) */ * from d YES h_sta
刚生产sql plan baseline的时候,第一次查询,无法找到执行计划,直到第二次执行的时候,才能看到,如下SQL> select /* outlinetest2 */ /*+ full(dh_stat) */ * from dh_stat where id=711; ID NAME TYPE---------- ------------------------------ --------------- 711 I_STREAMS_PROCESS_PARAMS1 INDEXSQL> select * from table(dbms_xplan.display_cursor('4vaj9fgjysy9c','',''));PLAN_TABLE_OUTPUT---------------------------------------------------------SQL_ID: 4vaj9fgjysy9c cannot be foundSQL> select sql_text,sql_id,hash_value,child_number,plan_hash_value,to_char(LAST_ACTIVE_TIME,'hh24:mi:ss') time 2 from v$sql a where sql_text like '%outlinetest%' and sql_text not like '%v$sql%';SQL_TEXT SQL_ID HASH_VALUE CHILD_NUMBER PLAN_HASH_VALUE TIME------------------------------------------------------- ------------- ---------- ------------ --------------- --------select /* outlinetest3 */ /*+ index(dh_stat) */ * from fm35jcmypb3qu 4250242778 0 2780970545 12:27:41dh_stat where id=711
SQL> select /* outlinetest2 */ /*+ full(dh_stat) */ * from dh_stat where id=711; ID NAME TYPE---------- ------------------------------ --------------- 711 I_STREAMS_PROCESS_PARAMS1 INDEXSQL> select sql_text,sql_id,hash_value,child_number,plan_hash_value,to_char(LAST_ACTIVE_TIME,'hh24:mi:ss') time 2 from v$sql a where sql_text like '%outlinetest%' and sql_text not like '%v$sql%';
SQL_TEXT SQL_ID HASH_VALUE CHILD_NUMBER PLAN_HASH_VALUE TIME------------------------------------------------------- ------------- ---------- ------------ --------------- --------select /* outlinetest2 */ /*+ full(dh_stat) */ * from d 4vaj9fgjysy9c 3823925548 0 1845196118 12:30:54h_stat where id=711select /* outlinetest3 */ /*+ index(dh_stat) */ * from fm35jcmypb3qu 4250242778 0 2780970545 12:27:41dh_stat where id=711
SQL> select * from table(dbms_xplan.display_cursor('4vaj9fgjysy9c','',''));PLAN_TABLE_OUTPUT-----------------------------------------------------------------------------------------------------------------------------------------------SQL_ID 4vaj9fgjysy9c, child number 0-------------------------------------select /* outlinetest2 */ /*+ full(dh_stat) */ * from dh_stat whereid=711
Plan hash value: 1845196118-----------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |-----------------------------------------------------------------------------| 0 | SELECT STATEMENT | | | | 124 (100)| ||* 1 | TABLE ACCESS FULL| DH_STAT | 1 | 38 | 124 (1)| 00:00:02 |-----------------------------------------------------------------------------Predicate Information (identified by operation id):---------------------------------------------------
1 - filter("ID"=711)Note----- - SQL plan baseline SQL_PLAN_13g6p1maja17934f41c8d used for this statement已选择23行。
将符合我们预期的执行计划的加载到第一次生成的sql baseline中!SQL> DECLARE 2 k1 pls_integer; 3 begin 4 k1 := DBMS_SPM.LOAD_PLANS_FROM_CURSOR_CACHE ( 5 sql_id=>'fm35jcmypb3qu', 6 plan_hash_value=>2780970545,sql_handle=>'SYS_SQL_11bcd50cd51504e9' 7 ); 8 end; 9 /
PL/SQL 过程已成功完成。
可以看到,SYS_SQL_11bcd50cd51504e9下目前有两个plan_nameSQL> select sql_handle,plan_name,dbms_lob.substr(sql_text,60,1) sql_text,ACCEPTED from dba_sql_plan_baselines;
SQL_HANDLE PLAN_NAME SQL_TEXT ACC------------------------------ ------------------------------ ------------------------------------------------------- ---SYS_SQL_11bcd50cd51504e9 SQL_PLAN_13g6p1maja1790cce5f0e select /* outlinetest2 */ /*+ full(dh_stat) */ * from d YES h_sta
SYS_SQL_11bcd50cd51504e9 SQL_PLAN_13g6p1maja17934f41c8d select /* outlinetest2 */ /*+ full(dh_stat) */ * from d YES h_sta删除第一个plan_name,即将我们不需要的执行计划版本去除掉!SQL> DECLARE 2 k1 pls_integer; 3 begin 4 k1 := DBMS_SPM.drop_sql_plan_baseline ( sql_handle=>'SYS_SQL_11bcd50cd51504e9',plan_name=>'SQL_PLAN_13g6p1maja17934f41c8d'); 5 end; 6 /
PL/SQL 过程已成功完成。
通过下面的一部分测试,我们可以看到,新的SQL计划基线已经正常生效,及时语句中包含full提示,执行计划也走索引定位数据SQL> select /* outlinetest2 */ /*+ full(dh_stat) */ * from dh_stat where id=711; ID NAME TYPE---------- ------------------------------ --------------- 711 I_STREAMS_PROCESS_PARAMS1 INDEX
SQL> select * from table(dbms_xplan.display_cursor('4vaj9fgjysy9c','',''));PLAN_TABLE_OUTPUT-----------------------------------------------------------------------------------------------------------------------------------------------SQL_ID 4vaj9fgjysy9c, child number 1-------------------------------------select /* outlinetest2 */ /*+ full(dh_stat) */ * from dh_stat whereid=711Plan hash value: 2780970545---------------------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |---------------------------------------------------------------------------------------| 0 | SELECT STATEMENT | | | | 2 (100)| || 1 | TABLE ACCESS BY INDEX ROWID| DH_STAT | 1 | 38 | 2 (0)| 00:00:01 ||* 2 | INDEX RANGE SCAN | IND_1 | 1 | | 1 (0)| 00:00:01 |---------------------------------------------------------------------------------------Predicate Information (identified by operation id):--------------------------------------------------- 2 - access("ID"=711)Note----- - SQL plan baseline SQL_PLAN_13g6p1maja1790cce5f0e used for this statement已选择24行。
可以通过dba_sql_plan_baselines来显示可用的SQL计划基线的一般信息,也可以通过如下这种方式显示执行SQL计划基线的详细信息!select * from table(dbms_xplan.display_sql_plan_baseline(sql_handle=>'SYS_SQL_11bcd50cd51504e9',plan_name=>'SQL_PLAN_13g6p1maja1790cce5f0e'));
PLAN_TABLE_OUTPUT-----------------------------------------------------------------------------------------------------------------------------------------------SQL handle: SYS_SQL_11bcd50cd51504e9SQL text: select /* outlinetest2 */ /*+ full(dh_stat) */ * from dh_stat where id=711--------------------------------------------------------------------------------
--------------------------------------------------------------------------------Plan name: SQL_PLAN_13g6p1maja1790cce5f0e Plan id: 214851342Enabled: YES Fixed: NO Accepted: YES Origin: MANUAL-LOAD--------------------------------------------------------------------------------
Plan hash value: 2780970545
---------------------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |---------------------------------------------------------------------------------------| 0 | SELECT STATEMENT | | 1 | 38 | 2 (0)| 00:00:01 || 1 | TABLE ACCESS BY INDEX ROWID| DH_STAT | 1 | 38 | 2 (0)| 00:00:01 ||* 2 | INDEX RANGE SCAN | IND_1 | 1 | | 1 (0)| 00:00:01 |---------------------------------------------------------------------------------------
Predicate Information (identified by operation id):---------------------------------------------------
2 - access("ID"=711)已选择26行。
查看SQL计划基线中保存的hint提示集合SQL> conn /as sysdba已连接。SQL> select 2 extractvalue(value(d), '/hint') as outline_hints 3 from 4 xmltable('/outline_data/hint' 5 passing ( 6 select 7 xmltype(comp_data) as xmlval 8 from 9 sqlobj$data sod, sqlobj$ so 10 where so.signature = sod.signature 11 and so.plan_id = sod.plan_id 12 and comp_data is not null 13 and name like '&baseline_plan_name' 14 ) 15 ) d;输入 baseline_plan_name 的值: SQL_PLAN_13g6p1maja1790cce5f0e原值 13: and name like '&baseline_plan_name'新值 13: and name like 'SQL_PLAN_13g6p1maja1790cce5f0e'
OUTLINE_HINTS-----------------------------------------------------------------------------------------------------------------------------------------------IGNORE_OPTIM_EMBEDDED_HINTSOPTIMIZER_FEATURES_ENABLE('11.2.0.1')DB_VERSION('11.2.0.1')ALL_ROWSOUTLINE_LEAF(@"SEL$1")INDEX_RS_ASC(@"SEL$1" "DH_STAT"@"SEL$1" ("DH_STAT"."ID"))
已选择6行。