oracle explain plan for获取执行计划并不可靠.

本文深入探讨了Oracle数据库中SQL执行计划的准确性问题,特别是当使用EXPLAIN PLAN获取含有BIND变量的SQL语句执行计划时存在的局限性。文章通过具体实例展示了如何计算不同路径的成本,并介绍了几种获取准确执行计划的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

要想调优sql首先需要获得一个准确的执行计划
那么当使用explain plan for 获得含有bind变量的sql语句的执行计划时,是不可靠的
原因:explain plan for不会peeking bind值


具体测试
SQL> select * from v$version where rownum<2;

BANNER
----------------------------------------------------------------
Oracle Database 10g Enterprise Edition Release 10.2.0.4.0 - Prod

SQL> conn xh/a831115
Connected.
SQL> select distinct sid from v$mystat;

       SID
----------
       159


SQL> create table t1  (a int,b int);

Table created.

SQL> SQL>  declare
  2      begin
  3      for i in 1..10000 loop
  4      insert into t1 values(i,i+1);
  5      end loop;
  6      commit;
  7      end;
  8      /


PL/SQL procedure successfully completed.

SQL> SQL> create index t1_ind on t1(a);

Index created.

SQL> execute dbms_stats.gather_table_stats('XH','T1');

PL/SQL procedure successfully completed.


SQL> variable a number
SQL> execute :a:=1

PL/SQL procedure successfully completed.
select * from t1 where a>:a

 

 col sql_text for a40

SQL> select b.sql_id ,b.sql_text,b.hash_value from v$session a ,v$sqlarea b where a.sid=159 and b.hash_value in (a.sql_hash_value,a.prev_hash_value);

SQL_ID        SQL_TEXT                                 HASH_VALUE
------------- ---------------------------------------- ----------
5mu9n4f3fqxtt select * from t1 where a>:a              2263578425


SQL> select * from table(dbms_xplan.display_cursor('5mu9n4f3fqxtt'));

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
SQL_ID  5mu9n4f3fqxtt, child number 0
-------------------------------------
select * from t1 where a>:a

Plan hash value: 3617692013

--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |       |       |     6 (100)|          |
|*  1 |  TABLE ACCESS FULL| T1   |  9999 | 69993 |     6   (0)| 00:00:01 |

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
--------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter("A">:A)


18 rows selected.


可以看到这个sql真正 的plan是走fts

那么当我们调整sql时候 会用到explain获得执行计划

SQL> explain plan for select * from t1 where a>:a  ;

Explained.

SQL> select * from table(dbms_xplan.display);

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 2059591622

--------------------------------------------------------------------------------
------

| Id  | Operation                   | Name   | Rows  | Bytes | Cost (%CPU)| Time
     |

--------------------------------------------------------------------------------
------


PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |        |   500 |  3500 |     3   (0)| 00:0
0:01 |

|   1 |  TABLE ACCESS BY INDEX ROWID| T1     |   500 |  3500 |     3   (0)| 00:0
0:01 |

|*  2 |   INDEX RANGE SCAN          | T1_IND |    90 |       |     2   (0)| 00:0
0:01 |

--------------------------------------------------------------------------------
------

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------


Predicate Information (identified by operation id):
---------------------------------------------------

   2 - access("A">TO_NUMBER(:A))

14 rows selected.


可以看到explain并没peeking,而是通过 计算fts与index访问的cost计算出了 走index的fts,那么走index访问的选择率
(表选择率,索引选择率) 因为是bind所以使用5%,最后走index cost为3 fts为6 选择了走index,但走index并没有实际peeking bind值
所以选择率是不对的,所以执行计划不正确 如果调整这样的sql那结果必定是错误的.


下面是具体走fts cost和走index cost计算(走index 选择率不准确,不能peeking用的是5%)
index
cost =
blevel +
ceiling(leaf_blocks * effective index selectivity) +
ceiling(clustering_factor * effective table selectivity)
可以写成cost=(blevel+(leaf_blocks+clustering_factor )*effective table selectivity)*(optimizer_index_cost_adj /100)


SQL> show parameter index

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
optimizer_index_caching              integer     0
optimizer_index_cost_adj             integer     100

 

SQL> select num_rows from user_tables where table_name='T1';

  NUM_ROWS
----------
     10000

 


SQL>  select column_name,NUM_NULLS,NUM_DISTINCT,density from user_tab_col_statistics where table_name='T1';

COLUMN_NAME                     NUM_NULLS NUM_DISTINCT    DENSITY
------------------------------ ---------- ------------ ----------
A                                       0        10000      .0001
B                                       0        10000      .0001

SQL> select LEAF_BLOCKS,BLEVEL ,clustering_factor from user_indexes where index_name='T1_IND';

LEAF_BLOCKS     BLEVEL CLUSTERING_FACTOR
----------- ---------- -----------------
         21          1                18

io_cost =
select 1+(21+18)*0.05 from dual;

1+(21+18)*0.05
--------------
          2.95
取整不太准确,未用10053 跟踪

走fts时cost计算
SQL> select pname,pval1 from aux_stats$ where sname='SYSSTATS_MAIN';

