一、什么是ACS(adaptiver cursor sharing)
Oracle通过绑定变量技术解决了SQL语句硬解析过多的问题,降低了资源的争用。但是绑定变量在引入cursor sharing,增加了软解析的同时,也带来了CBO环境下的Bind peeking问题。
所谓bind peeking是指,Oracle在第一次解析sql的时候,会“偷偷”地(peek)查看一下输入的绑定变量值,然后根据“偷看”到的数据值来确定执行计划,保存在liberary cache中,作为下一次的执行计划。这就带来一个问题,如果下次sql输入的变量恰好是和第一次取值分布差异很大的数据值(date kew),那么对该sql来讲就有可能使用低效甚至是错误的执行计划。
从oracle11g开始,为了弥补bind peeking的缺陷,oracle引入了ACS(Adaptive Cursor Sharing)技术,该技术将绑定变量的执行计划变为一个基于统计量分析的自适应过程,会根据绑定变量的数值动态调整执行计划。
Oracle使用ACS有两个前提条件:
1、绑定变量使用了bind peeking。
2、绑定变量的列上有直方图信息。
下面对ACS(adaptiver cursor sharing)进行测试。
二、实例演示ACS
1、建立测试对象
SQL> select * from v$version;
BANNER
--------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production
PL/SQL Release 11.2.0.3.0 - Production
CORE 11.2.0.3.0 Production
TNS for Linux: Version 11.2.0.3.0 - Production
NLSRTL Version 11.2.0.3.0 - Production
create table t
as
select 1 X, rpad('*',4000,'*') data from all_objects a
/
insert into t
select 2, rpad('*',4000,'*') from all_objects a where rownum = 1
/
create index t_idx on t(x)
/
SQL> select x,count(*) from t group by x;
X COUNT(*)
---------- ----------
1 90892
2 1
--没有统计信息没有直方图
2.没有使用绑定变量
SQL> set autotrace trace exp;
SQL> select count(data) from t where x=1;
Execution Plan
----------------------------------------------------------
Plan hash value: 2966233522
---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 2015 | 24785 (1)| 00:04:58 |
| 1 | SORT AGGREGATE | | 1 | 2015 | | |
|* 2 | TABLE ACCESS FULL| T | 82688 | 158M| 24785 (1)| 00:04:58 |
---------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("X"=1)
Note
-----
- dynamic sampling used for this statement (level=2)
SQL> select count(data) from t where x=2;
Execution Plan
----------------------------------------------------------
Plan hash value: 1789076273
--------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 2015 | 10 (0)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | 2015 | | |
| 2 | TABLE ACCESS BY INDEX ROWID| T | 1 | 2015 | 10 (0)| 00:00:01 |
|* 3 | INDEX RANGE SCAN | T_IDX | 1 | | 1 (0)| 00:00:01 |
--------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("X"=2)
Note
-----
- dynamic sampling used for this statement (level=2)
--收集表的统计信息
SQL> exec dbms_stats.gather_table_stats('scott','t');
PL/SQL procedure successfully completed.
SQL> select count(data) from t where x=1;
Execution Plan
----------------------------------------------------------
Plan hash value: 2966233522
---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 4004 | 24785 (1)| 00:04:58 |
| 1 | SORT AGGREGATE | | 1 | 4004 | | |
|* 2 | TABLE ACCESS FULL| T | 90876 | 347M| 24785 (1)| 00:04:58 |
---------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("X"=1)
SQL> select count(data) from t where x=2;
Execution Plan
----------------------------------------------------------
Plan hash value: 1789076273
--------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 4004 | 18 (0)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | 4004 | | |
| 2 | TABLE ACCESS BY INDEX ROWID| T | 17 | 68068 | 18 (0)| 00:00:01 |
|* 3 | INDEX RANGE SCAN | T_IDX | 17 | | 1 (0)| 00:00:01 |
--------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("X"=2)
可见当你有没有收集统计信息谓词条件为1时走全表扫描,谓词条件为2时走index range scan,11g的动态采样还是蛮准确的,但是全表扫描时估计消耗的资源差距蛮大。
3.使用绑定变量
SQL> alter session set statistics_level=all;
Session altered.
SQL> alter system flush shared_pool;
System altered.
SQL> variable x number;
SQL> exec :x:=1;
SQL> select count(data) from t where x=:x;
COUNT(DATA)
-----------
90892
SQL> select sql_id,child_number,executions,loads,buffer_gets,is_bind_sensitive as "bind_sensi",is_bind_aware as "bind_aware",is_shareable as "bind_share" from v$sql where sql_text like 'select count(data) from t where x=:x';
SQL_ID CHILD_NUMBER EXECUTIONS LOADS BUFFER_GETS b b b
------------- ------------ ---------- ---------- ----------- - - -
0qfzsu17zyhtt 0 1 2 91072 Y N Y
设置绑定变量值为2后的第一次查询:
SQL> exec :x:=2;
SQL> select count(data) from t where x=:x;
COUNT(DATA)
-----------
1
SQL> select sql_id,child_number,executions,loads,buffer_gets,is_bind_sensitive as "bind_sensi",is_bind_aware as "bind_aware",is_shareable as "bind_share" from v$sql where sql_text like 'select count(data) from t where x=:x';
SQL_ID CHILD_NUMBER EXECUTIONS LOADS BUFFER_GETS b b b
------------- ------------ ---------- ---------- ----------- - - -
0qfzsu17zyhtt 0 2 2 182001 Y N Y
结果表明,谓词条件为2时的第一次查询,沿用了谓词等于1时的执行计划。
设置绑定变量值为2后的第二次查询:
SQL> select count(data) from t where x=:x;
COUNT(DATA)
-----------
1
SQL> select sql_id,child_number,executions,loads,buffer_gets,is_bind_sensitive as "bind_sensi",is_bind_aware as "bind_aware",is_shareable as "bind_share" from v$sql where sql_text like 'select count(data) from t where x=:x';
SQL_ID CHILD_NUMBER EXECUTIONS LOADS BUFFER_GETS b b b
------------- ------------ ---------- ---------- ----------- - - -
0qfzsu17zyhtt 0 2 2 182001 Y N N
0qfzsu17zyhtt 1 1 2 3 Y Y Y
结果表明,谓词条件为2时的第二次查询,重新生成了新的执行计划。
设置绑定变量值为2后的第三次查询:
SQL> select count(data) from t where x=:x;
COUNT(DATA)
-----------
1
SQL> select sql_id,child_number,executions,loads,buffer_gets,is_bind_sensitive as "bind_sensi",is_bind_aware as "bind_aware",is_shareable as "bind_share" from v$sql where sql_text like 'select count(data) from t where x=:x';
SQL_ID CHILD_NUMBER EXECUTIONS LOADS BUFFER_GETS b b b
------------- ------------ ---------- ---------- ----------- - - -
0qfzsu17zyhtt 0 2 2 182001 Y N N
0qfzsu17zyhtt 1 2 2 6 Y Y Y
结果表明,谓词条件为2时的第三次查询,沿用了新生成的执行计划。
Oracle从11g开始,在v$sql视图中增加了is_bind_sensitive、is_bind_aware和is_shareable三列。其中:
1、is_bind_sensitive
表示游标是否对绑定变量敏感。数值如果为Y,表示当绑定变量的数值发生变化后,优化器有可能会产生一个不同的执行计划,简单说就是ACS生效了。
2、is_bind_aware
表示该游标是否使用了extended cursor sharing技术,数值如果为Y,表示oracle认为此处cursor的值可能会改变执行计划。
3、is_shareable
表示该游标能否重用,能否被下次共享。数值如果为Y表示能够共享,数值如果为N表示该子游标失去了共享价值,等待被Age Out出内存;
--设置绑定变量值为1后的再次查询:
SQL> exec :x:=1;
PL/SQL procedure successfully completed.
SQL> select count(data) from t where x=:x;
COUNT(DATA)
-----------
90892
SQL> select sql_id,child_number,executions,loads,buffer_gets,is_bind_sensitive as "bind_sensi",is_bind_aware as "bind_aware",is_shareable as "bind_share" from v$sql where sql_text like 'select count(data) from t where x=:x';
SQL_ID CHILD_NUMBER EXECUTIONS LOADS BUFFER_GETS b b b
------------- ------------ ---------- ---------- ----------- - - -
0qfzsu17zyhtt 0 2 2 182001 Y N N
0qfzsu17zyhtt 1 2 2 6 Y Y Y
0qfzsu17zyhtt 2 1 1 90929 Y Y Y
结果表明,谓词条件为1时的重新查询,生产新的执行计划。因为之前的执行计划已经不能共享了
查看绑定变量为1时的执行计划:
SQL> select * from table(dbms_xplan.display_cursor('0qfzsu17zyhtt',format => 'advanced allstats peeked_binds'));
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID 0qfzsu17zyhtt, child number 0
-------------------------------------
select count(data) from t where x=:x
Plan hash value: 2966233522
------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time | A-Rows | A-Time | Buffers | Reads |
------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 2 | | | 24785 (100)| | 2 |00:00:01.28 | 181K| 104K|
| 1 | SORT AGGREGATE | | 2 | 1 | 4004 | | | 2 |00:00:01.28 | 181K| 104K|
|* 2 | TABLE ACCESS FULL| T | 2 | 90876 | 347M| 24785 (1)| 00:04:58 | 90893 |00:00:01.27 | 181K| 104K|
------------------------------------------------------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
1 - SEL$1
2 - SEL$1 / T@SEL$1
Outline Data
-------------
/*+
BEGIN_OUTLINE_DATA
IGNORE_OPTIM_EMBEDDED_HINTS
OPTIMIZER_FEATURES_ENABLE('11.2.0.3')
DB_VERSION('11.2.0.3')
ALL_ROWS
OUTLINE_LEAF(@"SEL$1")
FULL(@"SEL$1" "T"@"SEL$1")
END_OUTLINE_DATA
*/
Peeked Binds (identified by position):
--------------------------------------
1 - (NUMBER): 1
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("X"=:X)
Column Projection Information (identified by operation id):
-----------------------------------------------------------
1 - (#keys=0) COUNT("DATA")[22]
2 - "DATA"[VARCHAR2,4000]
查看绑定变量为2时,新生成的执行计划:
SQL> select * from table(dbms_xplan.display_cursor('0qfzsu17zyhtt',1,format => 'advanced allstats peeked_binds'));
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID 0qfzsu17zyhtt, child number 1
-------------------------------------
select count(data) from t where x=:x
Plan hash value: 1789076273
--------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time | A-Rows | A-Time | Buffers |
--------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 2 | | | 18 (100)| | 2 |00:00:00.01 | 6 |
| 1 | SORT AGGREGATE | | 2 | 1 | 4004 | | | 2 |00:00:00.01 | 6 |
| 2 | TABLE ACCESS BY INDEX ROWID| T | 2 | 17 | 68068 | 18 (0)| 00:00:01 | 2 |00:00:00.01 | 6 |
|* 3 | INDEX RANGE SCAN | T_IDX | 2 | 17 | | 1 (0)| 00:00:01 | 2 |00:00:00.01 | 4 |
--------------------------------------------------------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
1 - SEL$1
2 - SEL$1 / T@SEL$1
3 - SEL$1 / T@SEL$1
Outline Data
-------------
/*+
BEGIN_OUTLINE_DATA
IGNORE_OPTIM_EMBEDDED_HINTS
OPTIMIZER_FEATURES_ENABLE('11.2.0.3')
DB_VERSION('11.2.0.3')
ALL_ROWS
OUTLINE_LEAF(@"SEL$1")
INDEX_RS_ASC(@"SEL$1" "T"@"SEL$1" ("T"."X"))
END_OUTLINE_DATA
*/
Peeked Binds (identified by position):
--------------------------------------
1 - (NUMBER): 2
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("X"=:X)
Column Projection Information (identified by operation id):
-----------------------------------------------------------
1 - (#keys=0) COUNT("DATA")[22]
2 - "DATA"[VARCHAR2,4000]
3 - "T".ROWID[ROWID,10]
由执行计划可知,设置绑定变量为2后,第二次以后的执行计划是正确的执行计划。由此可知,ACS技术弥补了bind peeking的不足,保证了绑定变量数值发生变化后,sql语句能够选择正确的执行计划。
本文乃原创文章,请勿转载。如须转载请详细标明转载出处