DBMS Tuning & My Life

Posts Tagged ‘11g

How to turn on the Adaptive Cursor Sharing at statement level even though, _optim_peek_user_binds=false.

leave a comment »

Although, the Adaptive Cursor Sharing(ACS) is a very useful feature, but, if you experiences some performance problem of bind peeking, maybe you turn off the bind peeking feature(_optim_peek_user_binds=false). In my previous post, I mentioned that if you turn off the bind peeking then the ACS won’t work.

In this case, we use OPT_PARAM and BIND_AWARE hint for using ACS at statement level.

 SQL> show parameter _optim

NAME                                 TYPE                   VALUE
------------------------------------ ---------------------- ------------------------------
_optim_peek_user_binds               boolean                FALSE
_optimizer_adaptive_cursor_sharing   boolean                FALSE
_optimizer_extended_cursor_sharing_r string                 NONE
el


var b1 number
exec :b1:=10;
 
select /*+BIND_AWARE opt_param('_optim_peek_user_binds','true') */
       count(*), max(c1), avg(c1)
from   t1
where  c2=:b1;
 
  COUNT(*)    MAX(C1)    AVG(C1)
---------- ---------- ----------
         1         10         10

select * from table(dbms_xplan.display_cursor);

SQL_ID  2xsn3xundg0yz, 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 |
-------------------------------------------------------------------------------------

select child_number, executions, buffer_gets,
       is_bind_sensitive, is_bind_aware
from   v$sql
where  sql_id='2xsn3xundg0yz';

CHILD_NUMBER EXECUTIONS BUFFER_GETS IS_BIND_SENSITIVE IS_BIND_AWARE
------------ ---------- ----------- ----------------- -------------
           0          1           3 Y                 Y

exec :b1:=1001; 

select /*+BIND_AWARE opt_param('_optim_peek_user_binds','true') */
       count(*), max(c1), avg(c1)
from   t1
where  c2=:b1;
 

  COUNT(*)    MAX(C1)    AVG(C1)
---------- ---------- ----------
     99000     100000    50500.5

 
SQL_ID  2xsn3xundg0yz, 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='2xsn3xundg0yz';

CHILD_NUMBER EXECUTIONS BUFFER_GETS IS_BIND_SENSITIVE IS_BIND_AWARE
------------ ---------- ----------- ----------------- -------------
           0          1           3 Y                 Y
           1          1       14593 Y                 Y

Written by sean70kim

July 9, 2013 at 1:29 am

The behavior of BIND_AWARE hint.

leave a comment »

Oracle supports BIND_AWARE and NO_BIND_AWARE hints that could control the Adaptive Cursor Sharing. From the name of each hint, We could expect the behavior. The test of NO_BIND_AWARE hint is unnecessary. That hint is so clear. But, BIND_AWARE hint isn’t clear to me. So let’s try to test.

BIND_AWARE hint.
For the test, the disable parameter value would be needed.

alter session set "_optimizer_adaptive_cursor_sharing"=false;
alter session set "_optimizer_extended_cursor_sharing_rel"=false;

Next, execute query using hint. The test environment is all same as previous post.
From the test result, we know that BIND_WARE hint works well regardless of the parameter value.

var b1 number
exec :b1:=10; 
 
select /*+BIND_AWARE*/
       count(*), max(c1)
from   t1
where  c2=:b1;
  
  COUNT(*)    MAX(C1)
---------- ----------
         1         10 
 
select * from table(dbms_xplan.display_cursor);

SQL_ID  9avbm7xarm430, 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 |
-------------------------------------------------------------------------------------

select child_number, executions, buffer_gets,
       is_bind_sensitive, is_bind_aware
from   v$sql
where  sql_id='9avbm7xarm430';
CHILD_NUMBER EXECUTIONS BUFFER_GETS IS_BIND_SENSITIVE IS_BIND_AWARE
------------ ---------- ----------- ----------------- -------------
           0          1           3 Y                 Y
    
exec :b1:=1001;
 
select /*+BIND_AWARE*/
       count(*), max(c1)
from   t1
where  c2=:b1;
  COUNT(*)    MAX(C1)
---------- ----------
     99000     100000
  
SQL_ID  9avbm7xarm430, 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='9avbm7xarm430';
 
