oracle优化器介绍

SQL> select /*+index_desc(t ind_t)*/colx,coly from t where coly<3;

     COLX                COLY

--------------- ---------------

       1                      2

       1                      1

       1                      0

Execution Plan

----------------------------------------------------------

0 SELECT STATEMENT Optimizer=CHOOSE (Cost=1 Card=4 Bytes=104)

1 0 TABLE ACCESS (BY INDEX ROWID) OF 'T' (Cost=1 Card=4 Bytes=104)

2 1 INDEX (RANGE SCAN DESCENDING) OF 'IND_T' (NON-UNIQUE) (Cost=2 Card=1)

4)跳跃式索引扫描

跳跃式索引扫描是用来提高复合索引效率的,通常当复合索引的第一个索引字段不在语句中指定时是无法使用复合索引的,此时如果复合索引的第一个索引字段DISTINCT值非常小,而复合索引的其他索引字段DISTINCT值非常大时,可以使用跳跃式索引扫描来跳过该复合索引的第一个索引字段。跳跃式扫描会使复合索引在逻辑上分裂成N个较小的索引,N值等于复合索引的第一个索引字段的DISTINCT值。

例如:

SQL> select* from employees;

  SEX         EMPLOYEE_ID           ADDRESS

------ -------------------- --------------------

   F               98                         ABC

   F               100                       ABC

   F               102                       ABC

   F               104                       ABC

   M              101                       ABC

   M              103                       ABC

   M              105                       ABC

SQL> create index ind_sex_empid on employees(sex,employee_id);

SQL>set autotrace traceonly

SQL>select/*+index_ss(employees ind_sex_empid)*/* from employees where employee_id=101;

Execution Plan

----------------------------------------------------------

0 SELECT STATEMENT Optimizer=CHOOSE (Cost=3 Card=1 Bytes=11)

1 0 TABLE ACCESS (BY INDEX ROWID) OF 'EMPLOYEES' (Cost=3 Card=1 Bytes=11)

2 1 INDEX (SKIP SCAN) OF 'IND_SEX_EMPID' (NON-UNIQUE) (Cost=2 Card=1)

索引IND_SEX_EMPID结构如下:

image

5)全索引扫描

当查询涉及的字段都包含在索引中,如果WHERE子句中谓词非第一个索引字段,或无WHERE子句但是被索引字段中至少有一个非空属性时,通常会做全索引扫描。全索引扫描结果集按索引字段排序。

6)快速全索引扫描

当查询涉及的字段都包含在索引中,且被索引字段中至少有一个非空属性时,可以使用INDEX_FFS(table_name index_name)来使语句做快速全索引扫描。快速全索引扫描不同于全索引扫描,它使用多块读取的方式来读全部索引块,而且可以使用并行读取。快速全索引扫描的结果集不会排序。位图索引不能使用快速全索引扫描。

7)索引连接

索引连接是几个索引的散列连接。如果查询的字段上都存在索引,可以使用索引连接来避免访问表。

例如:

SQL> select* from t;

        COL1               COL2              COL3              COL4

--------------- --------------- --------------- ---------------

         1                     2                    3                   4

         1                     1                    3                   4

         1                     1                    5                   4

SQL> create index ind_col1 on t(col1);

索引已创建。

SQL> create index ind_col2 on t(col2);

索引已创建。

SQL> create index ind_col3 on t(col3);

索引已创建。

不使用索引连接:

SQL> set autotrace traceonly

SQL> select col1,col2,col3 from t where col2>0;

Execution Plan

----------------------------------------------------------

0 SELECT STATEMENT Optimizer=CHOOSE

1 0 TABLE ACCESS (BY INDEX ROWID) OF 'T'

2 1 INDEX (RANGE SCAN) OF 'IND_COL2' (NON-UNIQUE)

使用索引连接:

SQL> select /*+index_join(t ind_col1 ind_col2 ind_col3)*/col1,col2,col3 from t where col2>0;

Execution Plan

----------------------------------------------------------

0 SELECT STATEMENT Optimizer=CHOOSE (Cost=70 Card=4 Bytes=156)

1 0 VIEW OF 'index$_join$_001' (Cost=70 Card=4 Bytes=156)

2 1 HASH JOIN

3 2 HASH JOIN

4 3 INDEX (RANGE SCAN) OF 'IND_COL2' (NON-UNIQUE) (Cost=3 Card=4 Bytes=156)

5 3 INDEX (FAST FULL SCAN) OF 'IND_COL1' (NON-UNIQUE) (Cost=3 Card=4 Bytes=156)

6 2 INDEX (FAST FULL SCAN) OF 'IND_COL3' (NON-UNIQUE) (Cost=3 Card=4 Bytes=156)

簇扫描

在被索引的簇中,有着相同簇键值的行存储在同一数据块中。执行簇扫描时,首先通过扫描簇索引获得被检索行的Rowid,然后使用Rowid来定位具体的行。

散列扫描

散列扫描就是在一个散列簇中定位数据行。在一个散列簇中,具有相同散列值的行存储在相同的数据块中。在执行散列扫描时,首先通过一个散列函数来获得散列值,然后用散列值在数据块中定位具体行。

表取样扫描

当FROM子句后带有SAMPLE或SAMPLE BLOCK时,会执行表取样扫描来随机检索表中的数据。如:select* from t sample block (1);

处理连接

影响一个连接语句执行计划的四个重要因素是:访问路径、连接方式、连接顺序和成本评估。

优化器如何处理连接顺序

