|
心血来潮,准备写一篇所有11G新的SQL hint的整理性文章。
11G有了一个新的视图v$sql_hint,我们可以通过它找到所有的新的SQL hints。
故名思意,INVERSE列代表这个hint相反操作的hint,VERSION列代表着这个hint正式公布引入的版本。
(注意,VERSION并不代表这个hint从哪个版本开始可以使用。因为以下有些hint,例如INDEX_RS_ASC就在10G就可以用了。所以我认为,VERSION代表着“正式公布引入”的意思。)
(蓝色的是已经是本文介绍的,其他的要么是不常用,要么是我不知道怎么使用的(undocumented))
SQL> select NAME,CLASS,INVERSE,VERSION from v$sql_hint where version like '11%' order by class,name;
NAME CLASS INVERSE VERSION
------------------------------ -------------------------- ------------------------------ --------------------
INDEX_RS_ASC ACCESS 11.1.0.6
INDEX_RS_DESC ACCESS 11.1.0.6
NLJ_BATCHING ACCESS NO_NLJ_BATCHING 11.1.0.6
NLJ_PREFETCH ACCESS NO_NLJ_PREFETCH 11.1.0.6
NO_NLJ_BATCHING ACCESS NLJ_BATCHING 11.1.0.6
NO_NLJ_PREFETCH ACCESS NLJ_PREFETCH 11.1.0.6
APPEND_VALUES APPEND_VALUES NOAPPEND 11.2.0.1
BIND_AWARE BIND_AWARE NO_BIND_AWARE 11.1.0.7
NO_BIND_AWARE BIND_AWARE BIND_AWARE 11.1.0.7
CHANGE_DUPKEY_ERROR_INDEX CHANGE_DUPKEY_ERROR_INDEX 11.1.0.7
CHECK_ACL_REWRITE CHECK_ACL_REWRITE NO_CHECK_ACL_REWRITE 11.1.0.6
COALESCE_SQ COALESCE_SQ NO_COALESCE_SQ 11.2.0.1
NO_COALESCE_SQ COALESCE_SQ COALESCE_SQ 11.2.0.1
CONNECT_BY_ELIM_DUPS CONNECT_BY_ELIM_DUPS NO_CONNECT_BY_ELIM_DUPS 11.2.0.1
NO_CONNECT_BY_ELIM_DUPS CONNECT_BY_ELIM_DUPS CONNECT_BY_ELIM_DUPS 11.2.0.1
COST_XML_QUERY_REWRITE COST_XML_QUERY_REWRITE NO_COST_XML_QUERY_REWRITE 11.1.0.6
DB_VERSION DB_VERSION 11.1.0.6
DOMAIN_INDEX_FILTER DOMAIN_INDEX_FILTER NO_DOMAIN_INDEX_FILTER 11.1.0.6
DST_UPGRADE_INSERT_CONV DST_UPGRADE_INSERT_CONV NO_DST_UPGRADE_INSERT_CONV 11.2.0.1
NO_DST_UPGRADE_INSERT_CONV DST_UPGRADE_INSERT_CONV DST_UPGRADE_INSERT_CONV 11.2.0.1
EXPAND_TABLE EXPAND_TABLE NO_EXPAND_TABLE 11.2.0.1
NO_EXPAND_TABLE EXPAND_TABLE EXPAND_TABLE 11.2.0.1
FACTORIZE_JOIN FACTORIZE_JOIN NO_FACTORIZE_JOIN 11.2.0.1
NO_FACTORIZE_JOIN FACTORIZE_JOIN FACTORIZE_JOIN 11.2.0.1
GBY_PUSHDOWN GBY_PUSHDOWN NO_GBY_PUSHDOWN 11.1.0.6
NO_GBY_PUSHDOWN GBY_PUSHDOWN GBY_PUSHDOWN 11.1.0.6
IGNORE_ROW_ON_DUPKEY_INDEX IGNORE_ROW_ON_DUPKEY_INDEX 11.1.0.7
USE_MERGE_CARTESIAN JOIN 11.1.0.6
MONITOR MONITOR NO_MONITOR 11.1.0.6
NO_MONITOR MONITOR MONITOR 11.1.0.6
NO_CHECK_ACL_REWRITE NO_CHECK_ACL_REWRITE CHECK_ACL_REWRITE 11.1.0.6
NO_COST_XML_QUERY_REWRITE NO_COST_XML_QUERY_REWRITE COST_XML_QUERY_REWRITE 11.1.0.6
NO_DOMAIN_INDEX_FILTER NO_DOMAIN_INDEX_FILTER DOMAIN_INDEX_FILTER 11.1.0.6
NO_LOAD NO_LOAD 11.1.0.6
NO_SUBSTRB_PAD NO_SUBSTRB_PAD 11.2.0.1
NO_OUTER_JOIN_TO_INNER OUTER_JOIN_TO_INNER OUTER_JOIN_TO_INNER 11.1.0.6
OUTER_JOIN_TO_INNER OUTER_JOIN_TO_INNER NO_OUTER_JOIN_TO_INNER 11.1.0.6
NO_PLACE_DISTINCT PLACE_DISTINCT PLACE_DISTINCT 11.2.0.1
PLACE_DISTINCT PLACE_DISTINCT NO_PLACE_DISTINCT 11.2.0.1
NO_PLACE_GROUP_BY PLACE_GROUP_BY PLACE_GROUP_BY 11.1.0.6
PLACE_GROUP_BY PLACE_GROUP_BY NO_PLACE_GROUP_BY 11.1.0.6
NO_RESULT_CACHE RESULT_CACHE RESULT_CACHE 11.1.0.6
RESULT_CACHE RESULT_CACHE NO_RESULT_CACHE 11.1.0.6
RETRY_ON_ROW_CHANGE RETRY_ON_ROW_CHANGE 11.1.0.7
NO_STATEMENT_QUEUING STATEMENT_QUEUING STATEMENT_QUEUING 11.2.0.1
STATEMENT_QUEUING STATEMENT_QUEUING NO_STATEMENT_QUEUING 11.2.0.1
NO_SUBQUERY_PRUNING SUBQUERY_PRUNING SUBQUERY_PRUNING 11.1.0.6
SUBQUERY_PRUNING SUBQUERY_PRUNING NO_SUBQUERY_PRUNING 11.1.0.6
NO_TRANSFORM_DISTINCT_AGG TRANSFORM_DISTINCT_AGG TRANSFORM_DISTINCT_AGG 11.2.0.1
TRANSFORM_DISTINCT_AGG TRANSFORM_DISTINCT_AGG NO_TRANSFORM_DISTINCT_AGG 11.2.0.1
NO_USE_INVISIBLE_INDEXES USE_INVISIBLE_INDEXES USE_INVISIBLE_INDEXES 11.1.0.6
USE_INVISIBLE_INDEXES USE_INVISIBLE_INDEXES NO_USE_INVISIBLE_INDEXES 11.1.0.6
NO_XMLINDEX_REWRITE XMLINDEX_REWRITE XMLINDEX_REWRITE 11.1.0.6
NO_XMLINDEX_REWRITE_IN_SELECT XMLINDEX_REWRITE XMLINDEX_REWRITE_IN_SELECT 11.1.0.6
XMLINDEX_REWRITE XMLINDEX_REWRITE NO_XMLINDEX_REWRITE 11.1.0.6
XMLINDEX_REWRITE_IN_SELECT XMLINDEX_REWRITE NO_XMLINDEX_REWRITE_IN_SELECT 11.1.0.6
XMLINDEX_SEL_IDX_TBL XMLINDEX_SEL_IDX_TBL 11.2.0.1
XML_DML_RWT_STMT XML_DML_RWT_STMT 11.1.0.6
58 rows selected.
1.INDEX_RS_ASC,INDEX_RS_DESC
这两个hints其实10203就可以使用了,最开始我是从这个bug看到他们俩的:
Bug 4323868 - INDEX hints can lead to INDEX SCAN FULL [ID 4323868.8]
因为有时INDEX这个hint无法控制是走INDEX RANGE SCAN还是INDEX FULL SCAN。
并且优化器常常会很SB无法判断,于是引入他们俩强制走INDEX RANGE SCAN(RS就是这个意思)。
例如:
select /*+index_rs_desc(t)*/ count(id) from t where id=1;
-------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 3 | 188 (2)| 00:00:03 |
| 1 | SORT AGGREGATE | | 1 | 3 | | |
|* 2 | INDEX RANGE SCAN DESCENDING| I | 104K| 304K| 188 (2)| 00:00:03 |
-------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("ID"=1)
filter("ID"=1)
select /*+index_rs(t)*/ count(id) from t where id=1;
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 3 | 188 (2)| 00:00:03 |
| 1 | SORT AGGREGATE | | 1 | 3 | | |
|* 2 | INDEX RANGE SCAN| I | 104K| 304K| 188 (2)| 00:00:03 |
--------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("ID"=1)
2.NLJ_BATCHING,NO_NLJ_BATCHING
这是一种11G新的内部优化算法。目前在网上几乎没有任何参考资料讲如何实现的。
从《Troubleshooting Oracle Performance》书上的Chapter 10 Optimizing Joins我可以找到只言片语:
“As of Oracle Database 11g, the following execution plan might be observed instead of the
previous one. Note that even if the query is always the same (that is, a two-table join), the
execution plan contains two nested loop joins! A simple performance test showed an improvement
of about 10 percent using it. This is probably because of a new internal optimization
that applies only to the new execution plan. To control this new execution plan, the hints
nlj_batching and no_nlj_batching are available.”
让我举个例,一个很简单的两个表的neested loop join:
create table t1 as select * from dba_objects;
create index t1idx on t1(object_id);
create table t2 as select * from dba_objects;
create index t2idx on t2(object_id);
select /*+use_nl(t1 t2) index(t1) index(t2) ordered NO_NLJ_BATCHING(t2)*/
t2.* from t1,t2
where t1.object_id=999
and t2.object_id=t1.object_id;
-------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 19511 | 4191K| 282 (1)| 00:00:04 |
| 1 | TABLE ACCESS BY INDEX ROWID| T2 | 140 | 28980 | 2 (0)| 00:00:01 |
| 2 | NESTED LOOPS | | 19511 | 4191K| 282 (1)| 00:00:04 |
|* 3 | INDEX RANGE SCAN | T1IDX | 140 | 1820 | 1 (0)| 00:00:01 |
|* 4 | INDEX RANGE SCAN | T2IDX | 56 | | 1 (0)| 00:00:01 |
-------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("T1"."OBJECT_ID"=999)
4 - access("T2"."OBJECT_ID"=999)
接着看看如果使用NLJ_BATCHING后SQL PLAN会变成什么样:
select /*+use_nl(t1 t2) index(t1) index(t2) ordered NLJ_BATCHING(t2)*/
t2.* from t1,t2
where t1.object_id=999
and t2.object_id=t1.object_id;
--------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 19511 | 4191K| 282 (1)| 00:00:04 |
| 1 | NESTED LOOPS | | | | | |
| 2 | NESTED LOOPS | | 19511 | 4191K| 282 (1)| 00:00:04 |
|* 3 | INDEX RANGE SCAN | T1IDX | 140 | 1820 | 1 (0)| 00:00:01 |
|* 4 | INDEX RANGE SCAN | T2IDX | 56 | | 1 (0)| 00:00:01 |
| 5 | TABLE ACCESS BY INDEX ROWID| T2 | 140 | 28980 | 2 (0)| 00:00:01 |
--------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("T1"."OBJECT_ID"=999)
4 - access("T2"."OBJECT_ID"=999)
可以看到,当我们使用NLJ_BATCHING后,一个两个表的neested loop join在执行计划里会显示两个neested loops。
经过我的若干大数据量的实验,并没有发现使用NLJ_BATCHING有特别大的如前面文档中所述的10%的improvement。
对于如下的小实验,使用NLJ_BATCHING反而有微小的performance degradation。
create table t1 as select * from dba_objects where rownum<=1000;
create table t2 as select * from dba_objects where rownum<=1000;
update t1 set object_id=999;
update t2 set object_id=999;
create index t1idx on t1(object_id);
create index t2idx on t2(object_id);
select /*+use_nl(t1 t2) index(t1) index(t2) ordered NO_NLJ_BATCHING(t2)*/
t2.* from t1,t2
where t1.object_id=999
and t2.object_id=t1.object_id;
Elapsed: 00:00:56.73
Statistics
----------------------------------------------------------
324 recursive calls
0 db block gets
149706 consistent gets
6 physical reads
0 redo size
45993532 bytes sent via SQL*Net to client
733849 bytes received via SQL*Net from client
66668 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1000000 rows processed
select /*+use_nl(t1 t2) index(t1) index(t2) ordered NLJ_BATCHING(t2)*/
t2.* from t1,t2
where t1.object_id=999
and t2.object_id=t1.object_id;
Elapsed: 00:00:57.15
Statistics
----------------------------------------------------------
1 recursive calls
0 db block gets
149674 consistent gets
0 physical reads
0 redo size
45993532 bytes sent via SQL*Net to client
733849 bytes received via SQL*Net from client
66668 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1000000 rows processed
BTW,这里有个隐藏参数控制是否默认开启NLJ_BATCHING:
_nlj_batching_enabled : enable batching of the RHS IO in NLJ
3.APPEND_VALUES,NOAPPEND
11G的APPEND_VALUES能让普通的insert...VALUES语句使用直接路径INSERT。
在以前我们常常使用insert /*+append*/ into <table> select...子句来使用直接路径INSERT。
从Oracle文档中看到:
“In direct-path INSERT, data is appended to the end of the table, rather than using existing space currently allocated to the table. As a result, direct-path INSERT can be considerably faster than conventional INSERT.”
这个hint对于我们需要批量insert特定的数据时,可以不用先sqlldr进一个临时表,然后使用insert+append+select子句来实现了。我们现在可以直接在程序里加上APPEND_VALUES hint来实现。
以下实验验证明普通insert和insert /*+APPEND_VALUES*/的区别(以一个ASSM tablespace的表为例):
create table testhao(n1 char(2000),n2 char(2000),n3 char(2000));
insert into testhao values('1','2','3');
commit;
SQL> exec hao_show_space('TESTHAO');
##################################################
--------hao_show_space kit created by Hao---------
##################################################
ASSM tablespace:
The segment space usage for TABLE "HAOZHU_USER.TESTHAO"
##################################################
UNUSED BLOCKS...........................0
UNUSED Bytes............................0
------------------------HWM------------------------
UNFORMATTED_BLOCKS......................0
--------------------------------------------------
FS1 Blocks..............................0
FS1 Bytes ..............................0
--------------------------------------------------
FS2 Blocks..............................1
FS2 Bytes ..............................8192
--------------------------------------------------
FS3 Blocks..............................0
FS3 Bytes ..............................0
--------------------------------------------------
FS4 Blocks..............................4
FS4 Bytes ..............................32768
--------------------------------------------------
FULL BLOCKS.............................0
FULL_BYTES .............................0
##################################################
Data Blocks(under HWM)..................5
All Blocks (under HWM)..................8
Total Blocks............................8
Total Bytes.............................65536
##################################################
Last Used Ext FileId....................4
Last Used Ext BlockId...................224
Last Used Block.........................8
PL/SQL procedure successfully completed.
SQL> insert /*+APPEND_VALUES*/ into testhao values('1','2','3');
1 row created.
SQL> exec hao_show_space('TESTHAO');
##################################################
--------hao_show_space kit created by Hao---------
##################################################
ASSM tablespace:
The segment space usage for TABLE "HAOZHU_USER.TESTHAO"
##################################################
UNUSED BLOCKS...........................8
UNUSED Bytes............................65536
------------------------HWM------------------------
UNFORMATTED_BLOCKS......................0
--------------------------------------------------
FS1 Blocks..............................0
FS1 Bytes ..............................0
--------------------------------------------------
FS2 Blocks..............................1
FS2 Bytes ..............................8192
--------------------------------------------------
FS3 Blocks..............................0
FS3 Bytes ..............................0
--------------------------------------------------
FS4 Blocks..............................4
FS4 Bytes ..............................32768
--------------------------------------------------
FULL BLOCKS.............................0
FULL_BYTES .............................0
##################################################
Data Blocks(under HWM)..................5
All Blocks (under HWM)..................8
Total Blocks............................16
Total Bytes.............................131072
##################################################
Last Used Ext FileId....................4
Last Used Ext BlockId...................224
Last Used Block.........................8
PL/SQL procedure successfully completed.
注释:以上可见直接路径insert后,HWM以下的blocks数目没变,但是HWM以上的UNUSED BLOCKS增加了,这是因为此时一个新的extent被分配进来了。并且直接路径insert的数据在HWM之上。
接着,我们commit。
SQL> commit;
Commit complete.
SQL> exec hao_show_space('TESTHAO');
##################################################
--------hao_show_space kit created by Hao---------
##################################################
ASSM tablespace:
The segment space usage for TABLE "HAOZHU_USER.TESTHAO"
##################################################
UNUSED BLOCKS...........................7
UNUSED Bytes............................57344
------------------------HWM------------------------
UNFORMATTED_BLOCKS......................0
--------------------------------------------------
FS1 Blocks..............................0
FS1 Bytes ..............................0
--------------------------------------------------
FS2 Blocks..............................1
FS2 Bytes ..............................8192
--------------------------------------------------
FS3 Blocks..............................0
FS3 Bytes ..............................0
--------------------------------------------------
FS4 Blocks..............................4
FS4 Bytes ..............................32768
--------------------------------------------------
FULL BLOCKS.............................1
FULL_BYTES .............................8192
##################################################
Data Blocks(under HWM)..................6
All Blocks (under HWM)..................9
Total Blocks............................16
Total Bytes.............................131072
##################################################
Last Used Ext FileId....................4
Last Used Ext BlockId...................232
Last Used Block.........................1
PL/SQL procedure successfully completed.
注释:从上可见,commit之后,直接路径insert的数据才可见,表现在HWM下多了一个data block。
接着,我们比较下普通insert。
SQL> insert into testhao values('1','2','3');
1 row created.
SQL> exec hao_show_space('TESTHAO');
##################################################
--------hao_show_space kit created by Hao---------
##################################################
ASSM tablespace:
The segment space usage for TABLE "HAOZHU_USER.TESTHAO"
##################################################
UNUSED BLOCKS...........................7
UNUSED Bytes............................57344
------------------------HWM------------------------
UNFORMATTED_BLOCKS......................0
--------------------------------------------------
FS1 Blocks..............................0
FS1 Bytes ..............................0
--------------------------------------------------
FS2 Blocks..............................2
FS2 Bytes ..............................16384
--------------------------------------------------
FS3 Blocks..............................0
FS3 Bytes ..............................0
--------------------------------------------------
FS4 Blocks..............................3
FS4 Bytes ..............................24576
--------------------------------------------------
FULL BLOCKS.............................1
FULL_BYTES .............................8192
##################################################
Data Blocks(under HWM)..................6
All Blocks (under HWM)..................9
Total Blocks............................16
Total Bytes.............................131072
##################################################
Last Used Ext FileId....................4
Last Used Ext BlockId...................232
Last Used Block.........................1
PL/SQL procedure successfully completed.
注释:以上可见,普通insert直接使用了当前可用的一个block,并没有使用HWM以上的block。
APPEND_VALUES和APPEND这两个hints的反向hint即是NO_APPEND。
那什么时候我们需要用NO_APPEND呢?
既然我们需要显示加上hint才能走直接路径INSERT,那不加这些hints不就可以不走直接路径么?
上面这句话只说对了一半,因为我们走并行insert时,默认是开启直接路径INSERT的。
所以,在这个时候我们需要显示加上NO_APPEND表明不希望走直接路径。
“Conventional INSERT is the default in serial mode, and direct-path INSERT is the default in parallel mode.”
4.BIND_AWARE,NO_BIND_AWARE
跟11G Adaptive Cursor Sharing相关的hint。
具体参见我的另一篇研究ACS的文章:
http://space.itpub.net/15415488/viewspace-621535 |
|