关闭

Oracle的优化器有两种优化方式(一)

标签: 优化oracletableaccessoptimizationdate
3297人阅读 评论(0) 收藏 举报
分类:

Oracle的优化器有两种优化方式(整理), 2010-04-13
RBO
方式:基于规则的优化方式(Rule-Based Optimization,简称为RBO)
  优化器在分析SQL语句时,所遵循的是Oracle内部预定的一些规则。比如我们常见的,当一个where子句中的一列有索引时去走索引。


CBO
方式基于代价的优化方式(Cost-Based Optimization,简称为CBO)
它是看语句的代价(Cost),这里的代价主要指Cpu和内存。优化器在判断是否用这种方式时,主要参照的是表及索引的统计信息。统计信息给出表的大小、有少行、每行的长度等信息。这些统计信息起初在库内是没有的,是做analyze后才出现的,很多时候过期统计信息会令优化器做出一个错误的执行计划,因此应及时更新这些信息。

 

所以hint也不例外,除了/*+rule*/其他的都是CBO优化方式
优化模式包括RuleChooseFirst rowsAll rows四种方式

Rule基于规则的方式。
Choose
默认的情况下Oracle用的便是这种方式。指的是当一个表或索引有统计信息,则走CBO的方式,如果表或索引没统计信息,表又不是特别的小,而且相应的列有索引时,那么就走索引,走RBO的方式。

PS: 优化模式是CHOOSE的情况下,看Cost参数是否有值来决定采用CBO还是RBO
SELECT STATEMENT [CHOOSE] Cost=1234 --Cost
有值,采用CBO
SELECT STATEMENT [CHOOSE] Cost=
    --Cost为空,采用RBO

PS的信息是从另外篇帖子里看到的,COST有值应该是指“有统计信息”的含义,暂时如此理解.
 First Rows
它与Choose方式是类似的,所不同的是当一个表有统计信息时,它将是以最快的方式返回查询的最先的几行,从总体上减少了响应时间。
All Rows:
也就是我们所说的Cost的方式,当一个表有统计信息时,它将以最快的方式返回表的所有的行,从总体上提高查询的吞吐量。没有统计信息则走RBO的方式

 

Oracle如何配置默认的优化规则
AInstance级别我们可以通过在initSID.ora文件中设定OPTIMIZER_MODE=RULE/CHOOSE/FIRST_ROWS/ALL_ROWS如果没设定OPTIMIZER_MODE参数则默认用的是Choose方式。

BSessions级别通过ALTER SESSION SET OPTIMIZER_MODE=RULE/CHOOSE/FIRST_ROWS/ALL_ROWS来设定.

C语句级别用Hint/*+ ... */来设定
为什么表的某个字段明明有索引,但执行计划却不走索引?

   
1、优化模式是all_rows的方式
    2
、表作过analyze,有统计信息

    3
、表很小,Oracle的优化器认为不值得走索引。


优化器提示 :不区分大小写, 多个提示用空格分开
如:
select /*+ hint1(tab1) hint2(TAB1 idx1) */ col1, col2 from tab1 where col1='xxx';
如果表使用了别名, 那么提示里也必须使用别名

如:
select /*+ hint1(t1) */ col1, col2 from tab1 t1 where col1='xxx';
如果使用同一个表的多个用,号分开

如: select /*+ index(t1.A,t1.B) */ col1, col2  from   tab1 t1 where  col1='xxx';

 

 

 

没有使用HINT的执行计划如下:

SELECT  EMPNO,ENAME,SAL FROM EMP WHERE EMPNO=7788;

Execution Plan

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

   0      SELECT STATEMENT Optimizer=CHOOSE

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

   2    1     INDEX (UNIQUE SCAN) OF 'PK_EMP' (UNIQUE)

 

1. /*+ALL_ROWS*/
表明对语句块选择基于开销的优化方法,并获得最佳吞吐量,使资源消耗最小化.

SELECT /*+ALL+_ROWS*/ EMPNO,ENAME,SAL FROM EMP WHERE EMPNO=7788;

Execution Plan

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

   0      SELECT STATEMENT Optimizer=CHOOSE

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

   2    1     INDEX (UNIQUE SCAN) OF 'PK_EMP' (UNIQUE)

2
. /*+FIRST_ROWS*/
表明对语句块选择基于开销的优化方法,并获得最佳响应时间,使资源消耗最小化
.
SELECT /*+FIRST_ROWS*/ EMPNO,ENAME,SAL FROM EMP WHERE EMPNO=7788;

Execution Plan

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

 0       SELECT STATEMENT Optimizer=HINT: FIRST_ROWS (Cost=1 Card=1 Bytes=33)

 1    0  TABLE ACCESS (BY INDEX ROWID) OF 'EMP' (Cost=1 Card=1 Bytes=33)

 2    1  INDEX (RANGE SCAN) OF 'PK_EMP' (UNIQUE) (Cost=1 Card=1)


3. /*+CHOOSE*/
表明如果数据字典中有访问表的统计信息,将基于开销的优化方法,并获得最佳的吞吐量;如果数据字典中没有访问表的统计信息,将基于规则开销的优化方法;
SELECT /*+CHOOSE*/ EMPNO,ENAME,SAL FROM EMP WHERE EMPNO=7788;

Execution Plan

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

   0      SELECT STATEMENT Optimizer=HINT: CHOOSE

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

   2    1     INDEX (UNIQUE SCAN) OF 'PK_EMP' (UNIQUE)

4. /*+RULE*/
表明对语句块选择基于规则的优化方法.
SELECT /*+ RULE */ EMPNO,ENAME,SAL FROM EMP WHERE EMPNO=7788
;
Execution Plan

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

   0      SELECT STATEMENT Optimizer=HINT: RULE

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

   2    1     INDEX (UNIQUE SCAN) OF 'PK_EMP' (UNIQUE)