PNAME                               PVAL1
------------------------------ ----------
CPUSPEED
CPUSPEEDNW                     484.974958  speed in millions of operations per second
IOSEEKTIM                              10  disk seek time in milliseconds
IOTFRSPEED                           4096   disk transfer time in bytes per millisecond
MAXTHR
MBRC
MREADTIM
SLAVETHR
SREADTIM

已选择9行。以上没有信息收集 ORACLE 会用DEFAULT 计算************

计算*使用 默认信息**************************
MBRC=db_file_multiblock_read_count=16
sreadtim=IOSEEKTIM+db+block_size/IOTFRSPEED=12
mreadtime=IOSEEKTIM+db_file_multiblock_read_count*db+block_size/IOTFRSPEED=42

MDRS=BLOCKS/MBRC
#SRds =0(single-block reads),因为是 FTS 都是多块读取
最早的公式变换一下(除开)
Cost = (
#SRds +
#MRds * mreadtim / sreadtim +**************io cost
#CPUCycles / (cpuspeed * sreadtim)********cpu cost

SQL> select 20/16*42/12 from dual;

20/16*42/12
-----------
      4.375

4.375=5 由于
_optimizer_ceil_cost           TRUE                      TRUE      FALSE      FALSE

CEIL cost in CBO~~~~~~~~~~取整

SQL> select ceil(20/16*42/12) from dual;

CEIL(20/16*42/12)
-----------------
                5~~~~~~~~~~~~~~~~~~~~~~~~~~~IO 部分IO COST

又由于 _table_scan_cost_plus_one           = true   加1 所以IO COST=6
true时候COST+1 表示开始需要访问表的SEGMENT HEADER BLOCK,这是优化器的内置技巧之一(避免小表中用INDEX)
********oracle cost-based oracle原文描述 _table_scan_cost_plus_one
This is the type of minor tweak that appears surprisingly frequently in Oracle, and makes
it very difficult to produce any sort of documentation about the optimizer that is both concise
and complete. In this case, the change may represent the requirement for the necessary access
to the table’s segment header block at the start of a tablescan; alternatively it may simply be
one of several tricks built into the optimizer to bias it towards using indexed access paths on
very small tables.
***********
CPU 部分 转换成IO COST 单位

#CPUCycles / (cpuspeed * sreadtim)
#CPUCycles:1642429
cpuspeed default 使用 CPUSPEEDNW
sreadtime:12(milliseconds)=12000(microseconds)
 
SQL> select 1642429/(484*12000) from dual;

1642429/(484*12000)
-------------------
         .282787362

 

那么如何才能获取准确的执行计划10g 可以从library cache中直接获取 使用dbms_xplan.display_cursor (AWR)
9i可以用sql_trace ,10046,或直接从v$sql_plan中获取准确的执行计划

 

SQL>   create table PLAN_TABLE2 (
  2          statement_id       varchar2(30),
  3          plan_id            number,
  4          timestamp          date,
  5          remarks            varchar2(4000),
  6          operation          varchar2(30),
  7          options            varchar2(255),
  8          object_node        varchar2(128),
  9          object_owner       varchar2(30),
 10          object_name        varchar2(30),
 11          object_alias       varchar2(65),
 12          object_instance    numeric,
 13          object_type        varchar2(30),
 14          optimizer          varchar2(255),
 15          search_columns     number,
 16          id                 numeric,
 17          parent_id          numeric,
 18          depth              numeric,
 19          position           numeric,
 20          cost               numeric,
 21          cardinality        numeric,
 22          bytes              numeric,
 23          other_tag          varchar2(255),
 24          partition_start    varchar2(255),
 25          partition_stop     varchar2(255),
 26          partition_id       numeric,
 27          other              long,
 28          distribution       varchar2(30),
 29          cpu_cost           numeric,
 30          io_cost            numeric,
 31          temp_space         numeric,
 32          access_predicates  varchar2(4000),
        filter_predicates  varchar2(4000),
 33   34          projection         varchar2(4000),
 35          time               numeric,
 36          qblock_name        varchar2(30),
 37          other_xml          clob
 38  );

Table created
SQL> select b.sql_id ,b.sql_text,b.hash_value,b.child_number,b.address from v$session a ,v$sql b where a.sid=159 and b.hash_value in (a.sql_hash_value,a.prev_hash_value);

SQL_ID        SQL_TEXT                                 HASH_VALUE CHILD_NUMBER
------------- ---------------------------------------- ---------- ------------
ADDRESS
--------
5mu9n4f3fqxtt select * from t1 where a>:a              2263578425            0
2FA4CF14

 


 INSERT INTO plan_table2 ( operation, options,
                         object_node, object_owner, object_name, optimizer,
                        search_columns, id, parent_id, position, cost,
                           cardinality, bytes, other_tag, partition_start,
                            partition_stop, partition_id, other, distribution,
                          cpu_cost, io_cost, temp_space, access_predicates,
                          filter_predicates)
 SELECT
         operation, options, object_node, object_owner, object_name,
         optimizer, search_columns, id, parent_id, position, cost,
        cardinality, bytes, other_tag, partition_start, partition_stop,
         partition_id, other, distribution, cpu_cost, io_cost, temp_space,
        access_predicates, filter_predicates
  FROM v$sql_plan
   WHERE address = '2FA4CF14'
  AND hash_value = 2263578425
  AND child_number = 0;

 

 

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/12020513/viewspace-626478/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/12020513/viewspace-626478/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值