- 数据库高效优化:架构、规范与SQL技巧
- 马立和 高振娇 韩锋
- 6566字
- 2020-08-27 05:47:11
2.1 优化器
优化器在整个SQL语句的执行过程中充当了非常重要的角色。图2-1是一个SQL语句从提交到最终得到结果的示意图,从中我们可以看到优化器充当的角色及其主要功能。
Oracle的优化器也是在不断演变中的。在早期的版本中,Oracle使用一种基于规则的优化器。顾名思义,它是按照某种特定的规则来制定执行计划的。这种方式比较简单直观,但对数据库自身情况及SQL语句中对象本身的情况都没有考虑。在后期的Oracle版本中,又推出了另外一种优化器——基于成本的优化器。下面将对两种主要的优化器分别加以介绍,并对和优化器相关的数据库参数和提示进行说明。
图2-1 SQL语句执行过程
2.1.1 基于规则的优化器
基于规则的优化器(Rule Based Optimizer,RBO)内部采用了一种规则列表,其中每一种规则代表一种执行路径并被赋予一个等级,不同的等级代表不同的优先级别。等级越高的规则越会被优先采用。Oracle会在代码里事先给各种类型的执行路径定一个等级,一共有15个等级,从等级1到等级15。Oracle会认为等级值低的执行路径的执行效率比等级值高的执行效率高。在决定目标SQL的执行计划时,如果可能的执行路径不止一条,则RBO就会从该SQL多种可能的执行路径中选择一条等级最低的执行路径来作为其执行计划。
1.RBO的具体规则
下面我们就来看看RBO的具体规则,如表2-1所示。
表2-1 RBO规则
下面针对表2-1中所示的每一种规则的含义及其用法进行说明。
·Single Row by ROWID:根据ROWID,返回一条记录。这种规则发生在SQL语句的WHERE部分,指定了记录的ROWID或者使用了CURRENT OF CURSOR形式的SQL。
·Single Row by Cluster Join:根据聚簇连接,返回一条记录。这种规则发生在SQL语句中WHERE部分,包含了两表关联,且关联字段为一个聚簇,同时还存在一个过滤条件为一个表的唯一索引或主键。
·Single Row by Hash Cluster Key with Unique or Primary Key:根据哈希聚簇键,返回一条记录。这种规则发生在SQL语句的WHERE部分所包含的过滤条件中,字段是一个哈希聚簇键且这个字段为唯一或主键索引字段。
·Single Row by Unique or Primary Key:根据主键或唯一索引键值,返回一条记录。这种规则发生在SQL语句中WHERE部分,为唯一或主键所有字段的等值连接条件。
·Clustered Join:根据聚簇连接,返回一组记录。这种规则跟Path 2类似,只不过过滤条件中没有唯一限制,可以返回多条记录。
·Hash Cluster Key:根据哈希聚簇键值,返回一条记录。这种规则跟表2-1所示Path 3类似,只不过过滤条件中没有唯一限制,可以返回多条记录。
·Indexed Cluster Key:根据一个索引的聚簇键字段,返回一组记录。
·Composite Index:根据一个组合索引字段,返回一组记录。这种规则中WHERE部分需要指定组合索引字段且通过逻辑“与”运算符进行连接。
·Single-Column Indexes:根据单一索引字段,返回一组记录。
·Bounded Range Search on Indexed Columns:根据索引字段的有限范围搜索,返回一组记录。这里所说的有限范围搜索,包括字段的等值比较、大于等于和小于等于、BETWEEN...AND、LIKE等过滤条件。
·Unbounded Range Search on Indexed Columns:根据索引字段的无限范围搜索,返回一组记录。这里所说的无限范围搜索,包括字段的大于等于、小于等于过滤条件。
·Sort Merge Join:根据排序合并关联,返回一组记录。
·MAX or MIN of Indexed Column:获取一个索引字段的最大、最小值。这种规则需要遍历整个索引。
·ORDER BY on Indexed Column:根据一个索引字段,进行排序操作。
·Full Table Scan:通过全表扫描方式,获取一个结果集。
2.RBO在实际工作中的应用
在一般的工作场景中,很少会涉及使用RBO的情况。随着Oracle自身技术的发展,CBO优化器成为首选。只有在极个别的情况下,需要手工调整执行计划时,可采取指定优化器参数或引用相关的提示(参见后面的介绍)。需要注意的是,因为RBO技术出现比较早,很多新的技术不支持,所以在很多情况下即使手工指定使用RBO优化器,也可能会失效,Oracle仍然会使用CBO优化器。下面介绍一下失效的情况。
只要出现如下的情形之一(包括但不限于这些情形),那么即便修改了优化器模式或者使用了RULE Hint,Oracle依然不会使用RBO(而是强制使用CBO)。
·目标SQL中涉及的对象有IOT。
·目标SQL中涉及的对象有分区表。
·使用了并行查询或者并行DML。
·使用了星型连接。
·使用了哈希连接。
·使用了索引快速全扫描。
·使用了函数索引。
2.1.2 基于成本的优化器
基于成本的优化器(Cost Based Optimizer,CBO)在坚持实事求是的基础上,通过对具有现实意义的诸多要素的分析和计算来完成最优路径的选择工作。这里的关键点在于对成本的理解,后面会有对成本的专门介绍。这里简单交代一句,成本可以理解为SQL执行的代价。成本越低,SQL执行的代价越小,CBO也就认为这是一个更优异的执行路径。
随着Oracle版本的不断演变,CBO优化器变得越来越智能,但需要注意的是,CBO仍然存在一些特殊情况,导致其可能产生较差的执行计划。这也是以后CBO发展需要弥补的弱点。CBO存在的问题主要有以下几个方面。
·多列关联关系:在默认情况下,CBO认为WHERE条件中的各个字段之间是独立的,并据此计算其选择率,进而估计成本来选择执行计划。但如果各列之间有某种关系,则估算的结果与实际结果之间往往存在较大误差。可以通过动态采样或者多列统计信息的方法解决部分问题,但都不是完美的解决方案。
·SQL无关性:CBO认为SQL语句运行都是相对独立的,之间没有任何关系;但在实际运行中可能是有关联的。例如前一条语句访问某个索引,则相关数据块会被缓存到Data Buffer中,后续SQL如果也需要访问这个索引,则可以从Cache获得,这将大大减少读取成本,但这一点CBO是无法感知的。
·直方图统计信息:一方面在12c之前,基于频率的直方图的桶的个数不能超过254,这可能导致一些精度的丢失。另一方面,对于文本型字段的直方图收集,Oracle只会提取前32字节(对于多字节字符集来说更加严重),这样获得的数据会失真,可能会导致优化器获得错误的执行计划。
·复杂多表关联:对于复杂的多表关联,其可能的表间关联顺序组合随着表的数量增加呈几何级数增长。假设多表关联的目标SQL包含表的数量为n,则该SQL各表之间可能的连接顺序的总数就是n!。CBO在处理这个问题时,是有所取舍的。在11gR2的版本中,CBO在解析这种多表关联的目标SQL时,所考虑的各个表连接顺序的总和会受到隐含参数_OPTIMIZER_MAX_PERMUTATIONS的限制。这意味着不管目标SQL在理论上有多少种可能的连接顺序,CBO至多只会考虑其中根据_OPTIMIZER_MAX_PERMUTATIONS计算出来的有限种可能。这同时也意味着只要该目标SQL正确的执行计划不在上述有限种可能之中,则CBO一定会漏选最优的执行计划。
2.1.3 对比两种优化器
RBO和CBO的优缺点对比如表2-2所示。
表2-2 RBO和CBO的优缺点对比
在通常情况下,已经没有理由不选用CBO优化器了,这也是Oracle强大之所在。在极个别的情况下,也存在对CBO优化器不适合使用的情况,原因可能是BUG或者CBO设计问题。此时可以考虑使用RBO优化器,但即使是这种情况,也要严格限制特定范围,一般只在语句级使用RBO优化器。
2.1.4 优化器相关参数
本小节重点介绍几个与优化器密切相关的参数。想真正了解优化器,这些参数是必须掌握的。
1.optimizer_mode
数据库使用哪种优化器主要是由optimizer_mode初始化参数决定的。
(1)取值说明
·RULE:使用RBO优化器。需要注意的是即使指定数据库使用RBO优化器,但有时Oracle数据库还是会采用CBO优化器,这并不是Oracle的BUG,主要是由于从Oracle 8i后引入的许多新特性都必须在CBO下才能使用,而你的SQL语句可能正好使用了这些新特性,此时数据库会自动转为CBO优化器执行这些语句。
·CHOOSE:根据实际情况,如果数据字典中包含被引用的表的统计数据,即引用的对象已经被分析,那么使用CBO优化器,否则为RBO优化器。CHOOSE是Oracle 9i的默认值。
·ALL_ROWS:为CBO优化器使用的第一种具体的优化方法,以数据的吞吐量为主要目标,以便可以使用最少的资源完成语句;是10g以及后续版本中optimizer_mode的默认值数。
·FIRST_ROWS:为优化器使用的第二种具体的优化方法,以数据的响应时间为主要目标,以便快速查询出开始的几行数据。
·FIRST_ROWS_[1 | 10 | 100 | 1000]:为优化器使用的第三种具体的优化方法,让优化器选择一个能够把响应时间减到最小的查询执行计划,以迅速产生查询结果的前n行。该值为Oracle 9i新引入的。注意以前的FIRST_ROWS已经不再使用,仅仅是为了向后兼容的需要。
(2)默认值变化
·在8i、9i等版本中,CHOOSE为默认值;在10g及以后不再支持基于RULE的优化器中,新的默认值为ALL_ROWS。因此,参数值CHOOSE和RULE都不再被支持。
·虽然从Oracle 10g开始,RBO优化器已不再被Oracle支持,但RBO优化器的相关实现代码并没有从Oracle数据库的代码中移除,这意味着即使是在11gR2中,依然可以通过修改优化器模式或使用RULE Hint来继续使用RBO。
(3)相关操作
初始化参数optimizer_mode是动态的,可以在实例级或会话级改变。此外使用提示(hint),也可以在SQL级别设置优化器。
//查看实例级优化器设置 select name,value,isdefault,ismodified,description from v$system_parameter where name like '%optimizer_mode%'; //修改会话级优化器设置 alter session set optimizer_mode=..; //查看当前会话设置 select name,value,isdefault,ismodified,description fromv$parameter where name like '%optimizer_mode%'; //相关提示 /*+ all_rows ... */ /*+ first_rows(n) ... */ 2. optimizer_features_enable
optimizer_features_enable参数控制使用的优化器特征的版本,比如从Oracle 8i升级到了Oracle 9i,默认情况下参数为9.2.0,如果将它设置为8.1.6,那么将使用Oracle 8i的优化器特征。Oracle不推荐显式设置该参数,而是更改应用程序中的相关SQL。参数optimizer_features_enable不仅能禁用特性,而且能禁用BUG修复。
(1)相关操作
1)查看可用的版本号,代码如下:
select value fromv$parameter_valid_values where name='optimizer_features_enable'; VALUE --------- 8.0.0 8.0.3 … 10.2.0.4 10.2.0.4.1
10gR1以前的版本不存在这个视图。要获得可用版本号,可以执行一个错误的设置,由系统提供可选项。类似下面的做法:
alter session set optimizer_features_enable='1.0.0'; ERROR: ORA-00096: invalid value 1.0.0 for parameter optimizer_features_enable, must be from among 10.2.0.4.1, 10.2.0.4, 10.2.0.3, 10.2.0.2, 10.2.0.1, 10.1.0.5, 10.,9.0.1, 9.0.0, 8.1.7, 8.1.6, 8.1.5, 8.1.4, 8.1.3, 8.1.0, 8.0.7, 8.0.6, 8.0.5, 8.0.4, 8.0.3, 8.0.0
2)设置版本号:可以在实例、会话、SQL级别设定optimizer_features_enable参数。设置示例分别如下。
alter system set optimizer_features_enable='9.2.0'; //实例级别 alter session set optimizer_features_enable='9.2.0'; //会话级别 /*+ optimizer_features_enable('9.2.0') */ //SQL级别(Hint)
(2)测试案例
下面通过一个简单的案例说明optimizer_features_enable参数的作用。案例通过设置optimizer_features_enable参数,模拟了不同版本数据库中不同的表间关联处理方式。具体关于表间关联的细节,可参见本书相应章节,这里只是说明参数的使用方法。案例中数据库版本为11gR2。
1)准备工作,具体如下:
create table anti_test1 as select * from dba_objects; create table anti_test2 as select * from dba_objects; desc anti_test1; Name Null? Type ------------------- -------- --------------------- OWNER VARCHAR2(30) OBJECT_NAME VARCHAR2(128) SUBOBJECT_NAME VARCHAR2(30) OBJECT_ID NUMBER DATA_OBJECT_ID NUMBER OBJECT_TYPE VARCHAR2(19) CREATED DATE LAST_DDL_TIME DATE TIMESTAMP VARCHAR2(19) STATUS VARCHAR2(7) TEMPORARY VARCHAR2(1) GENERATED VARCHAR2(1) SECONDARY VARCHAR2(1) NAMESPACE NUMBER EDITION_NAME VARCHAR2(30) //注意测试表中的OBJECT_ID字段是可以为空的。
2)测试11g的情况,具体如下:
select * from anti_test1 a where a.object_id not in (select b.object_id from anti_test2 b); ------------------------------------------------------------------------------ | Id | Operation | Name | Rows | Cost (%CPU)| Time | ------------------------------------------------------------------------------ | 0 | SELECT STATEMENT | | 65273 | 1384 (1)| 00:00:17 | |* 1 | HASH JOIN RIGHT ANTI NA| | 65273 | 1384 (1)| 00:00:17 | | 2 | TABLE ACCESS FULL | ANTI_TEST2 | 102K| 292 (1)| 00:00:04 | | 3 | TABLE ACCESS FULL | ANTI_TEST1 | 65273 | 292 (1)| 00:00:04 | ------------------------------------------------------------------------------ /* 在11gR2的版本中,对于上述表间关联,优化器采用了哈希连接的处理方式。这是一个在11g版本中新增的特性,称为NULL AWARE,可以支持空字段的反连接操作使用哈希连接处理。在老的版本中,不支持这样处理,因此只能使用较原始的嵌套循环方式处理。这个在后面的表连接的章节中会详细讲解。 */
3)测试10g的情况。
select /*+ optimizer_features_enable('10.2.0.5') */ * from anti_test1 a wherea.object_id not in (selectb.object_id from anti_test2 b); ------------------------------------------------------------------------- | Id | Operation | Name | Rows | Cost (%CPU)| Time | ------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 65241 | 4372 (1)| 00:00:53 | |* 1 | FILTER | | | | | | 2 | TABLE ACCESS FULL| ANTI_TEST1 | 65273 | 292 (1)| 00:00:04 | |* 3 | TABLE ACCESS FULL| ANTI_TEST2 | 97664 | 2 (0)| 00:00:01 | ------------------------------------------------------------------------- /* 与上面的执行计划不同,这里是因为通过提示的方式修改了optimizer_features_enable参数,指定优化器使用较老的版本。指定的10.2.0.5版本中,只能使用这种原始的嵌套循环方式处理表间关联。 */
2.1.5 优化器相关Hint
在SQL优化中,除了可以通过修改参数的方式干预优化器工作外,还可以使用提示的方式进行干预,而且这种方式更加精准、不影响其他SQL,故使用场景更加广泛。关于提示——Hint,将在后面的章节中详细介绍。
1.ALL_ROWS
说明:
·ALL_ROWS是针对整个目标SQL的Hint,它的含义是让优化器启用CBO,而且在得到目标SQL的执行计划时会选择那些吞吐量最佳的执行路径。这里的“吞吐量最佳”是指资源消耗量(即对I/O、CPU等硬件资源的消耗量)最小,也就是说在ALL_ROWS Hint生效的情况下,优化器会启用CBO,而且会依据各个执行路径的资源消耗量来计算它们各自的成本。
·ALL_ROWS Hint其实就相当于对目标SQL启用CBO,其优化器为ALL_ROWS。从Oracle 10g开始,ALL_ROWS就是默认的优化器模式。这也意味着自Oracle 10g以来,默认情况下优化器启用的就是CBO,而且会依据各条执行路径的资源消耗量来计算它们各自的成本。
·如果在目标SQL中除了ALL_ROWS之外还使用了其他与执行路径、表连接相关的Hint,则优化器会优先考虑ALL_ROWS。
格式:
/*+ ALL_ROWS */
范例:
select /*+ all_rows */ empno,ename,sal,job from emp where empno=7369;
2.FIRST_ROWS(n)
说明:FIRST_ROWS(n)是针对整个目标SQL的Hint,它的含义是让优化器启用CBO模式,而且在得到目标SQL的执行计划时会选择那些以最快响应并返回头n条记录的执行路径,也就是说在FIRST_ROWS(n)Hint生效的情况下,优化器会启用CBO,而且会依据返回头n条记录的响应时间来决定目标SQL的执行计划。
格式:
/*+ FIRST_ROWS(n) */
范例:
select /*+ first_rows(10) */ empno,ename,sal,job from emp where empno=7369;
优化器模式-FIRST_ROWS_n:FIRST_ROWS(n)Hint和优化器模式FIRST_ROWS_n不是一一对应的。优化器模式FIRST_ROWS_n中只能是1、10、100和1000,但FIRST_ROWS(n)Hint中的n可以是除1、10、100和1000之外的所有值。
alter session set optimizer_mode=first_rows_10;
忽略情况:如果在UPDATE、DELETE或者含如下内容的查询语句中使用了FIRST_ROWS(n)Hint,则该FIRST_ROWS(n)Hint会被Oracle忽略。
·集合运算(如UNION、INTERSECT、MINUS、UNION ALL等)
·GROUP BY
·FOR UPDATE
·聚合函数(比如SUM等)
·DISTINCT
·ORDER BY(对应的排序列上没有索引)
这里优化器会忽略FIRST_ROWS(n)Hint是因为对于上述类型的SQL语言而言,Oracle必须访问所有的行记录后才能返回满足条件的头n行记录,即在上述情形下,使用FIRST_ROWS(n)Hint是没有意义的。
3.RULE
说明:RULE是针对整个SQL的Hint,它表示对目标SQL启用RBO。
格式:
/*+ RULE */
范例:
select /*+ rule */ empno,ename,sal,job from emp where empno=7369;
RULE与其他Hint:RULE通常不能与除DRIVING_SITE以外的Hint联用,当RULE与除DRIVING_SITE以外的Hint联用时,其他的Hint可能会失效。但是,当RULE和DRIVING_SITE联用时,它自身可能会失效,所以RULE Hint最好是单独使用。
最佳实践:不推荐使用RULE Hint。一是因为Oracle早就不支持RBO了,二是因为启用RBO后优化器在执行目标SQL时选择的执行路径将大大减少,很多执行路径RBO根本就不支持(比如哈希连接),这也就意味着启用RBO后目标SQL跑出正确执行计划的概率将大大降低。
忽略情况:因为很多执行路径RBO根本就不支持,所以即使在目标SQL中使用了RULE Hint,如果出现了如下情况(包括但不限于),RULE Hint依然会被Oracle忽略。
·目标SQL除RULE之外还联合使用了其他Hint(比如DRIVING_SITE)。
·目标SQL使用了并行执行。
·目标SQL所涉及的对象有IOT。
·目标SQL所涉及的对象有分区表。
4.测试案例
下面通过一个完整的案例,介绍混合使用各种不同的提示并观察其效果。
准备工作,代码如下:
create table t1 as select * from dba_objects; insert into t1 select * from t1; insert into t1 select * from t1; commit; select count(*) from t1; => 292280 //构造了一张测试表,数据规模接近30万 create index idx_t1 on t1(object_id); //对OBJECT_ID字段创建了索引 update t1 set object_id=1 where rownum<288280; commit; select count(*) from t1 where object_id=1; => 288279 //手动修改了OBJECT_ID的值,将表中绝大多数记录的OBJECT_ID设置为1 exec dbms_stats.gather_table_stats( ownname=>'HF', tabname=>'T1', estimate_percent=>100, method_opt=>'for columns size auto object_id', cascade=>true); //收集表的统计信息,注意此时也收集了相关对象—索引的统计信息 select clustering_factor from dba_indexes where index_name='IDX_T1'; => 4213 /* 查看当前索引的聚簇因子为4213。关于聚簇因子,后面章节有详细说明。这里简单说明一下,聚簇因子反映了索引字段的顺序和表中数据存储的有序关系。聚簇因子越小,说明索引字段顺序与表中数据存储顺序一致性越高;反之,则一致性越低,即越无序 */ exec dbms_stats.set_index_stats( ownname=>'HF', indname=>'IDX_T1', clstfct=>10000, no_invalidate=>false); select clustering_factor from dba_indexes where index_name='IDX_T1'; => 10000 /* 这里手动修改了聚簇因子,将其设置为10000。手动修改统计信息是一种常用的优化手段,可以 便于我们分析问题。后面的统计信息的章节会有详细说明 */
测试SQL-默认情况,具体如下:
select object_name,object_id from t1 where object_id=1; -------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | -------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 287K| 19M| 1170 (1)| 00:00:15 | |* 1 | TABLE ACCESS FULL| T1 | 287K| 19M| 1170 (1)| 00:00:15 | -------------------------------------------------------------------------- /* 在默认情况下,上面的SQL应该是采用的索引扫描。因为上面手工修改了索引的聚簇因子,大大增加了索引扫描的成本。所以,这里选择使用了全表扫描。注意此时是使用了CBO,且优化器模式为默认值—ALL_ROWS */
测试SQL-first_rows(10),具体如下:
select /*+ first_rows(10) */ object_name,object_id from t1 where object_id=1; ------------------------------------------------------------------------------ | Id | Operation | Name | Rows | Cost (%CPU)| Time | ------------------------------------------------------------------------------ | 0 | SELECT STATEMENT | | 12 | 4 (0)| 00:00:01 | | 1 | TABLE ACCESS BY INDEX ROWID| T1 | 12 | 4 (0)| 00:00:01 | |* 2 | INDEX RANGE SCAN | IDX_T1 | | 3 (0)| 00:00:01 | ------------------------------------------------------------------------------ /* 这里使用了一个提示first_rows(10),其作用是优先返回10条记录。在使用提示后,Oracle认为此时扫描索引IDX_T1能够以最短的响应时间返回满足上述SQL的where条件object_id=1的头10条记录,因此这里使用了索引范围扫描 */
测试SQL-first_rows(9),具体如下:
select /*+ first_rows(9) */ object_name,object_id from t1 where object_id=1; ------------------------------------------------------------------------------ | Id | Operation | Name | Rows | Cost (%CPU)| Time | ------------------------------------------------------------------------------ | 0 | SELECT STATEMENT | | 11 | 4 (0)| 00:00:01 | | 1 | TABLE ACCESS BY INDEX ROWID| T1 | 11 | 4 (0)| 00:00:01 | |* 2 | INDEX RANGE SCAN | IDX_T1 | | 3 (0)| 00:00:01 | ------------------------------------------------------------------------------ /* 使用提示first_rows(9),带来的变化就是优化器对基数的估算不同。注意观察执行计划中的Rows部分。从first_rows(10)的12变成了11 */
测试SQL-all_rows,具体如下:
select /*+ all_rows */ object_name,object_id from t1 where object_id=1; -------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | -------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 287K| 19M| 1170 (1)| 00:00:15 | |* 1 | TABLE ACCESS FULL| T1 | 287K| 19M| 1170 (1)| 00:00:15 | -------------------------------------------------------------------------- /* ALL_ROWS Hint其实就相当于对目标SQL启用CBO且优化器模式为ALL_ROWS,而ALL_ROWS本身就是自10g以来优化器模式的默认设置,即在默认情况下单独使用ALL_ROWS Hint和不使用任何Hint的效果是一样的 */
测试SQL-rule,具体如下:
select /*+ rule */ object_name,object_id from t1 where object_id=1; ---------------------------------------------- | Id | Operation | Name | ---------------------------------------------- | 0 | SELECT STATEMENT | | | 1 | TABLE ACCESS BY INDEX ROWID| T1 | |* 2 | INDEX RANGE SCAN | IDX_T1 | ---------------------------------------------- Note ----- - rule based optimizer used (consider using cbo) /* 注意执行计划中的关键字rule based...,并且显示的具体执行步骤中并没有Cost列,这说明RULE起作用了(现在用的是RBO) */
测试SQL-rule + parallel,具体如下:
alter table t1 parallel; select /*+ rule */ object_name,object_id from t1 where object_id=1; ----------------------------------------------------- | Id | Operation | Name |Cost (%CPU)| ----------------------------------------------------- | 0 | SELECT STATEMENT | | 81 (0)| | 1 | PX COORDINATOR | | | | 2 | PX SEND QC (RANDOM)| :TQ10000 | 81 (0)| | 3 | PX BLOCK ITERATOR | | 81 (0)| |* 4 | TABLE ACCESS FULL| T1 | 81 (0)| ----------------------------------------------------- /* 输出中包含了Cost列,这表示上述SQL在解析时使用的是CBO,这也验证了之前的观点:如果目标SQL使用了并行执行,就意味着其中的RULE Hint会失效,此时Oracle会自动启用CBO */