一、和优化器相关的hint
oracle允许在系统级别、会话级别、SQL语句级别设定优化器的类型:
A 系统级别:alter system set optimizer_mode=all_rows
B 会话级别:alter session set optimizer_mode=all_rows
C SQL语句级别:HINT
all_rows和first_rows默认是用CBO优化器,显示的告诉优化器当前使用那种模式来作SQL语句的解析。
案例:
全局参数:
SQL> show parameter opt
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
optimizer_mode string ALL_ROWS
但是个别语句用first_rows。
SQL> select /*+ first_rows(20) */ * from t a where a.id>20; --用返回结果的前20行的执行计划来运行查询sql的需求
Execution Plan
----------------------------------------------------------
Plan hash value: 3772518221
------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 20 | 360 | 2 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| T | 20 | 360 | 2 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | T_PK | 80 | | 1 (0)| 00:00:01 |
------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("A"."ID">20)
用返回结果的前20行的执行计划来运行查询sql的需求,这种情况反应的执行计划是不真实的。除非查询的结果是小于20行的。
所以在应用中,为了保证查询和计划的匹配,一般都返回的结果是小于等于20行的。如果有需要,再临时用全部的数据生成执行计划的,但是查询全部数据的需求量小。
SQL> select /*+ rule */ * from t a;
Execution Plan
----------------------------------------------------------
Plan hash value: 1601196873
----------------------------------
| Id | Operation | Name |
----------------------------------
| 0 | SELECT STATEMENT | |
| 1 | TABLE ACCESS FULL| T |
----------------------------------
Note
-----
- rule based optimizer used (consider using cbo)
数据迁移完之后,尤其从9i迁移到10g,存在问题:优化器从RBO到CBO,但是因为RBO下很稳定,暂时用RBO,需要所有的SQL都加上这个提示。
数据迁移,10g到10g以上的版本,CBO-CBO,将性能数据用export方法导出到表,将表迁移,再目标库上用import方法导入。
二、和访问路径相关的hint
会直接影响SQL的执行计划,所以在使用的时候要格外小心,特别是代码中。
这种hint对于DBA来说,分析SQL性能的时候用处很大,DBA可以使用不同的SQL使用不同的hint得到不同的执行计划,通过比较不同的执行计划的效率来分析当前SQL语句的性能。比如,是否合适的创建了索引,是否合理的做了性能数据的收集。
1、full hint /*+ full(t) */
2、index hint ――如果所强制走索引可能导致错误的查询结果的时候,hint会被忽略。
3、强制不走索引。
SQL> select * from t a where a.id<20;
Execution Plan
----------------------------------------------------------
Plan hash value: 3772518221
------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 19 | 342 | 2 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| T | 19 | 342 | 2 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | T_PK | 19 | | 1 (0)| 00:00:01 |
------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("A"."ID"<20)
SQL> select /*+ no_index(a t_pk)*/ * from t a where a.id<20; --避开走索引会选择全表扫
Execution Plan
----------------------------------------------------------
Plan hash value: 1601196873
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 19 | 342 | 3 (0)| 00:00:01 |
|* 1 | TABLE ACCESS FULL| T | 19 | 342 | 3 (0)| 00:00:01 |
--------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("A"."ID"<20)
使用场合:
1、假如这个表上所有的SQL需求,大部分是走索引效率高的,个别SQL需要全表扫,此时,针对的个别 的SQL可以用这个提示忽略索引。
2、CBO是自动选择的,随着系统的运行,某此分析完之后,由于数据的变化,性能数据不匹配了,来不及更新性能数据,执行计划就错了,需要临时加一个不用索引而让plan正确。
4、索引降序扫index desc hint
场合:查询的时候一般情况下查询最有可能的是近几天生成的数据,插入数据的时候,表的主键是ID值按照升序排列的,此时查询数据扫索引的时候,从大往小扫,很容易能扫到要的数据。
SQL> select /*+ index_desc(a t_pk)*/ * from t a where a.id<20;
Execution Plan
----------------------------------------------------------
Plan hash value: 725076093
-------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 19 | 342 | 2 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID | T | 19 | 342 | 2 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN DESCENDING| T_PK | 19 | | 1 (0)| 00:00:01 |
-------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("A"."ID"<20)
filter("A"."ID"<20)
SQL> select * from t a where a.id<20;
Execution Plan
----------------------------------------------------------
Plan hash value: 3772518221
------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 19 | 342 | 2 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| T | 19 | 342 | 2 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | T_PK | 19 | | 1 (0)| 00:00:01 |
------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("A"."ID"<20)
5、index_combine HINT
告诉优化器要强制使用位图索引,当访问这个索引会使结果集不正确的时候,会忽略这个hint
SQL> select /*+ index_combine(t t_pk) */ * from t; 等同于 /*+ index(t t_pk)*/
Execution Plan
----------------------------------------------------------
Plan hash value: 1399892806
------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 99 | 1782 | 2 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| T | 99 | 1782 | 2 (0)| 00:00:01 |
| 2 | INDEX FULL SCAN | T_PK | 99 | | 1 (0)| 00:00:01 |
------------------------------------------------------------------------------------
SQL> select /*+ index(t t_pk) */ * from t;
Execution Plan
----------------------------------------------------------
Plan hash value: 1399892806
------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 99 | 1782 | 2 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| T | 99 | 1782 | 2 (0)| 00:00:01 |
| 2 | INDEX FULL SCAN | T_PK | 99 | | 1 (0)| 00:00:01 |
------------------------------------------------------------------------------------
SQL> select * from t;
Execution Plan
----------------------------------------------------------
Plan hash value: 1601196873
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 99 | 1782 | 3 (0)| 00:00:01 |
| 1 | TABLE ACCESS FULL| T | 99 | 1782 | 3 (0)| 00:00:01 |
--------------------------------------------------------------------------
创建位图索引
SQL> alter table t drop constraint t_pk;
Table altered.
SQL> create bitmap index bitind_id on t(id);
Index created.
SQL> exec dbms_stats.gather_table_stats(user,'t',cascade=>true,estimate_percent=>100);
PL/SQL procedure successfully completed.
SQL> select /*+ index_combine(t bitind_id) */ * from t;
Execution Plan
----------------------------------------------------------
Plan hash value: 48670168
------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 99 | 1782 | 22 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID | T | 99 | 1782 | 22 (0)| 00:00:01 |
| 2 | BITMAP CONVERSION TO ROWIDS| | | | | |
| 3 | BITMAP INDEX FULL SCAN | BITIND_ID | | | | |
------------------------------------------------------------------------------------------
SQL> select /*+ index(t bitind_id) */ * from t;
Execution Plan
----------------------------------------------------------
Plan hash value: 48670168
------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 99 | 1782 | 22 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID | T | 99 | 1782 | 22 (0)| 00:00:01 |
| 2 | BITMAP CONVERSION TO ROWIDS| | | | | |
| 3 | BITMAP INDEX FULL SCAN | BITIND_ID | | | | |
------------------------------------------------------------------------------------------
结论:index和index_combine是等价的。
SQL> select count(1) from t where id<15; --作count查询容易用到位图索引。
Execution Plan
----------------------------------------------------------
Plan hash value: 408230478
-------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 3 | 1 (0)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | 3 | | |
| 2 | BITMAP CONVERSION COUNT | | 14 | 42 | 1 (0)| 00:00:01 |
|* 3 | BITMAP INDEX FAST FULL SCAN| BITIND_ID | | | | |
-------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - filter("ID"<15)
6、index_ffs hint
index fast full scan 当访问这个索引会使结果集不正确的时候会忽略这个索引。
SQL> begin
2 for i in 100..100000 loop
3 insert into t values(i,'XXXX');
4 end loop;
5 end;
6 /
PL/SQL procedure successfully completed.
SQL> commit;
Commit complete.
SQL> exec dbms_stats.gather_table_stats(user,'t',cascade=>true,estimate_percent=>100);
PL/SQL procedure successfully completed.
SQL> select id from t where id>99;
Execution Plan
----------------------------------------------------------
Plan hash value: 1176281950
------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 99902 | 487K| 48 (7)| 00:00:01 |
|* 1 | INDEX FAST FULL SCAN| T_IND | 99902 | 487K| 48 (7)| 00:00:01 |
------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("ID">99)
SQL> select id from t where id<=99;
Execution Plan
----------------------------------------------------------
Plan hash value: 422821423
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 99 | 495 | 2 (0)| 00:00:01 |
|* 1 | INDEX RANGE SCAN| T_IND | 99 | 495 | 2 (0)| 00:00:01 |
--------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("ID"<=99)
原理:
FULL INDEX SCAN――查找索引的root块、branch块、leaf块,然后从第一个leaf块开始,沿着双向链表,所读取的索引键值块是有顺序的,排序的。
fast full index scan――按照索引块在磁盘上的存储顺序来扫描的,包括位图块、root块、branch块都可能读到。读取完后索引是不排序的。每次读取16个块。
SQL> show parameter multi
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
db_file_multiblock_read_count integer 16 --多块读方式下一次读取16个块。一般是2的n次方
同等条件下,ffs方式性能要优于full scan方式。
SQL> drop table t1;
Table dropped.
SQL> create table t1 as select * from all_objects where object_id is not null;
Table created.
SQL> create index t1_ind on t1(object_id);
Index created.
SQL> exec dbms_stats.gather_table_stats(user,'t1',cascade=>true,estimate_percent=>100);
PL/SQL procedure successfully completed.
SQL> select count(1) from t1;
Execution Plan
----------------------------------------------------------
Plan hash value: 2264155217
------------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 23 (5)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | | |
| 2 | INDEX FAST FULL SCAN| T1_IND | 40913 | 23 (5)| 00:00:01 |
------------------------------------------------------------------------
SQL> select /*+ no_index_ffs(t1 t1_ind) */ count(1) from t1;
Execution Plan
----------------------------------------------------------
Plan hash value: 3083331628
-------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
-------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 93 (2)| 00:00:02 |
| 1 | SORT AGGREGATE | | 1 | | |
| 2 | INDEX FULL SCAN| T1_IND | 40913 | 93 (2)| 00:00:02 | --说明ffs方式性能高于fs方式
-------------------------------------------------------------------
SQL> drop table t1;
Table dropped.
SQL> create table t1 as select * from all_objects where object_id is not null and rownum<=100;
Table created.
SQL> alter table t1 add constraint t1_pk primary key(object_id);
Table altered.
SQL> exec dbms_stats.gather_table_stats(user,'t1',cascade=>true,estimate_percent=>100);
PL/SQL procedure successfully completed.
SQL> select object_id from t1;
Execution Plan
----------------------------------------------------------
Plan hash value: 1683850221
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 100 | 400 | 1 (0)| 00:00:01 |
| 1 | INDEX FULL SCAN | T1_PK | 100 | 400 | 1 (0)| 00:00:01 |
--------------------------------------------------------------------------
SQL> select count(object_id) from t1;
Execution Plan
----------------------------------------------------------
Plan hash value: 1213398864
------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 1 (0)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | | |
| 2 | INDEX FULL SCAN| T1_PK | 100 | 1 (0)| 00:00:01 |
------------------------------------------------------------------
SQL> select count(1) from t1;
Execution Plan
----------------------------------------------------------
Plan hash value: 1213398864
------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 1 (0)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | | |
| 2 | INDEX FULL SCAN| T1_PK | 100 | 1 (0)| 00:00:01 |
------------------------------------------------------------------
总结:
1、对于数据量巨大的表,用ffs方式更快
2、对于数据量相对较小的表,用fs方式更快。
SQL> select /*+ index_ffs(t1 t1_pk) */ count(1) from t1;
Execution Plan
----------------------------------------------------------
Plan hash value: 1018460547
-----------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
-----------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 2 (0)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | | |
| 2 | INDEX FAST FULL SCAN| T1_PK | 100 | 2 (0)| 00:00:01 |
-----------------------------------------------------------------------
7、index_join hint
索引关联,当谓词中引用的列上都有索引的时候,可以通过索引关联的方式访问数据。
SQL> create table t as select * from dba_objects;
Table created.
SQL> create index t_ind on t(object_id);
Index created.
SQL> create bitmap index t_bm on t(object_name);
Index created.
SQL> exec dbms_stats.gather_table_stats(user,'t',cascade=>true,estimate_percent=>100);
PL/SQL procedure successfully completed.
SQL> select /*+ index_join(t t_ind t_bm) */ * from t where object_id<100 and object_name='DBMS_REGISTER';
no rows selected
8、索引跳跃扫 index_ss hint 关于联合索引的扫描方式
当在一个联合索引中,某些谓词条件不在联合索引第一列的时候,可通过索引跳跃扫的方式获得数据。当联合索引第一列重复率很高的时候,使用索引跳跃扫效率会比较高。
SQL> drop table t;
Table dropped.
SQL> create table t as select 1 id,object_name from dba_objects;
Table created.
SQL> insert into t select 2 id,object_name from dba_objects;
50602 rows created.
SQL> insert into t select 3 id,object_name from dba_objects;
50602 rows created.
SQL> insert into t select 4 id,object_name from dba_objects;
50602 rows created.
SQL> commit;
Commit complete.
SQL> create index ind_t on t(id,object_name);
Index created.
SQL> exec dbms_stats.gather_table_stats(user,'t',cascade=>true,estimate_percent=>100);
PL/SQL procedure successfully completed.
SQL> set autotrace trace exp
SQL> select * from t where object_name='TEST'; --过滤条件落在了第二列上。
Execution Plan
----------------------------------------------------------
Plan hash value: 3688940926
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 7 | 189 | 6 (0)| 00:00:01 |
|* 1 | INDEX SKIP SCAN | IND_T | 7 | 189 | 6 (0)| 00:00:01 |
--------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("OBJECT_NAME"='TEST')
filter("OBJECT_NAME"='TEST')
SQL> select * from t where id=2; --过滤条件放在第一列上用不到索引
Execution Plan
----------------------------------------------------------
Plan hash value: 1601196873
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 50602 | 1334K| 230 (4)| 00:00:03 |
|* 1 | TABLE ACCESS FULL| T | 50602 | 1334K| 230 (4)| 00:00:03 |
--------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("ID"=2)
如果第一列重复率不高,还用这种方法,是否能skip scan,不能的,但是能用到索引范围扫。
如果创建索引的时候,create index ind_t on t(object_name,id);怎样写SQL效率会高? --将过滤条件落在第一列上。
SQL> drop index ind_t;
Index dropped.
SQL> create index ind_t on t(object_name,id); --第一列用索引条件较好
Index created.
SQL> exec dbms_stats.gather_table_stats(user,'t',cascade=>true,estimate_percent=>100);
PL/SQL procedure successfully completed.
SQL> select * from t where object_name='TEST';
Execution Plan
----------------------------------------------------------
Plan hash value: 3131770069
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 7 | 189 | 3 (0)| 00:00:01 |
|* 1 | INDEX RANGE SCAN| IND_T | 7 | 189 | 3 (0)| 00:00:01 |
--------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("OBJECT_NAME"='TEST')
SQL> select /*+ index_ss(t ind_t) */ * from t where object_name='TEST'; --强制索引跳越扫
Execution Plan
----------------------------------------------------------
Plan hash value: 3688940926
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 7 | 189 | 6 (0)| 00:00:01 |
|* 1 | INDEX SKIP SCAN | IND_T | 7 | 189 | 6 (0)| 00:00:01 |
--------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("OBJECT_NAME"='TEST')
总结:
1、当创建联合索引的时候,如果其中的一列重复率低,且谓词条件也在这列上,考虑将这个列作为联合索引的第一列。用索引范围扫。
2、当创建联合索引的时候,谓词条件在某一列上,但是这个列用索引条件比较好,此时想用好索引,可以找另外一列重复率较高的列作为联合索引的第一列,谓词列落在联合索引的第二列上,此时用跳跃扫。
3、如果一列的重复率低,但是谓词不在这一列上,。