Oracle optimizer & 表链接类型

优化器的模式用于决定在oracle中解析目标SQL时所使用的优化器的类型,以及决定当使用CBO时计算成本。sql

OPTIMIZER_MODE参数 决定优化器类型,选项有 RULE,CHOOSE,first_wors_n,first_rows 和 all_rows。session

rule : 表示oracle使用RBO模式来解析目标sql,oracle

choose:oracle 9i中optimizer_mode的默认值,他表示oracle在解析目标sql的时候有系统本身决定使用什么模式的优化器,取决于目标对象是否用统计信息。具体来讲,若是有目标表的统计信息则使用CBO,反之使用RBO。oop

first_rows_n :(n=1,10,100,1000),此时使用的是CBO优化器,且此时的CBO 在计算该SQL的各条执行计划的时候着重返回前n条记录,这就意味着采用的执行计划不在是基于成本最小而是基于返回相应速度最快的执行计划。优化

first_rows:oracle 9i的参数,表示在接续目标语句时联合使用CBO,RBO。debug

ALL_rows:oracle 10g级之后版本优化器默认的参数,他表示使用CBO来解析目标SQL,此时CBO在计算执行路径时 侧重点在于最佳的i/o和cpu消耗。对象

 

实际上,成本的计算方式随着优化器模式不一样而不一样,主要体如今ALL_ROWS和first_rows_n对成本的计算方式上。当优化器为all_rows时,CBO计算成本侧重点在于最佳吞吐上;当优化器为first_rows_n时,CBO计算成本侧重于最快相应速度。blog

 

结果集(Row Source)是指包含指定执行计划结果的集合。对于优化器而言,结果集和目标sql的执行步骤对应。对于目标SQL执行计划而言,其中某个执行步骤的输出结果就是该执行步骤所对应的输出结果集,同时该执行不走所对应的输出结果集可能就是下一个执行步骤的输入结果集。这样一步一步执行下来,伴随的就是结果集在各个执行步骤之间的传递,等目标SQL执行计划的各个步骤所有执行完毕,最后的输出结果集就是该SQL最终的执行结果。排序

对于CBO而言,对应执行计划中的列(Rows)反映的就是CBO当前步骤输出的结果集记录数(Cardinality)的估计值。索引

访问数据的路径(Accent Path)对于优化器而言,它在解析目标SQL,获得执行计划相当重要的一点就是决定访问表数据的方法。目标SQL所须要访问数据通常采用2种方式:直接访问和索引访问。

访问表的方式对应为:全表扫描和rowid扫描。

全表扫描是指从表第一个区开始扫描一直到该表的高水位线为止,随着表数据越多,扫描所消耗的成本也就越高。若是对目标表不停的插入数据,当分配给该表的现有空间不足时高水位线就会向上移动(delete并不会下降高水位线),这就意味着即便表中的数据删除了一部分,可是在进行全表扫描的时候仍是会扫描高水位线如下的数据块,即便大部分数据块时空的。

不是说 全表扫描很差,事实上oracle在作全表扫描操做时会使用多块读,这在目标表的数据量不大执行效率是很是高的。

Rowid是记录所在行的物理存储地址,那么当经过rowid扫描数据时就经过访问rowid直接访问对应记录。

咱们能够经过dbms_rowid.rowid_relative_fno,dbms_rowid.rowid_block_number和dbms_rowid.rowid_row_number将rowid转换成块的物理地址。

 

SQL > select empno,ename,rowid,dbms_rowid.rowid_relative_fno(rowid)||'-'||dbms_rowid.rowid_block_number(rowid)||'-'||dbms

_rowid.rowid_row_number(rowid) loca from emp

 

EMPNO ENAME ROWID LOCA

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

7900 SMITH AAATaLAAIAAAALTAAA 8-723-0

7901 ALLEN AAATaLAAIAAAALTAAB 8-723-1

7902 WARD AAATaLAAIAAAALTAAC 8-723-2

能够看到,7900记录存放在“8-723-0” 表示8号文件的723数据块的第0行记录。