CHILD_NUMBER EXECUTIONS BUFFER_GETS IS_BIND_SENSITIVE IS_BIND_AWARE
------------ ---------- ----------- ----------------- -------------
           0          1           3 Y                 Y
           1          1       14593 Y                 Y

After the test, I wonder that BIND_AWARE hint can even work, even if “_optim_peek_user_binds”=false? As expected, if “_optim_peek_user_binds”=false then ACS can’t work. Because, if you turn off the feature of bind peeking then the oracle doesn’t peek any bind variable.

alter session set "_optim_peek_user_binds"=false; 
var b1 number
exec :b1:=10;
 
select /*+BIND_AWARE*/
       count(*), max(c1), min(c1)
from   t1
where  c2=:b1;
 
  COUNT(*)    MAX(C1)    MIN(C1)
---------- ---------- ----------
         1         10         10
 
select * from table(dbms_xplan.display_cursor);

SQL_ID  1wx1pmnfj5ajp, child number 0
-------------------------------------------------------------------------------------
| Id  | Operation                    | Name | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |      |       |       |    16 (100)|          |
|   1 |  SORT AGGREGATE              |      |     1 |    17 |            |          |
|   2 |   TABLE ACCESS BY INDEX ROWID| T1   |   100 |  1700 |    16   (0)| 00:00:01 |
|*  3 |    INDEX RANGE SCAN          | IDX1 |   100 |       |     1   (0)| 00:00:01 |
-------------------------------------------------------------------------------------
 
select child_number, executions, buffer_gets,
       is_bind_sensitive, is_bind_aware
from   v$sql
where  sql_id='1wx1pmnfj5ajp';
 
CHILD_NUMBER EXECUTIONS BUFFER_GETS IS_BIND_SENSITIVE IS_BIND_AWARE
------------ ---------- ----------- ----------------- -------------
           0          1           3 N                 N 

Written by sean70kim

July 9, 2013 at 1:19 am

Posted in Oracle

Tagged with , , ,

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 ,

The simple example of unpivot.

leave a comment »

Unpivot provides very conveninent way, when we search on v$sql_shared_cursor view.
When, we want to know the reason of multiple child cursors, we look at the v$sql_shared_cursor view. But, that view isn’t user-friendly. In this case, we can use unpivot feature from oracle 11g like this. The oracle version of test environment is 11.2.0.3.

-- fnd_shared_cursor.sql
 
WITH v1 AS (
          SELECT *
          FROM   v$sql_shared_cursor
          WHERE  sql_id=:sql_id 
     )
SELECT sql_id ,
       child_number ,
       reason_type ,
       result
FROM   v1
unpivot (result FOR reason_type IN
(
-- columns are vary depend on oracle version
"UNBOUND_CURSOR",
"SQL_TYPE_MISMATCH",
"OPTIMIZER_MISMATCH",
"OUTLINE_MISMATCH",
"STATS_ROW_MISMATCH",
"LITERAL_MISMATCH",
"FORCE_HARD_PARSE",
"EXPLAIN_PLAN_CURSOR",
"BUFFERED_DML_MISMATCH",
"PDML_ENV_MISMATCH",
"INST_DRTLD_MISMATCH",
"SLAVE_QC_MISMATCH",
"TYPECHECK_MISMATCH",
"AUTH_CHECK_MISMATCH",
"BIND_MISMATCH",
"DESCRIBE_MISMATCH",
"LANGUAGE_MISMATCH",
"TRANSLATION_MISMATCH",
"BIND_EQUIV_FAILURE",
"INSUFF_PRIVS",
"INSUFF_PRIVS_REM",
"REMOTE_TRANS_MISMATCH",
"LOGMINER_SESSION_MISMATCH",
"INCOMP_LTRL_MISMATCH",
"OVERLAP_TIME_MISMATCH",
"EDITION_MISMATCH",
"MV_QUERY_GEN_MISMATCH",
"USER_BIND_PEEK_MISMATCH",
"TYPCHK_DEP_MISMATCH",
"NO_TRIGGER_MISMATCH",
"FLASHBACK_CURSOR",
"ANYDATA_TRANSFORMATION",
"PDDL_ENV_MISMATCH",
"TOP_LEVEL_RPI_CURSOR",
"DIFFERENT_LONG_LENGTH",
"LOGICAL_STANDBY_APPLY",
"DIFF_CALL_DURN",
"BIND_UACS_DIFF",
"PLSQL_CMP_SWITCHS_DIFF",
"CURSOR_PARTS_MISMATCH",
"STB_OBJECT_MISMATCH",
"CROSSEDITION_TRIGGER_MISMATCH",
"PQ_SLAVE_MISMATCH",
"TOP_LEVEL_DDL_MISMATCH",
"MULTI_PX_MISMATCH",
"BIND_PEEKED_PQ_MISMATCH",
"MV_REWRITE_MISMATCH",
"ROLL_INVALID_MISMATCH",
"OPTIMIZER_MODE_MISMATCH",
"PX_MISMATCH",
"MV_STALEOBJ_MISMATCH",
"FLASHBACK_TABLE_MISMATCH",
"LITREP_COMP_MISMATCH",
"PLSQL_DEBUG",
"LOAD_OPTIMIZER_STATS",
"ACL_MISMATCH",
"FLASHBACK_ARCHIVE_MISMATCH",
"LOCK_USER_SCHEMA_FAILED",
"REMOTE_MAPPING_MISMATCH",
"LOAD_RUNTIME_HEAP_FAILED",
"HASH_MATCH_FAILED",
"PURGED_CURSOR",
"BIND_LENGTH_UPGRADEABLE",
"USE_FEEDBACK_STATS")
)
where result='Y';
 
