我们可以通过10046事件看到一个SQL的执行的统计信息,以及执行计划,但是我们只看到了CBO最终告诉我们的执行结果,却并不知道CBO为什么要这么选择,那么就可以通过10053事件来生成SQL分析的整个过程到trace文件中,通俗点讲10053跟踪选路过程,10046产生结果。
关于10046事件参考我的blog:
http://5073392.blog.51cto.com/5063392/1308195
10053包含2个级别:
Level 2:2级是1级的一个子集,它包含以下内容:
Column statistics
Single Access Paths
Join Costs
Table Joins Considered
Join Methods Considered (NL/MS/HA)
Level 1:1级比2级更详细,它包含2级的所有内容,在加如下内容:
Parameters used by the optimizer
Index statistics
产生一个10053的trace文件
SQL> create table t as select rownum id from dba_objects;
Table created.
SQL> create index idx_t on t(id);
Index created.
SQL> exec dbms_stats.gather_table_stats('sys','t',cascade=>true);
PL/SQL procedure successfully completed.
SQL> create table t1 as select id,'T1' name from t where id<1000;
Table created.
SQL> select tracefile from v$process where addr=(select paddr from v$session where sid=(select distinct sid from v$mystat));
TRACEFILE
--------------------------------------------------------------------------------
/u01/app/oracle/diag/rdbms/rbksafari/RBKSAFARI/trace/RBKSAFARI_ora_9558.trc
SQL> alter session set events '10053 trace name context forever, level 1';
Session altered.
SQL> explain plan for select t1.* from t,t1 where t.id<100 and t.id=t1.id;
Explained.
SQL> alter session set events '10053 trace name context off';
Session altered.
10053和10046事件的用法是一样的,需要注意的是这个trace文件不能用tkprof工具处理,tkprof工具只能处理10046和sql_trace文件,下面是刚才生成的trace文件内容
这里从BASE STATISTICAL INFORMATION开始看
***************************************
BASE STATISTICAL INFORMATION
***********************
Table Stats::
Table: T1 Alias: T1 (NOT ANALYZED)
#Rows: 164 #Blks: 2 AvgRowLen: 100.00
***********************
Table Stats::
Table: T Alias: T
#Rows: 73924 #Blks: 113 AvgRowLen: 5.00
Index Stats::
Index: IDX_T Col#: 1
LVLS: 1 #LB: 164 #DK: 73924 LB/K: 1.00 DB/K: 1.00 CLUF: 113.00
Access path analysis for T
***************************************
这一部分是sql语句中用到的对象基本信息,包括关联表和索引信息,我们看到这里列出了关于这个查询的三个对象信息,表T,T1和索引IDX_T,表信息包含了表的行数(#Rows),数据块数(#Blks),平均行长(AvgRowLen)索引项部分列出了索引叶块数(#LB),每个索引键值占据的数据块数(LB/K),每个索引键值对应的表中数据块数(DB/K)索引的聚合因子(CLUF),当索引的键值对应的数据库越大,索引的聚合因子就越大,越不利用索引的使用。
接下来的部分是CBO计算的每个对象单独访问的代价
***************************************
SINGLE TABLE ACCESS PATH
Single Table Cardinality Estimation for T[T]
Table: T Alias: T
Card: Original: 73924.000000 Rounded: 99 Computed: 99.00 Non Adjusted: 99.00
Access Path: TableScan
Cost: 32.49 Resp: 32.49 Degree: 0
Cost_io: 32.00 Cost_cpu: 15589523
Resp_io: 32.00 Resp_cpu: 15589523
Access Path: index (index (FFS))
Index: IDX_T
resc_io: 46.00 resc_cpu: 13734996
ix_sel: 0.000000 ix_sel_with_filters: 1.000000
Access Path: index (FFS)
Cost: 46.43 Resp: 46.43 Degree: 1
Cost_io: 46.00 Cost_cpu: 13734996
Resp_io: 46.00 Resp_cpu: 13734996
Access Path: index (IndexOnly)
Index: IDX_T
resc_io: 2.00 resc_cpu: 34243
ix_sel: 0.001339 ix_sel_with_filters: 0.001339
Cost: 2.00 Resp: 2.00 Degree: 1
Best:: AccessPath: IndexRange
Index: IDX_T
Cost: 2.00 Degree: 1 Resp: 2.00 Card: 99.00 Bytes: 0
Access path analysis for T1
***************************************
这里有两个指标对于我们分析执行计划比较重要:
Card: Original: 73924.000000
原纪录数,也就是操作数据原的输入记录数,在这里就是表的实际行数73294
Card: Rounded: 99
输出的记录数,CBO计算出通过条件过滤,预计得到的行数99
通过这里我们可以看出对于T表CBO给我们计算出了3种访问方式
全表扫描
Access Path: TableScan 开销:Cost: 32.49
索引快速扫描
Access Path: index (index (FFS)) 开销:Cost: 46.43
单独访问索引
Access Path: index (IndexOnly) 开销:Cost: 2.00
可以看出,单独访问索引的方式是代价最低的,因此CBO得出下来结论
Best:: AccessPath: IndexRange
Index: IDX_T
Cost: 2.00 Degree: 1 Resp: 2.00 Card: 99.00 Bytes: 0
这里我不明白上面写的是IndexOnly,为什么最后结论改写成IndexRange。为何oracle不直接将Access Path写成IndexRange
下面是对T1表访问方式的描述
Table: T1 Alias: T1
Card: Original: 999.000000 Rounded: 99 Computed: 99.00 Non Adjusted: 99.00
Access Path: TableScan
Cost: 2.01 Resp: 2.01 Degree: 0
Cost_io: 2.00 Cost_cpu: 216023
Resp_io: 2.00 Resp_cpu: 216023
Best:: AccessPath: TableScan
Cost: 2.01 Degree: 1 Resp: 2.01 Card: 99.00 Bytes: 0
由于我们没有在T1上创建索引因此对于T1表的访问只有TableScan全表扫描一种方式
下面是T与T1表的关联统计最终分析出关联最小的开销作为最终的执行计划
OPTIMIZER STATISTICS AND COMPUTATIONS
***************************************
GENERAL PLANS
***************************************
Considering cardinality-based initial join order.
Permutations for Starting Table :0
Join order[1]: T1[T1]#0 T[T]#1
***************
Now joining: T[T]#1
***************
NL Join (一)
Outer table: Card: 99.00 Cost: 2.01 Resp: 2.01 Degree: 1 Bytes: 17
Access path analysis for T
Inner table: T Alias: T
Access Path: TableScan
NL Join: Cost: 3082.41 Resp: 3082.41 Degree: 1
Cost_io: 3034.00 Cost_cpu: 1543578772
Resp_io: 3034.00 Resp_cpu: 1543578772
Access Path: index (index (FFS))
Index: IDX_T
resc_io: 44.43 resc_cpu: 13734996
ix_sel: 0.000000 ix_sel_with_filters: 1.000000
Inner table: T Alias: T
Access Path: index (FFS)
NL Join: Cost: 4443.65 Resp: 4443.65 Degree: 1
Cost_io: 4401.00 Cost_cpu: 1359980643
Resp_io: 4401.00 Resp_cpu: 1359980643
kkofmx: index filter:"T"."ID"<100
Access Path: index (AllEqJoinGuess)
Index: IDX_T
resc_io: 1.00 resc_cpu: 8171
ix_sel: 0.000014 ix_sel_with_filters: 0.000000
***** Logdef predicate Adjustment ******
Final IO cst 0.00 , CPU cst 50.00
***** End Logdef Adjustment ******
NL Join : Cost: 101.03 Resp: 101.03 Degree: 1
Cost_io: 101.00 Cost_cpu: 1029945
Resp_io: 101.00 Resp_cpu: 1029945
Best NL cost: 101.03 --nested loops join 代价是101.03
resc: 101.03 resc_io: 101.00 resc_cpu: 1029945
resp: 101.03 resp_io: 101.00 resc_cpu: 1029945
Join Card: 98.011326 = = outer (99.000000) * inner (99.001339) * sel (0.010000)
Join Card - Rounded: 98 Computed: 98.01
Outer table: T1 Alias: T1
resc: 2.01 card 99.00 bytes: 17 deg: 1 resp: 2.01
Inner table: T Alias: T
resc: 2.00 card: 99.00 bytes: 5 deg: 1 resp: 2.00
using dmeth: 2 #groups: 1
SORT ressource Sort statistics
Sort width: 179 Area size: 157696 Max Area size: 31666176
Degree: 1
Blocks to Sort: 1 Row size: 29 Total Rows: 99
Initial runs: 1 Merge passes: 0 IO Cost / pass: 0
Total IO sort cost: 0 Total CPU sort cost: 31913716
Total Temp space used: 0
SORT ressource Sort statistics
Sort width: 179 Area size: 157696 Max Area size: 31666176
Degree: 1
Blocks to Sort: 1 Row size: 16 Total Rows: 99
Initial runs: 1 Merge passes: 0 IO Cost / pass: 0
Total IO sort cost: 0 Total CPU sort cost: 31913716
Total Temp space used: 0
SM join: Resc: 6.01 Resp: 6.01 [multiMatchCost=0.00]
SM Join (二)
SM cost: 6.01 --Sort merge join 的代价是6.01
resc: 6.01 resc_io: 4.00 resc_cpu: 64077698
resp: 6.01 resp_io: 4.00 resp_cpu: 64077698
Outer table: T1 Alias: T1
resc: 2.01 card 99.00 bytes: 17 deg: 1 resp: 2.01
Inner table: T Alias: T
resc: 2.00 card: 99.00 bytes: 5 deg: 1 resp: 2.00
using dmeth: 2 #groups: 1
Cost per ptn: 0.50 #ptns: 1
hash_area: 124 (max=7731) buildfrag: 1 probefrag: 1 ppasses: 1
hash_area: 124 (max=7731) buildfrag: 1 probefrag: 1 ppasses: 1
Hash join: Resc: 4.51 Resp: 4.51 [multiMatchCost=0.00]
HA Join (三)
HA cost: 4.51 --Hash join的代价是4.51
resc: 4.51 resc_io: 4.00 resc_cpu: 16217089
resp: 4.51 resp_io: 4.00 resp_cpu: 16217089
Best:: JoinMethod: Hash --第一种关联花费最小的是Hash join
Cost: 4.51 Degree: 1 Resp: 4.51 Card: 98.01 Bytes: 22
***********************
Best so far: Table#: 0 cost: 2.0068 card: 99.0000 bytes: 1683
Table#: 1 cost: 4.5086 card: 98.0113 bytes: 2156
***********************
Join order[2]: T[T]#1 T1[T1]#0
***************
Now joining: T1[T1]#0
***************
NL Join (一)
Outer table: Card: 99.00 Cost: 2.00 Resp: 2.00 Degree: 1 Bytes: 5
Access path analysis for T1
Inner table: T1 Alias: T1
Access Path: TableScan
NL Join: Cost: 57.67 Resp: 57.67 Degree: 1
Cost_io: 57.00 Cost_cpu: 21420508
Resp_io: 57.00 Resp_cpu: 21420508
Best NL cost: 57.67
resc: 57.67 resc_io: 57.00 resc_cpu: 21420508
resp: 57.67 resp_io: 57.00 resc_cpu: 21420508
Join Card: 98.011326 = = outer (99.001339) * inner (99.000000) * sel (0.010000)
Join Card - Rounded: 98 Computed: 98.01
Outer table: T Alias: T
resc: 2.00 card 99.00 bytes: 5 deg: 1 resp: 2.00
Inner table: T1 Alias: T1
resc: 2.01 card: 99.00 bytes: 17 deg: 1 resp: 2.01
using dmeth: 2 #groups: 1
SORT ressource Sort statistics
Sort width: 179 Area size: 157696 Max Area size: 31666176
Degree: 1
Blocks to Sort: 1 Row size: 29 Total Rows: 99
Initial runs: 1 Merge passes: 0 IO Cost / pass: 0
Total IO sort cost: 0 Total CPU sort cost: 31913716
Total Temp space used: 0
SM join: Resc: 5.01 Resp: 5.01 [multiMatchCost=0.00]
SM Join (二)
SM cost: 5.01
resc: 5.01 resc_io: 4.00 resc_cpu: 32163982
resp: 5.01 resp_io: 4.00 resp_cpu: 32163982
Outer table: T Alias: T
resc: 2.00 card 99.00 bytes: 5 deg: 1 resp: 2.00
Inner table: T1 Alias: T1
resc: 2.01 card: 99.00 bytes: 17 deg: 1 resp: 2.01
using dmeth: 2 #groups: 1
Cost per ptn: 0.50 #ptns: 1
hash_area: 124 (max=7731) buildfrag: 1 probefrag: 1 ppasses: 1
Hash join: Resc: 4.51 Resp: 4.51 [multiMatchCost=0.00]
HA Join (三)
HA cost: 4.51
resc: 4.51 resc_io: 4.00 resc_cpu: 16217089
resp: 4.51 resp_io: 4.00 resp_cpu: 16217089
Join order aborted: cost > best plan cost
***********************
2中关联方式统计如下:
T1关联T
nested join: 101.03 resc_cpu: 1029945
sort merge join:6.01 resc_cpu: 64077698
Hash join:4.51 resc_cpu: 16217089
T关联T1
nested join:57.67resc_cpu: 21420508
nested join:5.01resc_cpu: 32163982
nested join:4.51resc_cpu: 16217089
下面是最终关联计算开销的结果Best join order: 1,从上面的结果看出2种关联中最优的都是Hash join,碰巧这个两个hash join开销一样,oracle选择第一种关联方式
Number of join permutations tried: 2
*********************************
Consider using bloom filter between T1[T1] and T[T]
kkoBloomFilter: join (lcdn:99 rcdn:99 jcdn:98 limit:4901)
Computing bloom ndv for creator:T1[T1] ccdn:99.0 and user:T[T] ucdn:99.0
kkopqComputeBloomNdv: predicate (bndv:73924 ndv:100) and (bndv:999 ndv:99)
kkopqComputeBloomNdv: pred cnt:2 ndv:99 reduction:1
kkoBloomFilter: join ndv:0 reduction:0.999986 (limit:0.500000) rejected because distinct value ratio
(newjo-save) [0 1 ]
Trying or-Expansion on query block SEL$1 (#0)
Transfer Optimizer annotations for query block SEL$1 (#0)
id=0 frofand predicate="T1"."ID"<100
id=0 frofkksm[i] (sort-merge/hash) predicate="T"."ID"="T1"."ID"
id=0 frosand (sort-merge/hash) predicate="T"."ID"="T1"."ID"
id=0 frofkke[i] (index stop key) predicate="T"."ID"<100
Final cost for query block SEL$1 (#0) - All Rows Plan:
Best join order: 1
Cost: 4.5086 Degree: 1 Card: 98.0000 Bytes: 2156
Resc: 4.5086 Resc_io: 4.0000 Resc_cpu: 16217089
Resp: 4.5086 Resp_io: 4.0000 Resc_cpu: 16217089
kkoqbc-subheap (delete addr=0x7ff58e2dfa50, in-use=29224, alloc=41296)
kkoqbc-end:
:
call(in-use=19752, alloc=82024), compile(in-use=67328, alloc=68488), execution(in-use=89616, alloc=93504)
kkoqbc: finish optimizing query block SEL$1 (#0)
下面是最终算出的执行计划结果
============
Plan Table
============
--------------------------------------+-----------------------------------+
| Id | Operation | Name | Rows | Bytes | Cost | Time |
--------------------------------------+-----------------------------------+
| 0 | SELECT STATEMENT | | | | 5 | |
| 1 | HASH JOIN | | 98 | 2156 | 5 | 00:00:01 |
| 2 | TABLE ACCESS FULL | T1 | 99 | 1683 | 2 | 00:00:01 |
| 3 | INDEX RANGE SCAN | IDX_T | 99 | 495 | 2 | 00:00:01 |
--------------------------------------+-----------------------------------+
Predicate Information:
----------------------
1 - access("T"."ID"="T1"."ID")
2 - filter("T1"."ID"<100)
3 - access("T"."ID"<100)
注:trace文件头部和尾部省略