基于索引(B-tree)访问数据有以下优点:

(1)全部的索引叶子块都在同一层,记他们距离索引根几点的深度是相同的。这也意味着访问索引叶子块的任何一个索引健值所花费的时间几乎是相同的。

(2)Oracle会保证全部的B树索引都是自平衡的,既不可能出现不一样的索引叶子块不处于同一层现象。

(3)经过B树索引访问表行记录的效率并不会随着表数据量增大而降低,即经过索引访问数据块是可控的,这是走索引扫描跟全表扫描最大的区别。b-tree索引结构就决定了oracle先访问b-tree索引,而后获得rowid,根据这个rowid在返回表访问对应的数据行记录。

常见索引扫描方式:

索引惟一扫描(index unique scan):针对惟一索引(unique index)的扫描,它仅仅适用于where条件里等值查询的SQL。由于扫描的对象是惟一性索引,因此索引惟一性扫描的结果最多只有一条。

create unique index empno_idx on emp(empno);

select * from emp where empno=1901;

索引范围扫描(index range scan):索引范围扫描的结果可能会返回多条记录。当目标索引的行数大于1时,索引就会走范围扫描。

create index empno_idx on emp(empno);

select * from emp where empno<1901;

索引全扫描(index full scan):所谓的“索引全扫描”,就是指要扫描目标索引全部叶子块的全部索引行。索引是有序的,因此索引全扫描返回的数据也是有序的。

select empno from emp;

索引全扫描的扫描结果是有序的这就决定了索引全扫描是不可以并行执行的,而且一般状况下索引全扫描使用的是单块读。

索引快速扫描(index fast full scan)和索引全扫描同样,索引快速扫描也须要扫描目标索引全部叶子块的全部全部行。

索引快速扫描跟索引全扫描区别以下:

(1)索引快速全扫描只用于CBO

(2)索引快速全扫描可使用多快读,也能够并行执行。

(3)索引快速全扫描的执行结果不必定是有序的。

索引跳跃扫描(index skip scan):它使那些在where条件中没有对目标索引的前导列指定查询条件可是又对非前导列制定了查询条件。

create index idx_employee on employee(gender,employee_id);

select * from employees where employee_id = 100;

表链接:

表链接的顺序(两两链接):驱动表(outer table)和被驱动表(inner table)

 

表链接类型:

内链接(Inner join)left join,rightjoin,full outer join,(+);

外连接(Outer join)left outer join,right outer join,full outer join

select * from t1 left outer join t2 on t1.col2 = t2.col2 等价于 select * from t1,t2 where t1.col2=t2.col2(+);

 

表链接方法:

排序合并链接(Sort Merge Join) 两个表在作表链接是用排序操做和合并操做。

首先以where谓词为条件去过滤表T1,而后对过滤的结果进行排序,结果集记为s1;

而后以where谓词为条件去过滤表T2,对过滤后的结果进行排序,结果集记为s2;

最后对结果集s1 和结果集s2 执行合并操做,从中取出匹配记录做为最终结果。

一般状况下,排序合并链接的执行效率远不如哈希链接,但前者使用范围更广,由于哈希链接一般只能用于等值链接条件,而排序合并链接还能用于其余链接条件。

 

嵌套循环链接(Nested Loops Join)是两个表在作链接时依靠两层嵌套循环来打得屁链接结果集的表链接方法。

一般状况下系统会按照必定规则来判断谁是驱动表谁是被驱动表,驱动表用于外层循环,被驱动表用于内层循环。接着以where谓词为条件去访问驱动表T1,获得结果集为s1;

而后遍历驱动表结果集s1并同时遍历被驱动表T2,拿s1第一条记录依次去匹配被驱动表T2中的全部记录,以此类推。经过谓词条件去遍历驱动表T1获得的结果集s1,这是第一个循环,经过对结果集s1

的记录依次匹配被驱动表T2这是第二个循环。很显然,外层循环结果集有多少条记录,遍历被驱动表T2的内层循环就须要多少次,这就是所谓的“嵌套循环”的含义。

嵌套循环适用场景:

