DBMS Tuning & My Life

The mechanism of Adaptive Cursor Sharing.

leave a comment »

Adaptive Cursor Sharing(ACS) was introduced in oracle 11g. Until 10g, the oracle only supports bind peeking (from 9i). But, bind peeking has a big problem, when the column in the where clause with the bind has a skew data.To solve that problem ACS need to use a column histogram. Now, try to test to understand the ACS.

(1) Create a table and insert skew data.

create table t1 (c1 number, c2 number, dummy char(1000));
 
insert into t1
select level, level, 'dummy'
from   dual
connect by level<=1000
union all
select level+1000, 1001, 'dummy'
from   dual
connect by level<=99000;
 
commit; 
 
create index idx1 on t1(c2) nologging;
exec dbms_stats.gather_table_stats(user,'T1', method_opt=>'FOR ALL INDEXED COLUMNS SIZE 254');

(2) c2 column has (HEIGHT BALANCED) histogram.

select column_name, histogram from user_tab_columns where table_name='T1'
order by column_id;
 
COLUMN_NAME  HISTOGRAM
-----------  ----------------
C1           NONE
C2           HEIGHT BALANCED
DUMMY        NONE

(3) Now, let’s try to test.
The value of all hidden parameters that was related to ACS are default (_optimizer_adaptive_cursor_sharing=true, _optimizer_extended_cursor_sharing_rel=simple). First bind value is 10 and the number of rows is 1, so optimizer decided to index scan. it was good choice.

var b1 number
exec :b1:=10;
 
select /*_ACS1*/
       count(*), max(c1)
from   t1
where  c2=:b1;
 
  COUNT(*)    MAX(C1)
---------- ----------
         1         10
 
select * from table(dbms_xplan.display_cursor);
 
SQL_ID  as9xzr2tdxj5u, child number 0
-------------------------------------------------------------------------------------
| Id  | Operation                    | Name | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |      |       |       |     2 (100)|          |
|   1 |  SORT AGGREGATE              |      |     1 |    17 |            |          |
|   2 |   TABLE ACCESS BY INDEX ROWID| T1   |     1 |    17 |     2   (0)| 00:00:01 |
|*  3 |    INDEX RANGE SCAN          | IDX1 |     1 |       |     1   (0)| 00:00:01 |
-------------------------------------------------------------------------------------

And that cursor was marked IS_BIND_SENSITIVE=’Y’.

select child_number, executions, buffer_gets,
       is_bind_sensitive, is_bind_aware
from   v$sql
where  sql_id='as9xzr2tdxj5u';
 
CHILD_NUMBER EXECUTIONS BUFFER_GETS IS_BIND_SENSITIVE IS_BIND_AWARE
------------ ---------- ----------- ----------------- -------------
           0          1           3 Y                 N

Second bind value is 1001 and the number of rows is 99,000. So, in this case, full scan is better than index scan. But, against my expectations, the optimizer still has been decided to do index scan.
exec :b1:=1001;

 
select /*_ACS1*/
       count(*), max(c1)
from   t1
where  c2=:b1;
 
  COUNT(*)    MAX(C1)
---------- ----------
     99000     100000
 
select * from table(dbms_xplan.display_cursor);
 
SQL_ID  as9xzr2tdxj5u, child number 0
-------------------------------------------------------------------------------------
| Id  | Operation                    | Name | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |      |       |       |     2 (100)|          |
|   1 |  SORT AGGREGATE              |      |     1 |    17 |            |          |
|   2 |   TABLE ACCESS BY INDEX ROWID| T1   |     1 |    17 |     2   (0)| 00:00:01 |
|*  3 |    INDEX RANGE SCAN          | IDX1 |     1 |       |     1   (0)| 00:00:01 |
-------------------------------------------------------------------------------------

If that statement was executed one more time by same bind value (1001), then oracle will create a new child cursor(#1) and the optimizer will do full scan. And then, that cursor was marked IS_BIND_AWARE=’Y’.

select /*_ACS1*/
count(*), max(c1)
from t1
where c2=:b1;
 
 COUNT(*) MAX(C1)
---------- ----------
    99000 100000
 
select * from table(dbms_xplan.display_cursor);
         
SQL_ID  as9xzr2tdxj5u, child number 1
---------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |       |       |  4134 (100)|          |
|   1 |  SORT AGGREGATE    |      |     1 |    17 |            |          |
|*  2 |   TABLE ACCESS FULL| T1   | 99016 |  1643K|  4134   (1)| 00:00:50 |
---------------------------------------------------------------------------
 
select child_number, executions, buffer_gets,
       is_bind_sensitive, is_bind_aware
from   v$sql
where  sql_id='as9xzr2tdxj5u';
 
CHILD_NUMBER EXECUTIONS BUFFER_GETS IS_BIND_SENSITIVE IS_BIND_AWARE
------------ ---------- ----------- ----------------- -------------
           0          2       14356 Y                 N
           1          1       14593 Y                 Y
 
The third bind value is 20 and the number of rows is 1. When we execute that sql statement, oracle creates a new child cursor(#2) and optimizer decided to index scan. From that time, the child cursor (#0) was no longer be used. That was marked IS_SHAERABLE='N'.
exec :b1:=20
 
elect /*_ACS1*/
       count(*), max(c1)
from   t1
where  c2=:b1;
 
select * from table(dbms_xplan.display_cursor);
 
SQL_ID  as9xzr2tdxj5u, child number 2
-------------------------------------------------------------------------------------
| Id  | Operation                    | Name | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |      |       |       |     2 (100)|          |
|   1 |  SORT AGGREGATE              |      |     1 |    17 |            |          |
|   2 |   TABLE ACCESS BY INDEX ROWID| T1   |     1 |    17 |     2   (0)| 00:00:01 |
|*  3 |    INDEX RANGE SCAN          | IDX1 |     1 |       |     1   (0)| 00:00:01 |
-------------------------------------------------------------------------------------
 
select child_number, executions, buffer_gets,
       is_bind_sensitive, is_bind_aware, is_shareable
from   v$sql
where  sql_id='as9xzr2tdxj5u';
 
CHILD_NUMBER EXECUTIONS BUFFER_GETS IS_BIND_SENSITIVE IS_BIND_AWARE IS_SHAREABLE
------------ ---------- ----------- ----------------- ------------- ------------
           0          2       14356 Y                 N             N
           1          1       14593 Y                 Y             Y
           2          1           3 Y                 Y             Y

Written by sean70kim

July 9, 2013 at 1:11 am

Posted in Oracle

Tagged with ,

Leave a comment