--Sample Output
SQL_ID        CHILD_NUMBER  REASON_TYPE          RESULT
c9q44tap900dw 1             EXPLAIN_PLAN_CURSOR  Y
c9q44tap900dw 2             EXPLAIN_PLAN_CURSOR  Y

Written by sean70kim

July 9, 2013 at 12:00 am

Posted in Oracle

Tagged with ,

From 11g, you can use DBMS_SPM to influence the execution plan without changing the sql statement.

leave a comment »

Until 10g, oracle provided outline and sql_profile package to influence execution plan without changing sql statement. And from 11g, oracle provide DBMS_SPM.

1) Create a sample table and a index.

SQL> create table t1 as
select '1' col1 ,
       LEVEL col2 ,
       mod(LEVEL , 10000 ) col3 ,
       'dummy' col4
from   dual
connect by level <=100000; 

SQL> create unique index t1_uk on t1 (col1, col2, col3) nologging; 
SQL> create index t1_idx1 on t1 (col3) nologging; 
SQL> exec dbms_stats.gather_table_stats(user,'t1');

2) Execute the bad sql statement.
Oracle optimizer choose T1_UK index, but col2 range is wide. So that is bad choise.

SQL> var b1 char(1);
SQL> var b2 number;
SQL> var b3 number;
SQL> var b4 number
SQL> exec :b1:='1';
SQL> exec :b2:=1
SQL> exec :b3:=50000
SQL> exec :b4:=1000 

SQL> SELECT *
FROM   t1
WHERE  col1=:b1
AND    col2 BETWEEN :b2 AND :b3
AND    col3=:b4;
 
call     count       cpu    elapsed       disk      query    current       rows        mis   Wait Ela
------- ------  -------- ---------- ---------- ---------- ---------- ---------- ---------- ----------
Parse        1      0.01       0.01          0          0          0          0          1       0.00
Exec         1      0.00       0.01          0          0          0          0          0       0.00
Fetch        1      0.06       0.06          0        151          0          5          0       0.00
------- ------  -------- ---------- ---------- ---------- ---------- ---------- ---------- ----------
Total        3      0.07       0.07          0        151          0          5          1       0.00
 
Rows       Row Source Operation
---------- ---------------------------------------------------
         5 FILTER  (cr=151 pr=0 pw=0 time=45586 us)
         5  TABLE ACCESS BY INDEX ROWID T1 (cr=151 pr=0 pw=0 time=45509 us cost=4 size=17 card=1)
         5   INDEX RANGE SCAN T1_UK (cr=146 pr=0 pw=0 time=45307 us cost=3 size=0 card=1) 

3) Find sql_id of bad sql statement.