(1)若是驱动表被过滤后的结果集s1记录少,同时被驱动表的链接咧上存在惟一索引(或者在被驱动表的链接咧上存在选择性很好的非惟一索引),那么此时使用嵌套循环执行效率是最高的。必定要确保结果集s1记录少,即便被驱动表有惟一性索引,那么效率也不会很高。

(2)只要驱动结果集记录少,那么就具有了嵌套循环的前提条件。

(3)嵌套循环链接有其余链接所不具有的一个优势:嵌套循环链接能够实现快速响应,即它能够第一时间先返回已经链接且知足条件的记录数,而没必要等到全部的操做所有完成才返回结果。

若是目标SQL的禅熏列并不能所有从被驱动表的相关索引中得到,那么oracle在作完嵌套循环链接后还须要对被驱动表执行回表操做。

 

哈希链接(Hash Join)

两个表依靠哈希运算获得结果集的链接方法,至于hash join值使用在CBO中。

在10g及之后的版本,哈希链接受限于_hash_join_enabled参数,默认值为true,固然也可使用hit(use_hash)来提示使用哈希链接。

表T1和表T2在通过谓词where过滤后获得的结果集最少的那个将被当作驱动表,,,,,(过程太繁杂,不写了)。

适用场景:

(1)只适用于CBO模式,只适用于等值链接条件

(2)经过哈希获得的结果集 大多状况下是不排序的

(3)哈希链接的驱动表所对应的链接咧的可选择性尽量好

(4)哈希很适用于大表跟小表作链接且链接结果集的记录较多的状况下效果更好。

(5)被选做驱动表的结果集若是比较小彻底能够在内存中作运算的话,此时的执行效率很是高。

能够经过00104事件来观察目标SQL在作哈希链接时的大体过程和一些统计信息:

oradebug setmypid

oradebug event 10104 trace name context forever,level 1

set autotrace traceonly

select * from plat2.emp where empno=1900;

oradebug tracefile_name

 

笛卡尔链接(Cross Join)又称为笛卡尔乘积(Cartesian Product),它是一种表之间没有任何链接条件时的链接方式。

笛卡尔链接其实是一种特殊的“合并链接”,相似于排序合并链接(Sort Merge Join),只是不须要排序,且在执行合并操做室没有链接条件而已。

最后结果 9,正好是3*3。

通常出现笛卡尔链接大多状况是表之间没有链接条件或者表统计信息不许确致使,在工做中应该尽可能避免笛卡尔链接出现。

 

反链接(Anti Join)

当作子查询展开时,Oracle常常会吧那些外部where条件为 not exists,not in或 <> all的子查询转换成对应的反链接。

select * from t1 where col2 not in (select col2 from t2);

半链接(Semi Join)Oracle常常会把那些外部where条件为exists,in或 = any的子查询转换成半链接。

星型链接(Star Join)

一般使用在数据仓库中,它是由一个事实表(Fact Table)和多个维度表(Dimension Table)之间的链接。

 

index join

针对单表上的不一样索引之间的链接。基于索引合并的状况咱们通常都使用复合索引来解决效率的问题。

VIEW

在处理包含视图的sql时,根据视图是否能合并,分为以下2种状况:

视图合并和视图不能合并

FILTER

过滤器,是一种改良的嵌套循环链接

 

SORT

排序,主要包括:

sort aggregate

sort unique

sort join

sort group by

sort order by

buffer sort

 

使用执行计划或者10032跟踪SQL排序状况

alter session set events '10032 trace name contents forever'

文件名就是<SID>_ora_<PID>.trc

 

CONCAT

其实即便in-list扩展(in-list expansion)或 or 扩展(or expansion)

select /*+use_concat*/ * from empno where empno in (1900,7900,7800);

 

union all 仅仅是简单地将两个结果集合并;union 将两个结果集合并而后对结果集排序去重操做。

 

connect by层次查询(Hierarchical Queries)

select empno,ename,mgr from emp start with empno = 7889 connect by prior empno=mgr;

相关文章
相关标签/搜索