sitemap

RSS地图

收藏本站

设为首页

Oracle研究中心

当前位置:Oracle研究中心 > 运维DBA >

【学习笔记】Oracle 11GR2新特性Adaptive Cursor Sharing(ACS)

时间:2016-11-14 10:07   来源:Oracle研究中心   作者:网络   点击:

天萃荷净 Oracle研究中心学习笔记:分享一篇关于Oracle 11.2.0.1 11Gr2数据库最新版本中最新特性Adaptive Cursor Sharing(ACS)深入研究笔记。

本站文章除注明转载外,均为本站原创: 转载自love wife & love life —Roger 的Oracle技术博客
本文链接地址: 11gR2 新特性之(一)Adaptive Cursor Sharing(ACS)

关于该特性,其实并不是11gR2开始引入的,其实在11gR1就引入了,只不过其问题较多,
并未引起太多关注而已(bug不少)。该特性主要解决了哪些问题?
● data skew (数据倾斜)
● bind peeking (绑定变量窥视)– oracle 9i 引入

1.如何理解ACS?

ACS will allow multiple execution plans for a statement that use bind variables
ensuring that the best execution plan will be used for a specific value of the bind variable.

通俗的讲,就是会根据绑定变量的值来智能判断选择最优的执行计划

例如:select * from tab_a where a=:x  可能存在多个不同的执行计划
关于该特性也是通过几个隐含参数来控制的,11gR2 默认为true,如下:

SQL> SHOW parameter _optimizer_adaptive
NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
_optimizer_adaptive_cursor_sharing   BOOLEAN     TRUE

SQL> SHOW parameter optim_peek

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
_optim_peek_user_binds               BOOLEAN     TRUE

说明:
_optimizer_adaptive_cursor_sharing ==> ACS特性
_optim_peek_user_binds             ==> 绑定变量窥视


2.创建测试表

SQL> CREATE TABLE ht1 AS SELECT owner,object_id,object_name FROM dba_objects;
TABLE created.

SQL> SELECT COUNT(object_id) FROM ht1;

COUNT(OBJECT_ID)
----------------
           71878

SQL> SELECT MAX(object_id) FROM ht1;

MAX(OBJECT_ID)
--------------
         73406

SQL> UPDATE ht1 SET object_id=100 WHERE object_id < 73405;
71876 ROWS updated.

SQL> commit;
Commit complete.

SQL> UPDATE ht1 SET object_id=100 WHERE object_id < 73000;
71679 ROWS updated.

SQL> commit;
Commit complete.

SQL> UPDATE ht1 SET object_id=1000 WHERE object_id > 73000 AND object_id < 73300;
150 ROWS updated.

SQL> commit;
Commit complete.

SQL> UPDATE ht1 SET object_id=10000 WHERE object_id > 73329;
34 ROWS updated.

SQL> commit;
Commit complete.

SQL> UPDATE ht1 SET object_id=10000 WHERE object_id > 70000;
15 ROWS updated.

SQL> commit;
Commit complete.

SQL> SELECT object_id,COUNT(*) FROM ht1 GROUP BY object_id;

OBJECT_ID   COUNT(*)
---------- ----------
       100      71679
      1000        150
     10000         49

SQL> CREATE INDEX idx_id ON ht1(object_id);
INDEX created.

SQL> EXEC dbms_stats.gather_table_stats(USER,'HT1',method_opt=>'for all columns size skewonly');
PL/SQL PROCEDURE successfully completed.

SQL> SELECT TABLE_NAME,COLUMN_NAME,DENSITY,HISTOGRAM
  2  FROM user_tab_columns
  3  WHERE TABLE_NAME='HT1';

TABLE_NAME                     COLUMN_NAME                       DENSITY HISTOGRAM
------------------------------ ------------------------------ ---------- ---------------
HT1                            OWNER                          6.9461E-06 FREQUENCY
HT1                            OBJECT_ID                      6.9461E-06 FREQUENCY
HT1                            OBJECT_NAME                    .000035426 HEIGHT BALANCED

SQL> ALTER system FLUSH shared_pool;
System altered.

SQL> var a NUMBER;
SQL> EXEC :a :=1000;
PL/SQL PROCEDURE successfully completed.