SQL> SELECT sql_id, plan_hash_value, sql_text from v$sql where sql_text like '%col2 BETWEEN :b2 AND :b3%'
 
SQL_ID         PLAN_HASH_VALUE  SQL_TEXT
-------------- ---------------- ---------------------------------------------
1mkrscbk593ut  555629870        SELECT * FROM   t1 WHERE  col1=:b1 AND    col 

4) Create a sql plan baseline for the bad sql statement.

$ sqlplus "/ as sysdba"
 
SQL> var cnt number;
SQL> exec :cnt:=dbms_spm.load_plans_from_cursor_cache(sql_id=>'1mkrscbk593ut');

SQL> col sql_handle for a20
SQL> col plan_name  for a40

SQL> SELECT sql_handle ,
       plan_name ,
       enabled
FROM   dba_sql_plan_baselines
WHERE  signature IN (
                     SELECT exact_matching_signature
                     FROM   v$sql
                     WHERE  sql_id='1mkrscbk593ut'
       )
   
SQL_HANDLE           PLAN_NAME                                ENABLE
-------------------- ---------------------------------------- ------
SQL_32cd48c2f499da8d SQL_PLAN_35ma8sbu9mqnde4162105           YES 

5) Captured plan is not efficient, so it should be disabled.

 SQL> exec :cnt:=dbms_spm.alter_sql_plan_baseline(sql_handle=>'SQL_32cd48c2f499da8d', -
 plan_name=>'SQL_PLAN_35ma8sbu9mqnde4162105', -
 attribute_name=>'enabled', -
 attribute_value=>'NO');

6) Modify sql statement to use hint & execute it.

SQL> select /*+ index(t1 t1_idx1) */
      * from t1
 where col1=:b1
 and   col2 between :b2 and :b3
 and   col3=:b4
 
 call     count       cpu    elapsed       disk      query    current       rows        mis   Wait Ela
------- ------  -------- ---------- ---------- ---------- ---------- ---------- ---------- ----------
Parse        1      0.01       0.00          0          0          0          0          1       0.00
Exec         1      0.01       0.01          0          0          0          0          0       0.00
Fetch        1      0.00       0.00          0         12          0          5          0       0.00
------- ------  -------- ---------- ---------- ---------- ---------- ---------- ---------- ----------
Total        3      0.02       0.01          0         12          0          5          1       0.00
 
Event waited on                             Count Zero Ela     Elapse   AVG(Ela)   MAX(Ela)   Blocks
---------------------------------------- -------- -------- ---------- ---------- ---------- --------
SQL*Net message to client                       2        0       0.00       0.00       0.00        0
SQL*Net message from client                     2        0       0.00       0.00       0.00        0
 
Rows       Row Source Operation
---------- ---------------------------------------------------
         5 FILTER  (cr=12 pr=0 pw=0 time=739 us)
         5  TABLE ACCESS BY INDEX ROWID T1 (cr=12 pr=0 pw=0 time=670 us cost=11 size=17 card=1)
        10   INDEX RANGE SCAN T1_IDX1 (cr=2 pr=0 pw=0 time=282 us cost=1 size=0 card=10) 

7) Find sql_id and plan_hash_value for hinted sql statement.

SQL> SELECT sql_id, plan_hash_value, sql_text from v$sql where sql_text like '%/*+ index(t1 t1_idx1) */%'
 
SQL_ID         PLAN_HASH_VALUE    SQL_TEXT
-------------- ------------------ ---------------------------------------
6q33rurh1snrg  1633758621         select /*+ index(t1 t1_idx1) */ * from 

8) Change bad sql execution plan to hinted sql execution plan.

SQL> exec :cnt:=dbms_spm.load_plans_from_cursor_cache(sql_id=>'6q33rurh1snrg',-
 plan_hash_value=> 1633758621, -
 sql_handle=>'SQL_32cd48c2f499da8d'); 
 
 *) sql_id => sql_id of hinted sql statement 
 *) plan_hash_value => plan_hash_value of hinted sql statement 
 *) sql_handle => sql_handle of bad sql statement

9) Run bad sql and check the result of execution plan.

SQL> SELECT *
FROM t1
WHERE col1=:b1
AND col2 BETWEEN :b2 AND :b3
AND col3=:b4;