优化器首先确定连接的表中是否包含其结果只有一行记录的表,如果存在这样的表,优化器在对连接表排序时会把这样的表放在最前端。如果是个外连接,含有(+)操作符的表一定排在不含(+)的表的后面。同理被转换成ANTI-JOIN或SEMI-JOIN的子查询,子查询的表一定排在外部表的后面,但是HASH-ANTI-JOIN和HASH-SEMI-JOIN在一定情况下可以违反此顺序。通常可以用ORDERED来指定连接顺序,但是ORDERED所指定的顺序如果违反了外连接的顺序,则ORDERED将被忽略。

优化器如何评估成本

嵌套循环连接,在于外表返回的每一行都要在内表中进行匹配的成本,成本计算如下:

cost of nested loop = access cost of outer + (number of rows from outer * access cost of inner)

排序合并连接,在于把两个大表读入内存并进行排序的成本,成本计算如下:

cost of merge join = access cost of A + access cost of B + (sort cost of A + sort cost of B)

散列连接,在于将小表读入内存分成若干散列表,然后由大表对每个散列表都进行一次匹配的成本,成本计算如下:

cost of hash join = access cost of smaller + (access cost of bigger * number of hash partitions of smaller)

以上成本计算公式不是绝对的,优化器对成本的评估还会受到其他因素的影响,比如:内存排序区过小会增加排序合并连接的成本,由于此种情况下的排序消耗了过多的CPU和I/O。多块读取会降低排序合并连接的成本,如果内表的连接字段存在索引也会降低嵌套循环连接的成本。

优化器可用的连接方式

1) 嵌套循环连接:适用于外表有效基数较小,内表连接字段含有索引,且查询整体返回结果集不太大(小于1万行)的情况下。HINTS:use_nl

2) 散列连接:适用于查询整体返回大量结果集,且有较小的连接表可以放入内存作为散列表的情况下。适用散列连接要注意HASH_AREA_SIZE要足够大,可以容下散列表。如果散列表无法完全放入内存,要设置较大的临时段,从而尽量提高I/O性能。HINTS:use_hash

3) 排序合并连接:适用于查询整体返回大量结果集,两个大表做连接,且表已经排过序的情况下。当两个表已经排过序时,使用排序合并连接的性能可能会优于散列连接。HASH_AREA_SIZE和SORT_AREA_SIZE设置过小,可能会导致优化器避开散列连接而选择排序合并连接。HINTS:use_merge

4) 迪卡尔积连接:当两个表没有任何连接条件时会使用此连接方式。

一些影响优化器的初始化参数

OPTIMIZER_FEATURES_ENABLE:每个版本的Oracle优化器特性都不相同,特别是做了版本升级以后一定要修改这个参数才可以使用仅被该版本支持的优化器特性。可以赋予它的值如:9.2.0、9.0.2、9.0.1、8.1.7、8.1.6等。

CURSOR_SHARING:这个参数会将SQL语句中的直接量用变量来替换,存在大批直接量的OLTP系统可以考虑启用这个参数。但是要注意,绑定变量虽然可以使大量的SQL重用,减少分析时间,但是执行计划可能会不理想。通常OLTP系统适用于绑定变量,OLTP系统特点是,SQL运行频繁且时间相对较短,SQL的分析时间比重较大。如果在DSS系统中,SQL运行时间长,相比之下分析时间微不足道,好的执行计划才是最重要的,因此DSS系统不建议使用这个参数。

HASH_AREA_SIZE:这是散列表的存放区域,如果使用散列连接这个参数值不能太小,否则对散列连接性能影响很大。如果是9i建议启动工作区自动管理,然后设置PGA_AGGREGATE_TARGET。

SORT_AREA_SIZE:内存排序区的大小,如果排序时内存区不够会写入磁盘。9i同样建议启动工作区自动管理,然后设置PGA_AGGREGATE_TARGET。

HASH_JOIN_ENABLED:只有启用这个参数,CBO在考虑连接方式的时候才会考虑散列连接。

OPTIMIZER_INDEX_CACHING:这个参数表示被缓存的索引块所占的百分比,可选值的范围是0-100。这个值会影响嵌套循环连接,如果这个值设得较高,CBO将更倾向使用嵌套循环。

OPTIMIZER_INDEX_COST_ADJ:优化器利用这个参数(是个百分比)把索引扫描的成本转换为等价的全表扫描的成本,然后与全表扫描的成本进行比较。缺省值100,表示索引扫描成本与全表扫描成本等价。可选值范围是0-10000。

OPTIMIZER_MAX_PERMUTATIONS:这个初始参数用来设定优化器最多考虑多少种连接顺序,优化器不断的产生可能的表的连接的排列,直到排列数达到参数optimizer_max_permutations为止。一旦优化器停止产生新的排列,它将会从中选择出成本最小的排列。

DB_FILE_MULTIBLOCK_READ_COUNT:这个参数表示在全表扫描或索引快速全扫描时一次I/O读的连续数据块数量(block#连续,且一次I/O不能超过extent)。

OPTIMIZER_MODE:优化器模式。值为:RULE、CHOOSE、ALL_ROWS、FIRST_ROWS_n、FIRST_ROWS。

PARTITION_VIEW_ENABLED:如果设置为TRUE, 该优化器将跳过分区视图中未被请求的分区,该参数还能更改基于成本的优化程序从基础表统计信息计算分区视图统计信息的方式。

QUERY_REWRITE_ENABLE:如果设置为TRUE,优化器将利用可用的物化视图来重写SQL。


你可能感兴趣的:(oracle优化器介绍)