SQL> SELECT * FROM ht1 WHERE object_id =:a;

OWNER                           OBJECT_ID OBJECT_NAME

------------------------------ ---------- -----------------------------------
APEX_030200                          1000 WWV_MIG_ACC_LOAD
... ... ...                          ... ... ...
SYS                                  1000 WRH$_SQLSTAT_PK
SYS                                  1000 WRH$_SYSTEM_EVENT

150 ROWS selected.

SQL> SELECT * FROM TABLE(dbms_xplan.display_cursor);

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------
SQL_ID  9zq6asm9yfrc9, child NUMBER 0
-------------------------------------
SELECT * FROM ht1 WHERE object_id =:a
Plan hash VALUE: 2446245938
--------------------------------------------------------------------------------------
| Id  | Operation                   | Name   | ROWS  | Bytes | Cost (%CPU)| TIME     |
--------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |        |       |       |     3 (100)|          |
|   1 |  TABLE ACCESS BY INDEX ROWID| HT1    |   223 |  7582 |     3   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN          | IDX_ID |   223 |       |     1   (0)| 00:00:01 |
--------------------------------------------------------------------------------------
Predicate Information (IDENTIFIED BY operation id):
---------------------------------------------------
   2 - access("OBJECT_ID"=:A)

19 ROWS selected.

SQL> SELECT hash_value FROM v$sql WHERE sql_id='9zq6asm9yfrc9';

HASH_VALUE
----------
3555155337

SQL> SELECT CHILD_NUMBER,PLAN_HASH_VALUE,EXECUTIONS,
  2         BUFFER_GETS/EXECUTIONS BG_PER_EX,
  3         IS_BIND_SENSITIVE BS,IS_BIND_AWARE BA,IS_SHAREABLE S
  4    FROM v$sql
  5   WHERE hash_value='&hash_value';

Enter VALUE FOR hash_value: 3555155337
CHILD_NUMBER PLAN_HASH_VALUE EXECUTIONS  BG_PER_EX B B S

------------ --------------- ---------- ---------- - - -
           0      2446245938          1        137 Y N Y

SQL> SELECT IS_OBSOLETE ,IS_BIND_SENSITIVE,IS_BIND_AWARE,IS_SHAREABLE,BIND_DATA
  2  FROM v$sql
  3  WHERE SQL_ID='9zq6asm9yfrc9';

I I I I BIND_DATA
- - - - ------------------------------------------------------------
N Y N Y BEDA0B2001004DFE20CD000101C0021602C20B

SQL> SELECT * FROM v$sql_cs_selectivity WHERE sql_id='9zq6asm9yfrc9';
no ROWS selected

3.测试说明:

这里有几个字段,需要解释一下,如下:
IS_OBSOLETE        ==> 是否废弃(cursor被废弃,起执行计划可能发生变化,通常情况下,其child cursor太大了,该游标将被obsolete)
IS_BIND_SENSITIVE  ==> 为Y表示启用了绑定变量窥视,SQL的执行计划取决于变量值
IS_BIND_AWARE      ==> 表示是否启动extended cursor sharing
IS_SHAREABLE       ==> 是否共享,如果不能共享,那么该SQL被page OUT出shared pool。

关于 extended cursor sharing,有2个参数,如下:

SQL> SHOW parameter extended_cursor
NAME                                    TYPE        VALUE
------------------------------------    ----------- ------------------------------
_optimizer_extended_cursor_sharing      string      UDO
_optimizer_extended_cursor_sharing_rel  string      SIMPLE       
                           

关于这2个隐含参数,还需要进一步研究。

SQL> var a NUMBER;
SQL> EXEC :a :=100;
PL/SQL PROCEDURE successfully completed.

SQL> SELECT * FROM ht1 WHERE object_id =:a;
OWNER                           OBJECT_ID OBJECT_NAME
------------------------------ ---------- -----------------------------------
... ... ...                           ... ... ...
... ... ...                           ... ... ...
APEX_030200                           100 APEX_MIGRATION_ACC_RPTS
APEX_030200                           100 APEX_MIGRATION_ACC_QUERIES
APEX_030200                           100 APEX_MIGRATION_REV_FORMS
APEX_030200                           100 APEX_MIGRATION_REV_RPTS
APEX_030200                           100 APEX_MIGRATION_REV_QUERIES

