oracle 执行计划(连接类型)

本文来自http://li.angshan.blog.163.com/blog/static/13133228920115284317868/

1. Oracle是通过CBO(基于成本的优化器)提供sql的执行计划的, 通常情况, 在统计数据完整的情况下, CBO都能提供最优化的执行计划(优化器会根据事先创建几个执行计划,然后比较选择一个成本最低的执行计划。),所以Oracle DBA 在做sql优化时基本就是对统计数据的管理与日常维护。
这就要求DBA们在日常的运维管理中对统计数据的监控和管理更加用心,这样才能为突如其来的sql语句提供最优的执行计划提供保障。
2.SQL语句的执行,有时要受到语句编写不当造成的性能降低,这涉及到业务提取的干扰,如访问了不需要的表,或者编写的格式不正确(表的访问顺序、索引的使用等)

SQL语句编写结构对性能的影响:
嵌套循环连接 Nested Loop:

一. NESTED LOOP:

 原理:扫描一个表,每读取驱动表的一条记录,就根据索引去另一个表里面查找,所有匹配记录放在结果集中,然后再读取驱动表的下一行。没有索引一般就不会是 nested loops 。

    条件:驱动表结果集不大,被驱动表连接字段要有索引。

    特点:使用嵌套循环连接是从连接结果中提取第一批记录的最快速方法。

    使用: USE_NL(t1 t2) 提示来强制执行 Nested Loops 

 

当一个小的数据集合被连接并且连接条件已等号的方式去连接另一个表时,嵌套循环连接很有用。在嵌套循环中,内表被外表驱动,外表返回的每一行都要在内表中检索找到与它匹配的行,因此整个查询返回的结果集不能太大(大于万不适合),要把返回子集较小表的作为外表(CBO 默认外表是驱动表),而且在内表的连接字段上一定要有索引。当然也可以用ORDERED 提示来改变CBO默认的驱动表,使用USE_NL(table_name1 table_name2)可是强制CBO 执行嵌套循环连接

 一点非常重要:内部表是被外部表驱动访问的, 也就说,循环从外部表开始,然后根据外部表的每个值在去全读一次内部表。如果不是这样, 嵌套循环连接会有相当大的性能损耗,这种情况,哈希连接会更好       

Nested loop一般用在连接的表中有索引,并且索引选择性较好的时候.

 

步骤:确定一个驱动表(outer table),另一个表为inner table,驱动表中的每一行与inner表中的相应记录JOIN。类似一个嵌套的循环。适用于驱动表的记录集比较小(<10000)而且inner表需要有有效的访问方法(Index。需要注意的是:JOIN的顺序很重要,驱动表的记录集一定要小,返回结果集的响应时间是最快的。

cost = outer access cost + (inner access cost * outer cardinality)

 

|   2 |   NESTED LOOPS                |              |     3 |   141 |     7  (15)| 
|* 3 | TABLE ACCESS FULL | EMPLOYEES | 3 | 60 | 4 (25)|
| 4 | TABLE ACCESS BY INDEX ROWID| JOBS | 19 | 513 | 2 (50)|
|* 5 | INDEX UNIQUE SCAN | JOB_ID_PK | 1 | | |

EMPLOYEESouter table, JOBSinner table.

在有索引的情况,优化器将使用嵌套循环连接,结果集较小的表做为驱动表,并且根据表的连接顺序无关。

注意: 在Oracle RBO 时代, 由于解析器是从sql语句的后面向前执行的, 所以写在from子句最后的表会先被执行,如果加上过滤条件就会影响到性能上的很大的差距。CBO则不会出现这个情况,它始终将结果集最小的表当做驱动表。

二. HASH JOIN :

 原理:优化器先扫描小表,根据连接键在内存中建立 hash 表,然后扫描大表,每得到一条记录就探测 hash 表一次,找出匹配行。

    条件:两个巨大表之间的连接,或一个巨大的表一个小表之间的连接。且连接键无索引。

    特点:需要较大的内存,如表太大则需要进行分区,并暂时存储至磁盘的临时段。扫描成本 = 全表扫描大表 + 分区数 * 表全表扫描小表;还需要注意的是:必须将 HASH_JOIN_ENABLED 设为 True, 并且为参数 PGA_AGGREGATE_TARGET 设置了一个足够大的值后,才可以执行 Hash Join 。

    使用: USE_HASH(t1 t2) 提示来强制执行 Hash Join

 

哈希连接用于连接大的数据集合。优化器在两个表中或多个数据源中选择一个数据量最小的集合在内存中构造一个含有连接健的结构,然后扫描大表,寻找哈希表对应的行。

这种方式适用于较小的表完全可以放于内存中的情况,这样总成本就是访问两个表的成本之和。但是在表很大的情况下并不能完全放入内存,这时优化器会将它分割成若干不同的分区,不能放入内存的部分就把该分区写入磁盘的临时段,此时要有较大的临时段从而尽量提高I/O 的性能。

也可以用USE_HASH(table_name1 table_name2)提示来强制使用散列连接。如果使用散列连接HASH_AREA_SIZE初始化参数必须足够的大,如果是9iOracle建议使用SQL工作区自动管理,设置WORKAREA_SIZE_POLICY AUTO,然后调整PGA_AGGREGATE_TARGET 即可。

        

Hash join在两个表的数据量差别很大的时候将被使用.

 

步骤:将两个表中较小的表根据连接键值构造一个内存HASH表,扫描另一个表,同样对JOIN KEY进行HASH后探测是否可以JOIN适用于记录集比较大的情况。需要注意的是:如果HASH表太大,无法一次构造在内存中,则分成若干个partition,写入磁盘的temporary segment,则会多一个写的代价,会降低效率。

cost = (outer access cost * # of hash partitions) + inner access cost

 

--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 665 | 13300 | 8 (25)|
| 1 | HASH JOIN | | 665 | 13300 | 8 (25)|
| 2 | TABLE ACCESS FULL | ORDERS | 105 | 840 | 4 (25)|
| 3 | TABLE ACCESS FULL | ORDER_ITEMS | 665 | 7980 | 4 (25)|
--------------------------------------------------------------------------

ORDERSHASH TABLEORDER_ITEMS扫描

在没有 索引的情况,优化器将使用哈希连接,结果集较小的表(ta) 做为内存表,并且根据表的连接顺序无关。 

三、 排序合并连接( Sort Merge Join )

    原理:将两个表分别进行排序,然后将两个表合并,查找出匹配的记录。

    条件:行源已经被排过序的情况下使用。

    特点:主要花费在两个表的全表扫描和各自的排序上。

    使用: USE_MERGE(t1 t2) 提示来强制执行 Sort Merge Join 。

 总结 :当缺少有用的索引时,哈希连接比嵌套循环连接更加有效。哈希连接可能比排序合并连接更快,因为在这种情况下只有一张源表需要排序。哈希连接也可能比嵌套循环连接更快,因为处理内存中的哈希表比检索 B_Tree 索引更加迅速。

 

 

原文地址:https://www.cnblogs.com/wujin/p/2546686.html