SQL_TRACE陷阱

April 4th, 2007 | Categories: Boring | Tags:

Explain plan有陷阱,有时候Explain plan看到的执行计划和实际的执行计划不一样。SQL_TRACE也有陷阱,tkprof看到的执行计划和应用程序实际执行的计划不一样。

先观察为什么SQL_TRACE可能产生不一致的执行计划。


SQL> create table t as select * from dba_objects order by object_name;
Table created.

SQL> create index tidxon_objectid on t(object_id);
Index created.

SQL> create index tidxon_created on t(created);
Index created.

SQL> analyze table t compute statistics;
Table analyzed.

SQL> alter session set optimizer_mode=choose;
Session altered.

SQL> var a number;
SQL> var b number;
SQL> exec :a:=1000;
PL/SQL procedure successfully completed.

SQL> exec :b:=1100;
PL/SQL procedure successfully completed.

SQL> var c varchar2(20)
SQL> exec :c:=to_char(sysdate+500,’yyyy-mm-dd’);
PL/SQL procedure successfully completed.

alter session set hash_join_enabled=false;

SQL> select object_id,created from t where object_id>=:a and object_id<=:b and created > to_date(:c,’yyyy-mm-dd’) +100;
no rows selected

执行几次;从v$sql_plan中找到执行计划

PLAN_TABLE_OUTPUT
——————————————————————————–
| Id  | Operation                    |  Name           | Rows  | Bytes | Cost  |
——————————————————————————–
|   0 | SELECT STATEMENT             |                 |       |       |    11 |
|*  1 |  FILTER                      |                 |       |       |       |
|*  2 |   TABLE ACCESS BY INDEX ROWID| T               |     4 |    44 |    11 |
|*  3 |    INDEX RANGE SCAN          | TIDXON_CREATED  |    76 |       |     2 |
——————————————————————————–
Predicate Information (identified by operation id):
—————————————————
1 - filter(:A<=:B)
2 - filter(”OBJECT_ID”>=:A AND “OBJECT_ID”<=:B)
3 - access(”CREATED”>TO_DATE(:C,’yyyy-mm-dd’)+100)
Note: cpu costing is off

为什么会使用TIDXON_CREATED,而不使用tidxon_objectid ?是因为
NAME                    VALUE
——————— ——–
_optim_peek_user_binds     TRUE

观察该SQL的统计信息:分析4次,执行4次
SQL> select hash_value,sql_text,OPEN_VERSIONS,LOADED_VERSIONS,
USERS_OPENING,EXECUTIONS,LOADS,FIRST_LOAD_TIME,PARSE_CALLS from v$sql where hash_value=3905077835;
HASH_VALUE
———-
SQL_TEXT
————————————————————————————————————————————
OPEN_VERSIONS LOADED_VERSIONS USERS_OPENING EXECUTIONS      LOADS FIRST_LOAD_TIME
————- ————— ————- ———- ———- ———————————————————
PARSE_CALLS
———–
3905077835
select * from t where object_id>=:a and object_id<=:b and created > to_date(:c,’yyyy-mm-dd’) +100
0               1             0          4          1 2007-04-04/01:29:56
4

SQL> select * from t where object_id>=:a and object_id<=:b and created > to_date(:c,’yyyy-mm-dd’) +100;

观察该SQL的统计信息:分析5次,执行5次
SQL>  select hash_value,sql_text,OPEN_VERSIONS,LOADED_VERSIONS,USERS_OPENING,EXECUTIONS,LOADS,FIRST_LOAD_TIME,PARSE_CALLS
from v$sql where hash_value=3905077835;

HASH_VALUE
———-
SQL_TEXT
————————————————————————————————————————————
OPEN_VERSIONS LOADED_VERSIONS USERS_OPENING EXECUTIONS      LOADS FIRST_LOAD_TIME
————- ————— ————- ———- ———- ———————————————————
PARSE_CALLS
———–
3905077835
select * from t where object_id>=:a and object_id<=:b and created > to_date(:c,’yyyy-mm-dd’) +100
0               1             0          5          1 2007-04-04/01:29:56
5

关键在这里,设置sql_trace

SQL> alter session set sql_trace=true;
Session altered.

执行2次
SQL> exec :a:=1000;

SQL> exec :b:=1001;

SQL> exec :c:=to_char(sysdate-100,’yyyy-mm-dd’);

SQL> select * from t where object_id>=:a and object_id<=:b and created > to_date(:c,’yyyy-mm-dd’) +100;

SQL> select * from t where object_id>=:a and object_id<=:b and created > to_date(:c,’yyyy-mm-dd’) +100;

SQL>alter session set sql_trace=false;
Session altered.

关闭SQL_TRACE,再察看统计信息

SQL> select hash_value,sql_text,OPEN_VERSIONS,LOADED_VERSIONS,USERS_OPENING,EXECUTIONS,LOADS,FIRST_LOAD_TIME,PARSE_CALLS from v$sql where hash_value=3905077835;