SQL> select * from table(DBMS_XPLAN.DISPLAY_CURSOR('1mkrscbk593ut', null)); 
PLAN_TABLE_OUTPUT
SQL_ID  1mkrscbk593ut, child number 0
-------------------------------------
SELECT * FROM   t1 WHERE  col1=:b1 AND    col2 BETWEEN :b2 AND :b3 AND
  col3=:b4
 
Plan hash value: 555629870 
--------------------------------------------------------------------------------------
| Id  | Operation                    | Name  | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |       |       |       |     4 (100)|          |
|*  1 |  FILTER                      |       |       |       |            |          |
|   2 |   TABLE ACCESS BY INDEX ROWID| T1    |     1 |    17 |     4   (0)| 00:00:01 |
|*  3 |    INDEX RANGE SCAN          | T1_UK |     1 |       |     3   (0)| 00:00:01 |
--------------------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
--------------------------------------------------- 
   1 - filter(TO_NUMBER(:B2)<=TO_NUMBER(:B3))
   3 - access("COL1"=:B1 AND "COL2">=TO_NUMBER(:B2) AND "COL3"=TO_NUMBER(:B4)
              AND "COL2"<=TO_NUMBER(:B3))
       filter("COL3"=TO_NUMBER(:B4))

 SQL_ID  1mkrscbk593ut, child number 1
-------------------------------------
SELECT * FROM   t1 WHERE  col1=:b1 AND    col2 BETWEEN :b2 AND :b3 AND
  col3=:b4
 
Plan hash value: 1633758621 
----------------------------------------------------------------------------------------
| Id  | Operation                    | Name    | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |         |       |       |    11 (100)|          |
|*  1 |  FILTER                      |         |       |       |            |          |
|*  2 |   TABLE ACCESS BY INDEX ROWID| T1      |     5 |    85 |    11   (0)| 00:00:01 |
|*  3 |    INDEX RANGE SCAN          | T1_IDX1 |    10 |       |     1   (0)| 00:00:01 |
----------------------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
--------------------------------------------------- 
   1 - filter(TO_NUMBER(:B2)<=TO_NUMBER(:B3))
   2 - filter(("COL2"<=TO_NUMBER(:B3) AND "COL1"=:B1 AND
              "COL2">=TO_NUMBER(:B2)))
   3 - access("COL3"=TO_NUMBER(:B4))
 
Note
-----
   - SQL plan baseline SQL_PLAN_35ma8sbu9mqnd97e27072 used for this statement

There are two child cursor (cursor number 0 and 1) in the v$sql. And, the execution plan of child cursor #1 is the same with the execution plan of hinted sql.

Written by sean70kim

July 7, 2013 at 1:26 am

Posted in Oracle

Tagged with ,

Identify root cause session using V$WAIT_CHAINS from 11g.

leave a comment »

From 11g, oracle provide V$WAIT_CHAINS dynamic performance view.
It’s very powerful and convenient to use. So, you can easily idenfify root cause session among the many sessions using that view. The contents of V$WAIT_CHAINS view are very similar with the section of the output of ‘hanganalyze 1’. So, you don’t need to anymore execute ‘hanganalyze’ oradebug command. Instead, you just run below query.

SELECT decode( a.blocker_sid , NULL , '<chain id#' ||a.chain_id||'>' ) chain_id,
       RPAD( '+' , LEVEL , '-' ) ||a.sid sid,
       RPAD( ' ' , LEVEL , ' ' ) ||a.wait_event_text wait_event
FROM   V$WAIT_CHAINS  a
CONNECT BY PRIOR a.sid=a.blocker_sid
AND    PRIOR a.sess_serial#=a.blocker_sess_serial#
AND    PRIOR a.instance = a.blocker_instance START WITH a.blocker_is_valid='FALSE'
ORDER  BY a.chain_id ,
       LEVEL
/
 
CHAIN_ID             SID        WAIT_EVENT
-------------------- ---------- --------------------------------------------------
<chain id#1>         +190        SQL*Net message from client
                     +-4          enq: TX - row lock contention
<chain id#2>         +221        SQL*Net message from client
                     +-108        enq: TX - row lock contention
                     +--8          enq: TX - row lock contention