5. /*+FULL(TABLE)*/
表明对表选择全局扫描的方法.
SELECT /*+FULL(E)*/  EMPNO,ENAME,SAL FROM EMP E WHERE EMPNO=7788;

Execution Plan

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

   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=2 Card=1 Bytes=33)

   1    0   TABLE ACCESS (FULL) OF 'EMP' (Cost=2 Card=1 Bytes=33)


6. /*+ROWID(TABLE)*/
提示明确表明对指定表根据ROWID进行访问.
SELECT /*+ROWID(BSEMPMS)*/ * FROM EMP WHERE ROWID>=' AAAHW7AABAAAMUiAAH'  AND EMPNO=7788;

Execution Plan

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

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

   1    0   TABLE ACCESS (BY INDEX ROWID) OF 'EMP' (Cost=1 Card=1 Byte s=94)

   2    1     INDEX (RANGE SCAN) OF 'PK_EMP' (UNIQUE) (Cost=1 Card=1)


7. /*+CLUSTER(TABLE)*/
(可能没有簇对象,暂且还没有使用和理解簇对象)
提示明确表明对指定表选择簇扫描的访问方法,它只对簇对象有效.
select /*+CLUSTER */ e.empno ,d.dname from emp e,dept d  where e.deptno=d.deptno;

Execution Plan

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

   0      SELECT STATEMENT Optimizer=CHOOSE

   1    0   NESTED LOOPS

   2    1     TABLE ACCESS (FULL) OF 'EMP'

   3    1     TABLE ACCESS (BY INDEX ROWID) OF 'DEPT'

   4    3       INDEX (UNIQUE SCAN) OF 'PK_DEPT' (UNIQUE)

8. /*+INDEX(TABLE INDEX_NAME)*/
表明对表选择索引的扫描方法. 指定使用表的某个索引:

我在EMP表建立HIREDATE索引create index emp_date_idx on emp(hiredate);

SQL> select * from emp e where e.hiredate=to_date('1987-04-19','yyyy-mm-dd') and e.empno=7788;

Execution Plan

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

   0      SELECT STATEMENT Optimizer=CHOOSE

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

   2    1     INDEX (UNIQUE SCAN) OF 'PK_EMP' (UNIQUE)

上面的查询语句同时使用了主键索引和日期索引,但是执行计划里并没有使用我建立日期的索引,因为采用基于效率的CBO模式,用EMPNOUNIQUE索引效率高,下面强制使用日期索引进行查询得到的执行计划如下:

SQL>select  /*+INDEX(emp emp_date_idx)*/  *  from emp e

where e.hiredate=to_date('1987-04-19','yyyy-mm-dd') and e.empno=7788;

 

Execution Plan

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

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

   1    0   TABLE ACCESS (BY INDEX ROWID) OF 'EMP' (Cost=1 Card=1 Bytes=87)

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

9. /*+INDEX_ASC(TABLE INDEX_NAME)*/
表明对表选择索引升序的扫描方法. (相当于ORDER BY 索引列 ASC
SQL> SELECT  /*+INDEX_ASC(emp PK_EMP)*/
 *  FROM  EMP;

Execution Plan

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

   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=826 Card=82 Bytes=7134)

   1    0   TABLE ACCESS (BY INDEX ROWID) OF 'EMP' (Cost=826 Card=82 Bytes=7134)

   2    1     INDEX (FULL SCAN) OF 'PK_EMP' (UNIQUE) (Cost=26 Card=82)

对比下执行计划  

SQL> select * from emp order by empno;(基于RBO的模式,理解有误?

Execution Plan

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

   0      SELECT STATEMENT Optimizer=CHOOSE

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

   2    1     INDEX (FULL SCAN) OF 'PK_EMP' (UNIQUE)

10. /*+INDEX_COMBINE*/
为指定表选择位图访问路经,如果INDEX_COMBINE中没有提供作为参数的索引,将选择出位图索引的布尔组合方式. (位图索引搁置….)


11. /*+INDEX_JOIN(TABLE INDEX_NAME)*/
提示明确命令优化器使用索引作为访问路径.
SQL>
SELECT /*+INDEX_JOIN(EMP EMP_DATE_IDX)*/  SAL,HIREDATE FROM EMP E

where e.hiredate>to_date('1980-04-19','yyyy-mm-dd');

Execution Plan

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

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

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

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

12. /*+INDEX_DESC(TABLE INDEX_NAME)*/
表明对表选择索引降序的扫描方法. (9)

13. /*+INDEX_FFS(TABLE INDEX_NAME)*/
对指定的表执行快速全索引扫描,而不是全表扫描的办法.
select /*+index_ffs(emp pk_emp)*/ * from emp where empno=7788;

14. /*+ADD_EQUAL TABLE INDEX_NAM1,INDEX_NAM2,...*/
提示明确进行执行规划的选择,将几个单列索引的扫描合起来
.
select /*+index_ffs(emp pk_emp emp_date_idx)*/ * from emp e where empno=7788 and e.hiredate=to_date('1987-04-19','yyyy-mm-dd');

15. /*+USE_CONCAT*/
对查询中的WHERE后面的OR条件进行转换为UNION  ALL的组合查询. (懵懂啊,先存着)

例如:
select /*+use_concat */ * from emp where deptno=10 OR empno=7788;

Execution Plan

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

   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=2 Card=2 Bytes=174)

   1    0   TABLE ACCESS (FULL) OF 'EMP' (Cost=2 Card=2 Bytes=174)

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:532819次
    • 积分:5756
    • 等级:
    • 排名:第4656名
    • 原创:115篇
    • 转载:51篇
    • 译文:1篇
    • 评论:109条
    最新评论