71679 ROWS selected.

SQL> SELECT * FROM TABLE(dbms_xplan.display_cursor);

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------
SQL_ID  9zq6asm9yfrc9, child NUMBER 0
-------------------------------------
SELECT * FROM ht1 WHERE object_id =:a
Plan hash VALUE: 2446245938
--------------------------------------------------------------------------------------
| Id  | Operation                   | Name   | ROWS  | Bytes | Cost (%CPU)| TIME     |
--------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |        |       |       |     3 (100)|          |
|   1 |  TABLE ACCESS BY INDEX ROWID| HT1    |   223 |  7582 |     3   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN          | IDX_ID |   223 |       |     1   (0)| 00:00:01 |
--------------------------------------------------------------------------------------
Predicate Information (IDENTIFIED BY operation id):
---------------------------------------------------
   2 - access("OBJECT_ID"=:A)  ###### 这里居然是INDEX range scan ######
                               ###### 由于id=100的选择性非常差,此时走全表扫描才是正常的执行计划 ######

19 ROWS selected.

SQL> SELECT CHILD_NUMBER,PLAN_HASH_VALUE,EXECUTIONS,
  2         BUFFER_GETS/EXECUTIONS BG_PER_EX,
  3         IS_BIND_SENSITIVE BS,IS_BIND_AWARE BA,IS_SHAREABLE S
  4    FROM v$sql
  5   WHERE sql_id='9zq6asm9yfrc9';

CHILD_NUMBER PLAN_HASH_VALUE EXECUTIONS  BG_PER_EX B B S
------------ --------------- ---------- ---------- - - -
           0      2446245938          2       5101 Y N Y

这里EXECUTIONS为2,说明这次的执行计划实际上是沿用的上次object_id为1000的执行计划。

再次执行相同的绑定变量值:

SQL> EXEC :a :=100;
PL/SQL PROCEDURE successfully completed.

SQL> SELECT * FROM ht1 WHERE object_id =:a;

OWNER                           OBJECT_ID OBJECT_NAME

------------------------------ ---------- -----------------------------------
... ... ...                           ... ... ...
... ... ...                           ... ... ...
APEX_030200                           100 APEX_MIGRATION_ACC_PROJECTS
APEX_030200                           100 APEX_MIGRATION_ACC_TABLES
APEX_030200                           100 APEX_MIGRATION_ACC_FORMS
APEX_030200                           100 APEX_MIGRATION_ACC_RPTS
APEX_030200                           100 APEX_MIGRATION_ACC_QUERIES
APEX_030200                           100 APEX_MIGRATION_REV_FORMS
APEX_030200                           100 APEX_MIGRATION_REV_RPTS
APEX_030200                           100 APEX_MIGRATION_REV_QUERIES

71679 ROWS selected.

SQL> SELECT * FROM TABLE(dbms_xplan.display_cursor);

PLAN_TABLE_OUTPUT
-------------------------------------------------------------------------
SQL_ID  9zq6asm9yfrc9, child NUMBER 1
-------------------------------------
SELECT * FROM ht1 WHERE object_id =:a
Plan hash VALUE: 3708914037
--------------------------------------------------------------------------
| Id  | Operation         | Name | ROWS  | Bytes | Cost (%CPU)| TIME     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |       |       |   116 (100)|          |
|*  1 |  TABLE ACCESS FULL| HT1  | 71590 |  2377K|   116   (1)| 00:00:02 |
--------------------------------------------------------------------------
Predicate Information (IDENTIFIED BY operation id):
---------------------------------------------------
   1 - FILTER("OBJECT_ID"=:A)    ###### 此时的执行计划就正确,变成了全表扫描 ######

18 ROWS selected.

SQL> SELECT CHILD_NUMBER,PLAN_HASH_VALUE,EXECUTIONS,                         
  2         BUFFER_GETS/EXECUTIONS BG_PER_EX,                                          
  3         IS_BIND_SENSITIVE BS,IS_BIND_AWARE BA,IS_SHAREABLE S
  4    FROM v$sql
  5   WHERE sql_id='9zq6asm9yfrc9';

CHILD_NUMBER PLAN_HASH_VALUE EXECUTIONS  BG_PER_EX B B S                     