<chain id#3>         +67         SQL*Net message from client
                     +-155        enq: TX - row lock contention
                     +--146        enq: TX - row lock contention
                     +-156        enq: TX - row lock contention 
 

Written by sean70kim

July 7, 2013 at 1:16 am

Posted in Oracle

Tagged with ,

About partition incremental statistics in 11g

leave a comment »

If you want to effectively manage global statistics of partitioned table, partition incremental statistics is good for you.

Partition incremental statistics (PIS) was introduced in 11g to improve the performance of gathering statistics on large partitioned table. Actually, Until 11g, many DBA hardly ever gather global statistics on partitioned table, if that table is very large. But, in some cases, the management of global statistics is very important in terms of performance. So, oracle introduced PIS. If you use PIS, oracle doesn’t gather global statistics via full table scan. Oracle gathers global statistics via partition full scan.
From a performance point, this is a very good idea. Let’s check on.

(1) Create partitioned table, data loading into p1_1, p1_2 and gather global statistics.

drop table p1;
 
create table p1
(
 c1 date,
 c2 number,
 dummy char(100)
)
partition by range (c1)
(
  partition p1_1 values less than (to_date('20130331','YYYYMMDD')),
  partition p1_2 values less than (to_date('20130430','YYYYMMDD')),
  partition p1_3 values less than (to_date('20130531','YYYYMMDD'))  
);
 
insert  into p1
select  to_date('20130301','YYYYMMDD'),
        level,
        'dummy1'
from    dual
connect by level<=100000;
 
insert  into p1
select  to_date('20130401','YYYYMMDD'),
        level,
        'dummy1'
from    dual
connect by level<=100000;
 
commit;
 
exec dbms_stats.gather_table_stats(user, tabname=>'P1', granularity=>'GLOBAL AND PARTITION');

(2) Data loading into p1_3 and gather global statistics without PIS.

insert into p1
select to_date('20130501','YYYYMMDD'),
       decode(mod(level,2),0,200000,999999),
       'dummy1'
from   dual
connect by level<=100000;
 
commit;
 
alter session set sql_trace=true;
 
exec dbms_stats.gather_table_stats(user, tabname=>'P1', granularity=>'GLOBAL AND PARTITION');
 
alter session set sql_trace=false;

Below is the result of sql trace. As you can see, oracle execute two sql statement. first one is gathered for partition statistics. and second one is gathered for global statistics.

select /*+  full(t)    no_parallel(t) no_parallel_index(t) dbms_stats
  cursor_sharing_exact use_weak_name_resl dynamic_sampling(0) no_monitoring
  no_substrb_pad  */to_char(count("C1")),to_char(substrb(dump(min("C1"),16,0,
  32),1,120)),to_char(substrb(dump(max("C1"),16,0,32),1,120)),
  to_char(count("C2")),to_char(substrb(dump(min("C2"),16,0,32),1,120)),
  to_char(substrb(dump(max("C2"),16,0,32),1,120)),to_char(count("DUMMY")),
  to_char(substrb(dump(min("DUMMY"),16,0,32),1,120)),
  to_char(substrb(dump(max("DUMMY"),16,0,32),1,120))
from
 "SCOTT"."P1" t  where TBL$OR$IDX$PART$NUM("SCOTT"."P1",0,4,0,"ROWID") =:objn 

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        3      0.00       0.00          0          0          0           0
Execute      3      0.01       0.00          0          0          0           0
Fetch        3      3.64       5.35       4265       6490          3           3
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        9      3.65       5.36       4265       6490          3           3
Rows (1st) Rows (avg) Rows (max)  Row Source Operation
---------- ---------- ----------  ---------------------------------------------------
         1          1          1  SORT AGGREGATE
    100000      66667     100000   APPROXIMATE NDV AGGREGATE
    100000     100000     100000    PARTITION RANGE SINGLE PARTITION: KEY KEY
    100000     100000     100000     TABLE ACCESS FULL P1 PARTITION: KEY KEY
 
 
