Oracle的优化器有两种优化方式(整理), 2010-04-13
RBO方式:基于规则的优化方式(Rule-Based Optimization,简称为RBO)
优化器在分析SQL语句时,所遵循的是Oracle内部预定的一些规则。比如我们常见的,当一个where子句中的一列有索引时去走索引。
CBO方式:基于代价的优化方式(Cost-Based Optimization,简称为CBO)
它是看语句的代价(Cost),这里的代价主要指Cpu和内存。优化器在判断是否用这种方式时,主要参照的是表及索引的统计信息。统计信息给出表的大小、有少行、每行的长度等信息。这些统计信息起初在库内是没有的,是做analyze后才出现的,很多时候过期统计信息会令优化器做出一个错误的执行计划,因此应及时更新这些信息。
所以hint也不例外,除了/*+rule*/其他的都是CBO优化方式
优化模式包括Rule、Choose、First rows、All 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如何配置默认的优化规则
A、Instance级别我们可以通过在initSID.ora文件中设定OPTIMIZER_MODE=RULE/CHOOSE/FIRST_ROWS/ALL_ROWS如果没设定OPTIMIZER_MODE参数则默认用的是Choose方式。
B、Sessions级别通过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模式,用EMPNO的UNIQUE索引效率高,下面强制使用日期索引进行查询得到的执行计划如下:
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)