------------ --------------- ---------- ---------- - - -                     
           0      2446245938          2       5101 Y N Y                     
           1      3708914037          1       5159 Y Y Y                     

这里我们可以发现,该游标多了一个child,child 1就是对于前面的全表扫描执行计划,其执行次数为1.
如果我们再次执行相同的SQL,那么child 1的executions 必然会增加1.
如下:

SQL> EXEC :a :=100;
PL/SQL PROCEDURE successfully completed.

SQL> SELECT * FROM ht1 WHERE object_id =:a;
OWNER                           OBJECT_ID OBJECT_NAME
------------------------------ ---------- -----------------------------------
... ... ...                           ... ... ...
... ... ...                           ... ... ...
APEX_030200                           100 APEX_MIGRATION_REV_RPTS
APEX_030200                           100 APEX_MIGRATION_REV_QUERIES

71679 ROWS selected.

SQL> SELECT CHILD_NUMBER,PLAN_HASH_VALUE,EXECUTIONS,                     
  2         BUFFER_GETS/EXECUTIONS BG_PER_EX,                     
  3         IS_BIND_SENSITIVE BS,IS_BIND_AWARE BA,IS_SHAREABLE S
  4    FROM v$sql
  5   WHERE sql_id='9zq6asm9yfrc9';

CHILD_NUMBER PLAN_HASH_VALUE EXECUTIONS  BG_PER_EX B B S
------------ --------------- ---------- ---------- - - -
           0      2446245938          2       5101 Y N Y
           1      3708914037          2       5159 Y Y Y


SQL> SELECT CHILD_NUMBER,PREDICATE,RANGE_ID,LOW,HIGH
  2  FROM v$sql_cs_selectivity
  3  WHERE hash_value='3555155337';

CHILD_NUMBER PREDICATE                                  RANGE_ID LOW        HIGH
------------ ---------------------------------------- ---------- ---------- ----------
           1 =A                                                0 0.896393   1.095591

在11.2的官方文档中,居然没有v$sql_cs_selectivity的说明,oracle也太扯淡了。
为什么说11gR2之前,这个新功能问题相对比较多多,metalink 搜索v$sql_cs_selectivity,居然有3个跟这个新特性相关的bug。

Bug 7213010  Adaptive cursor sharing generates lots of child cursors   --11.1.0.6
Bug 6644714 - High number of child cursors with adaptive cursor sharing  --11.1.0.6
Bug 8491399 - Adaptive Cursor Sharing does not match the correct cursor version for queries using CHAR datatype  --11.1.0.7

意外的收获是发现了一个查询V$SQL_CS_SELECTIVITY的 bug,如下:
Bug 10058195 - V$SQL_CS_SELECTIVITY columns are padded with chr(0) characters
不过这个bug不影响数据库正常使用。

补充:
跟11g自适应游标共享功能相关的有几个新的视图,平时我们可以借此来进行监控,如下:
V$SQL_CS_SELECTIVITY
V$SQL_CS_STATISTICS
V$SQL_CS_HISTOGRAM

关于这3个视图,oracle metalink的解释如下:

V$SQL_CS_SELECTIVITY exposes the valid selectivity ranges for a child cursor in extended
cursor sharing mode. Ahttp://www.oracleplus.net valid range consists of a low and high value
for each predicate containing binds. Each predicate's selectivity (with the current bind value) must
fall between the corresponding low and high values in order for the child cursor to be shared.

V$SQL_CS_STATISTICS contains the raw execution statistics used by the monitoring component
of adaptive cursor sharing. A sample of the executions is monitored.
This view exposes which executions were sampled, and what the statistics were for those
executions. The statistics are cumulative for each distinct set of bind values.

V$SQL_CS_HISTOGRAM summarizes the monitoring information stored by adaptive cursor
sharing. This information is used to decide whether to enable extended cursor sharing for a query. It
is stored in a histogram, whose bucket's contents are exposed by this view.

下面来查询一下看看;

SQL> SELECT * FROM V$SQL_CS_SELECTIVITY;