select /*+  full(t)    no_parallel(t) no_parallel_index(t) dbms_stats
  cursor_sharing_exact use_weak_name_resl dynamic_sampling(0) no_monitoring
  no_substrb_pad  */to_char(count("C1")),to_char(substrb(dump(min("C1"),16,0,
  32),1,120)),to_char(substrb(dump(max("C1"),16,0,32),1,120)),
  to_char(count("C2")),to_char(substrb(dump(min("C2"),16,0,32),1,120)),
  to_char(substrb(dump(max("C2"),16,0,32),1,120)),to_char(count("DUMMY")),
  to_char(substrb(dump(min("DUMMY"),16,0,32),1,120)),
  to_char(substrb(dump(max("DUMMY"),16,0,32),1,120))
from
 "SCOTT"."P1" t  /* NDV,NIL,NIL,NDV,NIL,NIL,NDV,NIL,NIL*/

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.01       0.01          0          0          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch        1      3.91       4.04       4246       5340          3           1
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        3      3.92       4.05       4246       5340          3           1

Rows (1st) Rows (avg) Rows (max)  Row Source Operation
---------- ---------- ----------  ---------------------------------------------------
         1          1          1  SORT AGGREGATE
    287397     287397     287397   APPROXIMATE NDV AGGREGATE
    300000     300000     300000    PARTITION RANGE ALL PARTITION: 1 3
    300000     300000     300000     TABLE ACCESS FULL P1 PARTITION: 1 3

(3) Repeat step(1), and apply PIS to the table and check it.

-- repeat step(1)
exec dbms_stats.set_table_prefs(user, tabname=>'P1', pname=>'INCREMENTAL', pvalue=>'TRUE');
 
SQL> select dbms_stats.get_prefs('INCREMENTAL','SCOTT', 'P1') from dual;
DBMS_STATS.GET_PREFS('INCREMENTAL','SCOTT','P1')
--------------------------------------------------------------------------------
TRUE

Repeat step(2). In this case, oracle use PIS. Below is the result of sql trace. As you can see, there is no sql statement to gather global statistics, because, oracle can calculate global statistics using p1_3 partition statistics. For that, oracle stores the synopsis for each partition in SYSAUX. The “SYN” in the sql statement stands for synopsis.

select /*+  full(t)    no_parallel(t) no_parallel_index(t) dbms_stats
  cursor_sharing_exact use_weak_name_resl dynamic_sampling(0) no_monitoring
  no_substrb_pad  */to_char(count("C1")),to_char(substrb(dump(min("C1"),16,0,
  32),1,120)),to_char(substrb(dump(max("C1"),16,0,32),1,120)),
  to_char(count("C2")),to_char(substrb(dump(min("C2"),16,0,32),1,120)),
  to_char(substrb(dump(max("C2"),16,0,32),1,120)),to_char(count("DUMMY")),
  to_char(substrb(dump(min("DUMMY"),16,0,32),1,120)),
  to_char(substrb(dump(max("DUMMY"),16,0,32),1,120))
from
 "SCOTT"."P1" t  where TBL$OR$IDX$PART$NUM("SCOTT"."P1",0,4,0,"ROWID") =:objn 

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        3      0.00       0.00          0          0          0           0
Execute      3      0.01       0.00          0          0          0           0
Fetch        3      3.52       4.06       4412       6447          3           3
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        9      3.53       4.08       4412       6447          3           3

Rows (1st) Rows (avg) Rows (max)  Row Source Operation
---------- ---------- ----------  ---------------------------------------------------
         1          1          1  SORT AGGREGATE 
         2      66667     100000   APPROXIMATE NDV AGGREGATE 
    100000     100000     100000    PARTITION RANGE SINGLE PARTITION: KEY KEY 
    100000     100000     100000     TABLE ACCESS FULL P1 PARTITION: KEY KEY  

Written by sean70kim

July 5, 2013 at 12:55 am

Posted in Oracle

Tagged with ,

V$SQL_MONITOR displays bind values.

leave a comment »

What are the bind values of a long running query?

When you’re monitoring a long running query, you have to know the bind values of that query. Because, depending on the bind values, the performance of query is very different. For example:

    1. between :b1 and :b2

    -> If the range of :b1 and :b2 is wide, in some cases, Full Table Scan is better than Index Range Scan.

    2. col1 LIKE :b1||’%’

    -> If that query is slow, we have to check the contents of :b1. Maybe :b1 is NULL or a very short character (e.g ‘A’).

    3. col1 = :b1

    -> If col1 column has an index and that query is slow, we have to check column suppressing.

    and so on.