HASH_VALUE
———-
SQL_TEXT
————————————————————————————————————————————
OPEN_VERSIONS LOADED_VERSIONS USERS_OPENING EXECUTIONS      LOADS FIRST_LOAD_TIME
————- ————— ————- ———- ———- ———————————————————
PARSE_CALLS
———–
3905077835
select * from t where object_id>=:a and object_id<=:b and created > to_date(:c,’yyyy-mm-dd’) +100
0               1             0          5          1 2007-04-04/01:29:56
5

3905077835
select * from t where object_id>=:a and object_id<=:b and created > to_date(:c,’yyyy-mm-dd’) +100
0               1             0          2          1 2007-04-04/01:29:56
2

会发现该SQL有2个Child了,新Child分析了2次,执行了2次;这说明当trace sql的时候,自动会新生成一个Child SQL;sql trace为这个新的Child SQL
产生了下面的跟踪文件。 Misses parse = 1 表示该Child SQL作了硬分析,就意味着该Child SQL执行计划有可能会发生改变。

********************************************************************************
select *
from
t where object_id>=:a and object_id<=:b and created > to_date(:c,
‘yyyy-mm-dd’) +100

call     count       cpu    elapsed       disk      query    current        rows
——- ——  ——– ———- ———- ———- ———-  ———-
Parse        2      0.00       0.00          0          0          0           0
Execute      2      0.00       0.00          0          0          0           0
Fetch        4      0.01       0.00          0         10          0           4
——- ——  ——– ———- ———- ———- ———-  ———-
total        8      0.01       0.00          0         10          0           4
Misses in library cache during parse: 1
Optimizer goal: CHOOSE
Parsing user id: 31
Rows     Row Source Operation
——-  —————————————————
2  FILTER
2   TABLE ACCESS BY INDEX ROWID T
2    INDEX RANGE SCAN TIDXON_OBJECTID (object id 12293)
********************************************************************************

这里Child #2使用的是TIDXON_OBJECTID,而不是 Child #1的TIDXON_CREATED。这就是sql_trace的陷阱。

为什么会不同,因为_optim_peek_user_binds=TRUE

在次执行

SQL> select * from t where object_id>=:a and object_id<=:b and created > to_date(:c,’yyyy-mm-dd’) +100;

发现Child #1 的统计信息变化了,Child #2无变化。

SQL> select hash_value,sql_text,OPEN_VERSIONS,LOADED_VERSIONS,USERS_OPENING,EXECUTIONS,LOADS,FIRST_LOAD_TIME,PARSE_CALLS from v$sql where hash_value=3905077835;

HASH_VALUE
———-
SQL_TEXT
————————————————————————————————————————————
OPEN_VERSIONS LOADED_VERSIONS USERS_OPENING EXECUTIONS      LOADS FIRST_LOAD_TIME
————- ————— ————- ———- ———- ———————————————————
PARSE_CALLS
———–
3905077835
select * from t where object_id>=:a and object_id<=:b and created > to_date(:c,’yyyy-mm-dd’) +100
0               1             0          6          1 2007-04-04/01:29:56
6

3905077835
select * from t where object_id>=:a and object_id<=:b and created > to_date(:c,’yyyy-mm-dd’) +100
0               1             0          2          1 2007-04-04/01:29:56
2

这说明SQL TRACE会影响到游标的共享,因为Hard Parse的原因会产生与实际SQL运行不一样的执行计划。
可以从V$SQL_SHARED_CURSOR中看到SQL Plan不相同的原因。

在此例中,设置_optim_peek_user_binds=false,sql_trace就不会产生陷阱了。

Del.icio.us Google书签 Digg Live Bookmark Technorati Furl Yahoo书签 Facebook 百度搜藏 新浪 ViVi 365Key 网摘 天极网摘 和讯网摘 博拉网 POCO 网摘 饭否 QQ 书签 Digbuzz 我挖网 Mister Wong
  1. ggf0626
    April 4th, 2007 at 18:23
    Quote | #1

    问题是为什么会产生HADR PARSE呢?

  2. 玉面飞龙
    April 4th, 2007 at 21:24
    Quote | #2

    我在9205下测试的,要么是sql trace的feature要么是sql trace的bug.

  3. dbaoracle
    April 5th, 2007 at 10:19
    Quote | #3

    研究的挺深入嘛,学习ing

  4. ynhoo
    April 5th, 2007 at 15:59
    Quote | #4

    从sqlplus里面直接set autotrace on,执行select object_id,created from t where object_id>=:a and object_id to_date(:c,’yyyy-mm-dd’) 100;
    发现用的是’TIDXON_CREATED’,但是在toad中通过monitor检查发现用的是:
    INDEX RANGE SCAN TIDXON_OBJECTID

  5. yumianfeilong
    April 5th, 2007 at 16:43
    Quote | #5

    explain plan和set autotrace on 是不考虑 bind_peeking的,所以说是explain plan陷阱。

    真正的执行计划可以从v$sql_plan里面看到