ADDRESS  HASH_VALUE SQL_ID        CHILD_NUMBER PREDICATE                                  RANGE_ID LOW        HIGH
-------- ---------- ------------- ------------ ---------------------------------------- ---------- ---------- ----------
2998C51C 3555155337 9zq6asm9yfrc9            1 =A                                                0 0.896393   1.095591

SQL> SELECT * FROM V$SQL_CS_STATISTICS WHERE sql_id='9zq6asm9yfrc9';

ADDRESS  HASH_VALUE SQL_ID        CHILD_NUMBER BIND_SET_HASH_VALUE P EXECUTIONS ROWS_PROCESSED BUFFER_GETS   CPU_TIME
-------- ---------- ------------- ------------ ------------------- - ---------- -------------- ----------- ----------
2998C51C 3555155337 9zq6asm9yfrc9            1           336594526 Y          1          71679        5159          0
2998C51C 3555155337 9zq6asm9yfrc9            0          3036353656 Y          1            300         137          0

SQL> SELECT * FROM V$SQL_CS_HISTOGRAM
  2  WHERE sql_id='9zq6asm9yfrc9'
  3  ORDER BY CHILD_NUMBER;

ADDRESS  HASH_VALUE SQL_ID        CHILD_NUMBER  BUCKET_ID      COUNT
-------- ---------- ------------- ------------ ---------- ----------
2998C51C 3555155337 9zq6asm9yfrc9            0          1          1
2998C51C 3555155337 9zq6asm9yfrc9            0          0          1
2998C51C 3555155337 9zq6asm9yfrc9            0          2          0
2998C51C 3555155337 9zq6asm9yfrc9            1          1          2
2998C51C 3555155337 9zq6asm9yfrc9            1          0          0
2998C51C 3555155337 9zq6asm9yfrc9            1          2          0

6 ROWS selected.

4.如下结论

V$SQL_CS_SELECTIVITY 用于查询cursor的最高值和最低值的选择性,oracle也正是根据其选择性来决定起执行计划的,不过内部机制现
在我还无法得知,比如 object_id 有1000个值,不可能每次不同的绑定变量值,oracle都去生成一个执行计划或产生一个child cursor,
那样的话,代价就非常高了。– 这个需要进一步研究。

V$SQL_CS_STATISTICS 从上面的查询,我们就可以看出,该视图用于查询每个child cursor的统计信息,比如buffer gets。
其实,从这个我们也可以用来判断sql的效率,这个不就是我们常说的逻辑读吗?

V$SQL_CS_HISTOGRAM 类似直方图一样,用于记录cursor的执行次数,从上面的查询,我们可以发现每个child cursor一共有3个bucket。
关于这里的bucket,目前还不知道是不是就是固定的3个bucket。– 这里也需要进一步研究证明。

另外如果修改了参数curso_sharing为similar或force的话,也可能会导致比较严重的后果,可能会出现大量的 mutex X waits for cursor等待。
故我们仍然建议设置为EXACT,从应用角度进行绑定变量。

既然我们说ACS功能很强悍,假如不想用这个功能呢,是否能关闭呢? 回答是肯定的,通过如下的方式:

ALTER system SET "_optimizer_extended_cursor_sharing_rel"=NONE;
ALTER system SET "_optimizer_extended_cursor_sharing"=NONE;
ALTER system SET "_optimizer_adaptive_cursor_sharing"=FALSE;
另外我在阅读metalink 文档Adaptive Cursor Sharing Overview [ID 740052.1] 的时候,还发现了如下的信息:

If any of the following checks fail ECS will be disabled
- Extended cursor sharing is disabled
- The query has no binds
- Parallel query is used
- Certain parameters like ("bind peeking"=false) are set
- Hints are in use
- Outlines are being used
- It is a recursive query
- The number of binds in a given sql statement are greater than 14.
换句话说,就是ACS功能,在上面几种情况下是起作用的。

--------------------------------------ORACLE-DBA----------------------------------------

最权威、专业的Oracle案例资源汇总之【学习笔记】Oracle 11GR2新特性Adaptive Cursor Sharing(ACS)

本文由大师惜分飞原创分享,网址:http://www.oracleplus.net/arch/1262.html

Oracle研究中心

关键词:

Oracle SQL优化笔记

Adaptive Cursor Sharing

Oracle ACS

Oracle 11GR2深入研究新特性ACS