Until 10g, to find the bind values, we dump the process state by oradebug, as follows:

$ sqlplus "/ as sysdba"
SQL> oradebug setospid
SQL> oradebug dump processstate 10
SQL> oradebug tracefile_name

And then, search the “Bind#” in the trace file for that cursor. But, sometimes the bind values doesn’t exist in the tracefile. Morever, if the bind values are exist, It’s a little difficult to find the bind values.
Of course, from oracle 10g, oracle provides V$SQL_BIND_CAPTURE view. This view displays information on bind variables used by cursors. But, bind values aren’t always captured for this view.

To overcome this limiation, oracle 11g provide bind values by V$SQL_MONITOR view. This view displays SQL cursors which execution have been (or are being) monitored. SQL monitoring is automatically started when a SQL cursor runs parallel or when it has consumed at least 5 seconds of CPU or I/O time ( This is controlled by “_sqlmon_threshold”). Also, the view provide bind values in “BIND_XML” column. So, you can find the bind values as follows:

SELECT bv.name  name ,
       bv.pos   position,
       bv.type  type,
       bv.value value
FROM   v$sql_monitor s ,
       xmltable( '/binds/bind'
                  passing xmltype( s.binds_xml )
                  COLUMNS name  VARCHAR2( 30 )   path '@name' ,
                          pos   NUMBER           path '@pos',
                          type  VARCHAR2( 15 )   path '@dtystr' ,
                          value VARCHAR2( 4000 ) path '.'
               ) bv
WHERE  s.sql_id=&sql_id 
AND    s.sid=&sid
ORDER  BY bv.pos;
 
 
 
NAME POSITION TYPE VALUE
:B1 1 VARCHAR2(4000) 1
:B2 2 VARCHAR2(4000) 10000001
:B3 3 VARCHAR2(4000) 20000001 

Written by sean70kim

July 4, 2013 at 9:23 am

Posted in Oracle

Tagged with ,

DBMS_SQLTUNE.REPORT_SQL_MONITOR provide very convenient way to analyze the performance of Parallel Query.

leave a comment »

Until 10g, analyzing the performance of parallel query isn’t easy work. For monitoring PQ, we usually use v$px_session, v$px_sesstat, v$session and so on. But, that views support only real time statistics. So, when we need to analyze the whole sql statstiics, we have to use 10046 trace. But, if we used 10046 trace, there will be many trace files on the dump destination. So, we could hardly analyze them. Recently my co-worker suggested dbms_sqltune package to analyze the PQ in 11g. That’s very amazing. We can analyze the PQ with GUI. The usage is very simple.

(1) Find SID or SQL_ID for running sessions

(2) We can make report using dbms_sqltune.report_sql_monitor.
‘A’ option is for ‘Active-X’ type output. ‘H’ option is for ‘HTML’ type output.

 
 set trim on
 set pagesize 0
 set linesize 1000
 set long 1000000
 set longchunksize 10000000
 set serveroutput off
 set heading off
 set feedback off
 set echo off
 SET TRIMSPOOL off
 set verify off
 set timing off
 column file_str new_value file_str;
 column rtype new_value rtype; 

 select decode(upper('&2'), 'T', 'TEXT', 'H', 'HTML', 'A', 'ACTIVE', 'TEXT') rtype from dual; 
 
 select to_char(sysdate, 'yyyymmdd')||'_'|| '&1' || decode(upper('&2'), 'T', '_T.out', 'H', '_H.html', 'A', '_A.html', '.out')file_str from dual;
 spool &file_str

 --for SQL
 --select dbms_sqltune.report_sql_monitor( sql_id => '&1', report_level=>'ALL', type=> '&rtype' ) from dual;
 
 --for SESSION
 select dbms_sqltune.report_sql_monitor( session_id => '&1', report_level=>'ALL', type=> '&rtype' ) from dual;

 spool end
 set heading on
 set feedback on
 set verify on
 set timing on 

Bellow Pictures are the output of dbms_sqltune.report_sql_monitor. It’s beautiful. The first picture is the output of HTML type. Others are the output of ACTIVE-X type.

rs01

rs02

rs03

rs04

Written by sean70kim

July 4, 2013 at 8:49 am