目录
- 一、关于10053事件的概念
- 二、关于操作系统统计信息的解释
- 三、实战测试1—验证全表扫描的成本计算公式,贴出执行计划和计算公式。
- 四、实战测试2—验证B-tree索引Unique scan的成本计算公式,贴出执行计划和计算公式。
- 五、实验3——通过10053事件分析一个SQL执行计划的产生过程,需要贴出trace中的相关信息和必要的文字说明。
- 四、案例4:——当统计信息不准备确实的比较
Oracle 事件跟踪优化SQL实例之(四)—10053功能
一、关于10053事件的概念
Oracle 10046和10053都是非官方trace sql的方法,在官方文档上是找不到相关资料的,但在MOS上可以找到。sql_trace是官方推荐的trace sql的方法,在官方文档上是可以查询出来的。
1、功能介绍
10053跟踪事件(trace event)是一种用于生成CBO(Cost-Based Optimizer,基于成本的优化器)的详细查询执行计划的跟踪标记。
用来描述Oracle如何选择执行计划的过程,然后输出到trace文件里,共我们参考,因为我们经常看执行计划怎么执行的消耗了哪些资源,而不是常看执行计划怎么选择出来了的。
通过启用10053跟踪事件,可以获得优化器在查询优化过程中所做的各种决策和成本估算,以及生成的最终执行计划。
2、使用场景
当SQL语句执行时走的是错误的执行计划,而又找不到原因时,这时请用10053来分析一下原因。
3、特点
(1)只可以了解oracle执行计划的选择过程
(2)无法获知代价的计算公式,因为这是oracle内部的商业机密,而且每个oracle版本的优化器计算公式都不相同差距还是蛮大的,不同版本的同一个语句的代价也不一样,优化器现在还不是很成熟,还有待完善。
(3)在这个里面我们重点要了解的是“代价”是如何计算出来的,然后我们才能了解执行计划是如何选择的。
(4)在10053中可以了解哪些因素影响sql的执行代价
(5)oracle 8i cost等价IO资源消耗, 9i以后cost等价IO+CPU+网络+等待事件+其他代价
一般IO资源的权重比较大,CPU权重较小。
4、具体内容-10053内容:
--参数区: 初始化参数,隐含参数,这些参数可以左右oracle工作方式 --SQL区: 执行的SQL语句,是否使用绑定变量,是否进行了转换操作 --系统信息区: 操作系统统计信息,cpu主频CPU执行时间IO寻址时间单块读时间多块读时间 --对象统计信息区: --数据访问方式: 访问方式不一样计算代价的方法也不一样,全表扫描走索引多表关联代价都不同 --关联查询: 把每张表都作为驱动表去组合,择优选择“代价”最小的关联方式,与哪个表在前无关系 --代价的最后修正: oracle会对选择出来的代价再进行最后的修正,使其更准确一些,更合理一些 --选择出最终执行计划:这个过程是非常快速的,毫秒级就搞定了
具体内容
10053事件有2个level,1和2,1级比2级内容要详细的多
LEVEL 1:这是10053跟踪事件的基本级别,仅生成最基本的成本估算、查询转化和查询处理信息。这些信息对于初步分析查询性能问题和确定执行计划的基本策略非常有用。
LEVEL 8:这是10053跟踪事件的中等级别,比LEVEL 1级别生成更多的优化器决策信息,例如索引选项、JOIN算法和连接顺序等。这些信息可以帮助进一步了解优化器如何做出决策以及为什么选择某些执行计划。
LEVEL 32:这是10053跟踪事件的高级级别,生成大量的优化器决策信息,包括各种成本估算、推导式、谓词下推等复杂的优化器处理步骤。这些详细的信息可以帮助深入了解查询优化和执行过程中的性能问题。
LEVEL 255:这是10053跟踪事件的最高级别,通常用于进行高级的查询调优和性能优化,生成逐语句级别的跟踪信息,同时会针对每个数据块进行分析,更全面地反映系统的性能瓶颈。
二、关于操作系统统计信息的解释
1、查看Oracle版本
SQL>select * from v$version;
2、关于操作系统统计信息
(1)查看操作系统统计信息
col sname for a20 col pname for a20 col pval1 for 9999999999.9999999 col pval2 for a30 select * from sys.aux_stats$;
(2)针对sys.aut_stats$的说明
aux_stats$是sys管理员用户下的一个基表后缀为$,必须写schema才能查询到。
所谓的基表就是给动态性能视图提供数据的原始表,由于基表非常重要,oracle规定不允许直接访问和修改基表,如果你比较了解这些那么另说了。
这个表中记录了“操作系统统计信息”。Oracle会利用操作系统统计信息来修正执行计划的代价,也就是说这些信息是影响代价计算的因素之一。
注意:
如果oracle收集了操作系统统计信息,那么CBO采用工作量统计模式计算代价;
如果oracle没有收集操作系统统计信息,那么CBO采用非工作量统计模式计算代价,看上面MBRC没有参数值就说明还没有收集操作系统统计信息
这两个模式计算代价的公式是不同的。
(3)sys.aut_stats$的列名解释
SNAME:是指操作系统统计信息 PNAME:parameter name参数名 PVAL1:参数值 PVAL2:参数值
(4)PNAME值解释
FLAGS: 标志 CPUSPEEDNW: 非工作量统计模式下CPU主频,直接来自硬件 IOSEEKTIM: IO寻址时间(毫秒),直接来自硬件 IOTFRSPEED: IO传输速率(字节/毫秒) 后面这6个参数是在oracle收集完操作系统统计信息后才能得出的参数值。 SREADTIM: 读取单个数据块的平均时间 MREADTIM: 读取多个数据块的平均时间 CPUSPEED: 工作量统计模式下CPU主频,根据当前工作量评估出一个合理值 MBRC: Oracle收集完统计信息后评估出的一次多块读可以读几个数据块,db_file_multiblock_read_count(默认值128) MAXTHR: 最大IO吞吐量(字节/秒) SLAVETHR: 平均IO吞吐量(字节/秒)
(5)CBO的执行计划解释
CBO在计算SQL语句的代价时需要的内容:
--数据库对象,例如表索引等对象统计数据, --操作系统统计数据,例如CPU周期IO速度数据块读时间等,
CBO会选择花费时间最少的执行计划为最佳执行计划。
(6)关于收集操作系统统计信息
Oracle使用dbms_stats.gather_system_stats存储过程来收集操作系统统计信息,收集来的数据存放在sys.aux_stats$表中,
如果我们做了收集操作那么会有统计数据,如果没有做就没有统计数据,这两种计算代价的方法是不同的(后续会讲)。
使用DBMS_STATS.GATHER_SYSTEM_STATS过程可以收集关于系统统计信息的数据。
该过程可用于更新Oracle优化器使用的系统级统计信息,以便在生成查询执行计划时能够更准确地估计成本和选择最佳执行路径。
(7)dbms_stats.gather_system_stats语法
dbms_stats.gather_system_stats( gathering_mode => , interval => , stattab => , statid => , statown => )
gathering_mode 默认值“noworkload”,还可以设置为“workload”含义 noworkload:非工作量统计模式,收集上来的数据都是来自硬件 workload:工作量统计模式,收集上来的数据需要在特定的数据库负载间隔内统计出来的,这样的数据才能真实反映出数据库的操作系统参数(需要执行sql测评出来) interval: 可以指定收集统计信息的时间间隔,例如5收集5分钟的统计信息 stattab: 指定包含系统统计信息数据的表。通常情况下,使用默认值NULL即可,表示使用Oracle内部维护的默认表。 statid: 指定保存系统统计信息的标识符。可以是自定义的标识符,用于区分不同的系统统计信息。 statown: 指定包含系统统计信息数据表的所有者。默认为NULL,表示使用当前用户的所有者。
(8)手动收集系统统计信息
execute dbms_stats.gather_system_stats(‘start’); 上下两条指令间隔6分钟执行,然后把这6分钟的统计信息写入到sys.aux_stats$表里面 execute dbms_stats.gather_system_stats(‘stop’);
(9)关于参数值MBRC(db_file_multiblock_read_count):
它是初始化参数db_file_multiblock_read_count的简写中文翻译“一次读多少个数据块或者一次多块读可以读几个数据块”,
如果收集了统计信息那么CBO会用MBRC计算代价,如果没有收集统计信息CBO会用这个初始化参数db_file_multiblock_read_count计算代价。
这个参数值并不是无限大的,大多数平台下的oracle都是128。一般oracle block size =8k。
128*8=1M,也就是说1M是大多数操作系统一次最大IO的限制,如果还有其他限制要从这1M里面扣除,初始化参数db_file_multiblock_read_count的最大值之所以定为128,也是为了保守策略。
三、实战测试1—验证全表扫描的成本计算公式,贴出执行计划和计算公式。
1、创建表
create table test01 as select * from dba_objects;
2、收集表的统计信息
begin dbms_stats.gather_table_stats( ownname => 'TEST', tabname => 'TEST01', estimate_percent => null, method_opt => 'for all columns size 1', cascade => true); end; /
ownname= --用户名 tabname= --表名 cascade= --级联操作 estimate_percent --全表采样 method_opt --不作直方图分析,减小代价计算的影响
3、查看mbrc(多块读)
show parameter db_file_multiblock_read_count;
但这里需要重新设置一下,否则会导致成本cost是349,如果重新设置为128就能正常出数据(不知道为什么)
--下述是没有重新设置多块读的数量:128
--需要重新设置一下128
alter session set db_file_multiblock_read_count=128; --如果不重新设置,则会出现上述349的问题。
show parameter db_file_multiblock_read_count;
4、查看是否收集操作系统统计信息
如下,mbrc为空,所以我们没有收集操作系统统计信息,于是CBO采用了非工作量统计模式(noworkload)来计算代价
5、TEST01使用的总块数
6、查看解析
set autotrace trace explain; select * from test01;
全表扫描的成本等于224,其中CPU代价占整个权重百分比的1%.
7、成本的计算公式如下:
Cost = (SRds * sreadtim +MRds * mreadtim + CPUCycles / cpuspeed) / sreadtime
SRds - number of single block reads 单块读的次数 MRds - number of multi block reads 多块读的次数 CPUCyles - number of CPU cycles 一个CPU周期 sreadtim - single block read time 读取单个数据块的平均时间 mreadtim - multi block read time 读取多个数据块的平均时间 cpuspeed - CPU cycles per second CPU周期/秒
注意:
如果oracle收集了操作系统统计信息,那么CBO采用工作量统计模式计算代价
如果oracle没有收集操作系统统计信息,那么CBO采用非工作量统计模式计算代价我们现在处于“非工作量统计模式”
8、计算过程
(1)cost计算
SRds=0 因为是全表扫描,单块读为0,全都使用的是多块读 MRds= 表的块数/多块读参数=1277/128=9.9765625 mreadtim= ioseektim+db_file_multiblock_count*db_block_size/iotftspeed=10+128*8192/4096=266 sreadtim= ioseektim+db_block_size/iotfrspeed=10+8192/4096=12 CPUCycles= PLAN_TABLE里面的CPU_COST(46710049:根据如下得出) explain plan for select * from test01; select cpu_cost from plan_table; CPU_COST ---------------- 46710049 cpuspeed= CPUSPEEDNW= 2902.5812 Cost = (SRds * sreadtim +MRds * mreadtim + CPUCycles / cpuspeed) / sreadtime Cost = ((0+9.9765625*266+46710049/2902.5812/1000(毫秒换算成秒))/12=222.48 221 是IO代价 1.3410 是CPU代价 手工计算出来的COST用四舍五入等于223,和我们看到的233有差别,这是由于隐含参数_table_scan_cost_plus_one参数造成的
(2)查看当前隐藏参数的值
col name for a30 col value for a10 col describ for a70 conn / as sysdba --切换到sys用户才能查看隐含参数 SELECT x.ksppinm NAME, y.ksppstvl VALUE, x.ksppdesc describ FROM x$ksppi x,x$ksppcv y WHERE x.inst_id = USERENV ('Instance') AND y.inst_id = USERENV ('Instance') AND x.indx = y.indx AND x.ksppinm LIKE '%_table_scan_cost_plus_one%';
(3)更改当前隐藏参数的值为false
根据该参数的描述,在table full scan和index fast full scan的时候会将cost+1即223+1=224
alter session set "_table_scan_cost_plus_one"=false; 禁用 col name for a30 col value for a10 col describ for a70 SELECT x.ksppinm NAME, y.ksppstvl VALUE, x.ksppdesc describ FROM x$ksppi x,x$ksppcv y WHERE x.inst_id = USERENV ('Instance') AND y.inst_id = USERENV ('Instance') AND x.indx = y.indx AND x.ksppinm LIKE '%_table_scan_cost_plus_one%';
(4)重新查看计划
alter session set db_file_multiblock_read_count=128; set autotrace trace explain; select * from test.test01;
这次得到的COST等于223,与计算值正好匹配,这是禁用隐含参数的结果。
9、更改MBRC(多块读)的参数,查看cost值
10、结论
从实验中可以得出,oracle 11gR2中,全表扫描非工作量统计模式下COST计算公式依然和9i/10g一样,没有变化。
同时我们也看到了IO成本占整个代价权重的极大部分,是影响SQL效率的主要因素,需要我们多关注。
四、实战测试2—验证B-tree索引Unique scan的成本计算公式,贴出执行计划和计算公式。
1、CBO各种索引类型成本计算公式如下:
全表扫描: Full table scan cost= HWM/dbf_mbrc 索引唯一扫描: Unique scan cost = blevel +1 索引快速全扫描: Fast Full Scan cost=leaf_blocks/adj_mbrc 只访问索引,不访问原表扫描:Index-only cost = Blevel + effective index selectivity leaf_blocks 索引范围扫描: Range Cost = Blevel + effectivity index selectivity leaf_blocks + effective table selectivity clustering_factor 嵌套循环关联: nested loop join cost =outer access cost + (inner access cost outer cardinality) 排序合并关联: sort merge join cost = outer access cost + inner access cost + sort costs 哈希关联: hash join cost = (outer access cost # of hash partitions) + inner access cost
2、实验
(1)创建表test(表没有主键)
create table test01 as select * from dba_objects;
(2)给object_id创建索引
create index idx_test01 on test01(object_id);
(3)收集表信息
begin dbms_stats.gather_table_stats( wnname=>'test', tabname=>'test01', cascade=>true, estimate_percent=>null, method_opt=>'for all columns size 1'); end; /
可能需要做分析,如果表没有分析,下面统计信息可能就没有了
(4)查看收集表的统计信息后的索引信息
select index_name,blevel,leaf_blocks,clustering_factor,num_rows,distinct_keys from dba_indexes where index_name='IDX_TEST01';
BLEVEL: 索引层数 1表示就1层 LEAF_BLOCKS: 索引树的叶子块数 194 CLUSTERING_FACTOR:索引聚簇因子 NUM_ROWS: 有索引的行数87481和数据行数相匹配 DISTINCT_KEYS: 不同的索引键值87481
(5)查看表的数量
和索引数量一致:select count(*) from test01;
(6)开启跟踪计划
set autotrace trace explain; select * from test01 where object_id=10000;
COST=2,其中CPU代价=0,等值查询与索引的条数无关,消耗CPU资源可以忽略不计。
(7)公式
Index Range scan cost = blevel +1 INDEX Range SCAN的COST=1 --就是blevel,CBO看看需要递归几层索引,与统计信息中的blevel一致 TABLE ACCESS BY INDEX ROWID的COST=1 --通过索引rowid访问表产生的代价
因此最终COST=1+1=2
五、实验3——通过10053事件分析一个SQL执行计划的产生过程,需要贴出trace中的相关信息和必要的文字说明。
1、创建2张表并做相关统计信息收集
(1)创建表
create table test03 as select * from dba_objects; create table test04 as select * from test03 where rownum<100;
(2)创建索引
create index idx_test03 on test03(object_id); create index idx_test04 on test04(object_id);
(3)给表做统计信息收集
--test03
begin dbms_stats.gather_table_stats( ownname => 'TEST', tabname => 'TEST03', estimate_percent => null, method_opt => 'for all columns size 1', cascade => true); end; /
--test04
begin dbms_stats.gather_table_stats( ownname => 'TEST', tabname => 'TEST04', estimate_percent => null, method_opt => 'for all columns size 1', cascade => true); end; /
2、启动事件
(1)启动10053事件
alter session set events '10053 trace name context forever,level 1';
(2)查看事件
select count(*) from test03,test04 where test03.object_id=test04.object_id; --执行SQL
(3)关闭10053事件
alter session set events '10053 trace name context off'; 关闭10053事件
(4)查看跟踪文件是哪个
select value from v$diag_info where name='Default Trace File'; 当前会话写入的trace VALUE ------------------------------------------------------------------------- /oracle/app/oracle/diag/rdbms/mesorcl/mesorcl/trace/mesorcl_ora_28992.trc
-------------------------------------------------------------------------
-------------------------------------------------------------------------
-------------------------------------------------------------------------
-------------------------------------------------------------------------
3、查看分析跟踪文件
下面我们来看看trace文件中相关信息
[root@qdmes219 ~]# cat /oracle/app/oracle/diag/rdbms/mesorcl/mesorcl/trace/mesorcl_ora_28992.trc
(1)跟踪文件名称
Trace file /oracle/app/oracle/diag/rdbms/mesorcl/mesorcl/trace/mesorcl_ora_28992.trc
(2)Oracle系统环境
Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production With the Partitioning, OLAP, Data Mining and Real Application Testing options ORACLE_HOME = /oracle/app/oracle/product/11.2.0/db_1 System name: Linux Node name: qdmes219 Release: 4.14.35-1818.3.3.el7uek.x86_64 Version: #2 SMP Mon Sep 24 14:45:01 PDT 2018 Machine: x86_64 VM name: VMWare Version: 6 Instance name: mesorcl Redo thread mounted by this instance: 1 Oracle process number: 29 Unix process pid: 28992, image: oracle@qdmes219 (TNS V1-V3)
(3)会话及查看时间
*** 2023-11-30 15:30:52.070 *** SESSION ID:(160.8747) 2023-11-30 15:30:52.070 *** CLIENT ID:() 2023-11-30 15:30:52.070 *** SERVICE NAME:(SYS$USERS) 2023-11-30 15:30:52.070 *** MODULE NAME:(SQL*Plus) 2023-11-30 15:30:52.070 *** ACTION NAME:() 2023-11-30 15:30:52.070
(4)查询块签名
Registered qb: SEL$1 0xe8e556d0 (PARSER) --------------------- QUERY BLOCK SIGNATURE --------------------- signature (): qb_name=SEL$1 nbfros=2 flg=0 fro(0): flg=4 objn=97761 hint_alias="TEST03"@"SEL$1" fro(1): flg=4 objn=97762 hint_alias="TEST04"@"SEL$1" SPM: statement not found in SMB ************************** Automatic degree of parallelism (ADOP) ************************** Automatic degree of parallelism is disabled: Parameter. PM: Considering predicate move-around in query block SEL$1 (#0) ************************** Predicate Move-Around (PM)
(5)优化信息
************************** OPTIMIZER INFORMATION ****************************************** ----- Current SQL Statement for this session (sql_id=267m33n2qpu6a) ----- 这个会话的SQL_ID select count(*) from test03,test04 where test03.object_id=test04.object_id ******************************************* Legend --下面这些缩写都是优化器使用的trace标识 The following abbreviations are used by optimizer trace. CBQT - cost-based query transformation JPPD - join predicate push-down OJPPD - old-style (non-cost-based) JPPD FPD - filter push-down PM - predicate move-around CVM - complex view merging SPJ - select-project-join SJC - set join conversion SU - subquery unnesting OBYE - order by elimination OST - old style star transformation ST - new (cbqt) star transformation CNT - count(col) to count(*) transformation JE - Join Elimination JF - join factorization CBY - connect by SLP - select list pruning DP - distinct placement qb - query block LB - leaf blocks DK - distinct keys LB/K - average number of leaf blocks per key DB/K - average number of data blocks per key CLUF - clustering factor NDV - number of distinct values Resp - response cost Card - cardinality Resc - resource cost NL - nested loops (join) SM - sort merge (join) HA - hash (join) CPUSPEED - CPU Speed IOTFRSPEED - I/O transfer speed IOSEEKTIM - I/O seek time SREADTIM - average single block read time MREADTIM - average multiblock read time MBRC - average multiblock read count MAXTHR - maximum I/O system throughput SLAVETHR - average slave I/O throughput dmeth - distribution method 1: no partitioning required 2: value partitioned 4: right is random (round-robin) 128: left is random (round-robin) 8: broadcast right and partition left 16: broadcast left and partition right 32: partition left using partitioning of right 64: partition right using partitioning of left 256: run the join in serial 0: invalid distribution method sel - selectivity ptn - partition *************************************** PARAMETERS USED BY THE OPTIMIZER ******************************** ************************************* PARAMETERS WITH ALTERED VALUES ****************************** Compilation Environment Dump _pga_max_size = 322960 KB Bug Fix Control Environment
(6)参数区——包含初始化参数和隐含参数等
*************************************
PARAMETERS WITH DEFAULT VALUES --默认参数
******************************
Compilation Environment Dump
optimizer_mode_hinted = false
optimizer_features_hinted = 0.0.0
parallel_execution_enabled = true
parallel_query_forced_dop = 0
parallel_dml_forced_dop = 0
parallel_ddl_forced_degree = 0
....【此处省略好几百行】
fix 16799181 = enabled
fix 16582322 = enabled
fix 16712213 = enabled
fix 16516883 = enabled
fix 16725982 = enabled
fix 13253977 = enabled
fix 16554552 = enabled
***************************************
PARAMETERS IN OPT_PARAM HINT
****************************
(7)跟踪信息
***************************************
Column Usage Monitoring is ON: tracking level = 1 --标识10053事件用的时level1级别
***************************************
Considering Query Transformations on query block SEL$1 (#0)
**************************
Query transformations (QT)
**************************
JF: Checking validity of join factorization for query block SEL$1 (#0)
JF: Bypassed: not a UNION or UNION-ALL query block.
ST: not valid since star transformation parameter is FALSE
TE: Checking validity of table expansion for query block SEL$1 (#0)
TE: Bypassed: No partitioned table in query block.
CBQT bypassed for query block SEL$1 (#0): no complex view, sub-queries or UNION (ALL) queries.
CBQT: Validity checks failed for 267m33n2qpu6a.
CSE: Considering common sub-expression elimination in query block SEL$1 (#0)
*************************
Common Subexpression elimination (CSE)
*************************
CSE: CSE not performed on query block SEL$1 (#0).
OBYE: Considering Order-by Elimination from view SEL$1 (#0)
***************************
Order-by elimination (OBYE)
***************************
OBYE: OBYE bypassed: no order by to eliminate.
JE: Considering Join Elimination on query block SEL$1 (#0)
*************************
Join Elimination (JE)
*************************
SQL:******* UNPARSED QUERY IS *******
SELECT COUNT(*) "COUNT(*)" FROM "TEST"."TEST03" "TEST03","TEST"."TEST04" "TEST04" WHERE "TEST03"."OBJECT_ID"="TEST04"."OBJECT_ID"
JE: cfro: TEST03 objn:97762 col#:4 dfro:TEST04 dcol#:4
JE: cfro: TEST04 objn:97761 col#:4 dfro:TEST03 dcol#:4
SQL:******* UNPARSED QUERY IS *******
SELECT COUNT(*) "COUNT(*)" FROM "TEST"."TEST03" "TEST03","TEST"."TEST04" "TEST04" WHERE "TEST03"."OBJECT_ID"="TEST04"."OBJECT_ID"
Query block SEL$1 (#0) unchanged
CVM: Considering view merge in query block SEL$1 (#0)
OJE: Begin: find best directive for query block SEL$1 (#0)
OJE: End: finding best directive for query block SEL$1 (#0)
CNT: Considering count(col) to count(*) on query block SEL$1 (#0)
*************************
Count(col) to Count(*) (CNT)
*************************
CNT: COUNT() to COUNT(*) not done.
JE: Considering Join Elimination on query block SEL$1 (#0)
*************************
Join Elimination (JE)
*************************
SQL:******* UNPARSED QUERY IS *******
SELECT COUNT(*) "COUNT(*)" FROM "TEST"."TEST03" "TEST03","TEST"."TEST04" "TEST04" WHERE "TEST03"."OBJECT_ID"="TEST04"."OBJECT_ID"
JE: cfro: TEST03 objn:97762 col#:4 dfro:TEST04 dcol#:4
JE: cfro: TEST04 objn:97761 col#:4 dfro:TEST03 dcol#:4
SQL:******* UNPARSED QUERY IS *******
SELECT COUNT(*) "COUNT(*)" FROM "TEST"."TEST03" "TEST03","TEST"."TEST04" "TEST04" WHERE "TEST03"."OBJECT_ID"="TEST04"."OBJECT_ID"
Query block SEL$1 (#0) unchanged
query block SEL$1 (#0) unchanged
Considering Query Transformations on query block SEL$1 (#0)
**************************
Query transformations (QT)
**************************
JF: Checking validity of join factorization for query block SEL$1 (#0)
JF: Bypassed: not a UNION or UNION-ALL query block.
ST: not valid since star transformation parameter is FALSE
TE: Checking validity of table expansion for query block SEL$1 (#0)
TE: Bypassed: No partitioned table in query block.
CBQT bypassed for query block SEL$1 (#0): no complex view, sub-queries or UNION (ALL) queries.
CBQT: Validity checks failed for 267m33n2qpu6a.
CSE: Considering common sub-expression elimination in query block SEL$1 (#0)
*************************
Common Subexpression elimination (CSE)
*************************
CSE: CSE not performed on query block SEL$1 (#0).
SU: Considering subquery unnesting in query block SEL$1 (#0)
********************
Subquery Unnest (SU)
********************
SJC: Considering set-join conversion in query block SEL$1 (#0)
*************************
Set-Join Conversion (SJC)
*************************
SJC: not performed
JE: Considering Join Elimination on query block SEL$1 (#0)
*************************
Join Elimination (JE)
*************************
SQL:******* UNPARSED QUERY IS *******
SELECT COUNT(*) "COUNT(*)" FROM "TEST"."TEST03" "TEST03","TEST"."TEST04" "TEST04" WHERE "TEST03"."OBJECT_ID"="TEST04"."OBJECT_ID"
JE: cfro: TEST03 objn:97762 col#:4 dfro:TEST04 dcol#:4
JE: cfro: TEST04 objn:97761 col#:4 dfro:TEST03 dcol#:4
SQL:******* UNPARSED QUERY IS *******
SELECT COUNT(*) "COUNT(*)" FROM "TEST"."TEST03" "TEST03","TEST"."TEST04" "TEST04" WHERE "TEST03"."OBJECT_ID"="TEST04"."OBJECT_ID"
Query block SEL$1 (#0) unchanged
PM: Considering predicate move-around in query block SEL$1 (#0)
**************************
Predicate Move-Around (PM)
**************************
PM: PM bypassed: Outer query contains no views.
PM: PM bypassed: Outer query contains no views.
query block SEL$1 (#0) unchanged
FPD: Considering simple filter push in query block SEL$1 (#0)
"TEST03"."OBJECT_ID"="TEST04"."OBJECT_ID"
try to generate transitive predicate from check constraints for query block SEL$1 (#0)
finally: "TEST03"."OBJECT_ID"="TEST04"."OBJECT_ID"
apadrv-start sqlid=2530569016924891338 : call(in-use=1872, alloc=16344), compile(in-use=58640, alloc=60416), execution(in-use=3376, alloc=4032) ******************************************* Peeked values of the binds in SQL statement ******************************************* Final query after transformations:******* UNPARSED QUERY IS ******* SELECT COUNT(*) "COUNT(*)" FROM "TEST"."TEST03" "TEST03","TEST"."TEST04" "TEST04" WHERE "TEST03"."OBJECT_ID"="TEST04"."OBJECT_ID" kkoqbc: optimizing query block SEL$1 (#0) : call(in-use=1928, alloc=16344), compile(in-use=59928, alloc=60416), execution(in-use=3376, alloc=4032)
kkoqbc-subheap (create addr=0x7f3ee8e5fb58)
****************
QUERY BLOCK TEXT --查询块文本,就是执行的哪个SQL语句
****************
select count(*) from test03,test04 where test03.object_id=test04.object_id
---------------------
QUERY BLOCK SIGNATURE
---------------------
signature (optimizer): qb_name=SEL$1 nbfros=2 flg=0
fro(0): flg=0 objn=97761 hint_alias="TEST03"@"SEL$1"
fro(1): flg=0 objn=97762 hint_alias="TEST04"@"SEL$1"
(8)操作系统统计信息区
----------------------------- SYSTEM STATISTICS INFORMATION ----------------------------- Using NOWORKLOAD Stats --基于非工作量统计模式 CPUSPEEDNW: 2903 millions instructions/sec (default is 100) --非工作量统计模式下CPU主频 IOTFRSPEED: 4096 bytes per millisecond (default is 4096) --IO传输速率(字节/毫秒) IOSEEKTIM: 10 milliseconds (default is 10) --IO寻址时间(毫秒) MBRC: NO VALUE blocks (default is 8) --一次多块读可以读几个数据块
(9)基本统计信息(对象级别统计信息) OLAP系统而言拥有对象级别统计信息就已经足够了
--test04统计信息
*************************************** BASE STATISTICAL INFORMATION --这些统计信息都来自于视图 *********************** Table Stats:: --来自user_tables视图 Table: TEST04 Alias: TEST04 #Rows: 99 #Blks: 5 AvgRowLen: 75.00 ChainCnt: 0.00 行数 块数 平均行长 Column (#4): OBJECT_ID( AvgLen: 3 NDV: 99 Nulls: 0 Density: 0.010101 Min: 2 Max: 100 Index Stats:: --来自user_indexes视图 Index: IDX_TEST04 Col#: 4 LVLS: 0 #LB: 1 #DK: 99 LB/K: 1.00 DB/K: 1.00 CLUF: 2.00 索引几层 叶子块数多少个 唯一键值 每个键值有多少个叶块 每个键值有多少个数据块 聚簇因子
--test03统计信息
*********************** Table Stats:: Table: TEST03 Alias: TEST03 #Rows: 87483 #Blks: 1277 AvgRowLen: 98.00 ChainCnt: 0.00 Column (#4): OBJECT_ID( AvgLen: 5 NDV: 87483 Nulls: 0 Density: 0.000011 Min: 2 Max: 97761 Index Stats:: Index: IDX_TEST03 Col#: 4 LVLS: 1 #LB: 194 #DK: 87483 LB/K: 1.00 DB/K: 1.00 CLUF: 1375.00
--test03访问路径信息
Access path analysis for TEST03 *************************************** SINGLE TABLE ACCESS PATH Single Table Cardinality Estimation for TEST03[TEST03] Table: TEST03 Alias: TEST03 Card: Original: 87483.000000 Rounded: 87483 Computed: 87483.00 Non Adjusted: 87483.00 原始行数 近似值 精确值 非修正值 Access Path: TableScan --全表扫描代价 Cost: 348.79 Resp: 348.79 Degree: 0 --总代价=348.79 Cost_io: 348.00 Cost_cpu: 27465509 --总代价=IO代价+CPU代价 Resp_io: 348.00 Resp_cpu: 27465509 --并行访问代价 Access Path: index (index (FFS)) --索引快速全扫描 Index: IDX_TEST03 --索引扫描代价 resc_io: 54.00 resc_cpu: 11879519 --串行访问代价=54(因为索引是串行存储的) ix_sel: 0.000000 ix_sel_with_filters: 1.000000 --ix_sel=1/87483=1/87483=0.000011431 索引选择率 --ix_sel_with_filters 带过滤条件索引选择率 Access Path: index (FFS) Cost: 54.34 Resp: 54.34 Degree: 1 --索引并行访问代价=54.34>54(串行访问代价) Cost_io: 54.00 Cost_cpu: 11879519 --所以要选择串行访问 Resp_io: 54.00 Resp_cpu: 11879519 --并行度=1 Access Path: index (FullScan) --索引全扫描 Index: IDX_TEST03 resc_io: 195.00 resc_cpu: 18885281 --串行访问代价=195,这个比较高 ix_sel: 1.000000 ix_sel_with_filters: 1.000000 Cost: 195.54 Resp: 195.54 Degree: 1 --并行度=1 Best:: AccessPath: IndexFFS Index: IDX_TEST03 Cost: 54.34 Degree: 1 Resp: 54.34 Card: 87483.00 Bytes: 0
--test04访问路径分析
Access path analysis for TEST04 --test04表访问路径的不同代价 *************************************** SINGLE TABLE ACCESS PATH Single Table Cardinality Estimation for TEST04[TEST04] Table: TEST04 Alias: TEST04 Card: Original: 99.000000 Rounded: 99 Computed: 99.00 Non Adjusted: 99.00 原始行数 近似值 精确值 非修正值 Access Path: TableScan --全表扫描代价 Cost: 3.00 Resp: 3.00 Degree: 0 --总代价=3 Cost_io: 3.00 Cost_cpu: 56397 --IO代价+CPU代价 Resp_io: 3.00 Resp_cpu: 56397 --并行访问代价 Access Path: index (index (FFS)) --索引快速全扫描 Index: IDX_TEST04 resc_io: 2.00 resc_cpu: 19001 --串行访问代价=2 ix_sel: 0.000000 ix_sel_with_filters: 1.000000 --ix_sel=1/DK=1/99=0.01索引选择率 --ix_sel_with_filters带过滤条件索引选择率 Access Path: index (FFS) Cost: 2.00 Resp: 2.00 Degree: 1 --索引并行访问代价=2,并行度=1 Cost_io: 2.00 Cost_cpu: 19001 Resp_io: 2.00 Resp_cpu: 19001 Access Path: index (FullScan) --索引全扫描 Index: IDX_TEST04 resc_io: 1.00 resc_cpu: 26921 --串行访问代价=1,这个最低,就是它了 ix_sel: 1.000000 ix_sel_with_filters: 1.000000 Cost: 1.00 Resp: 1.00 Degree: 1 Best:: AccessPath: IndexRange Index: IDX_TEST04 Cost: 1.00 Degree: 1 Resp: 1.00 Card: 99.00 Bytes: 0
(10)关联查询—驱动表的选择
*************************************** OPTIMIZER STATISTICS AND COMPUTATIONS --优化器的统计和计算 *************************************** GENERAL PLANS --选择执行计划 *************************************** Considering cardinality-based initial join order. Permutations for Starting Table :0 Join order[1]: TEST04[TEST04]#0 TEST03[TEST03]#1 --关联的对象
(11)现在要用test04小表关联test03大表,test04做驱动表
*************** Now joining: TEST03[TEST03]#1 --现在要用test04小表关联test03大表,test04做驱动表 *************** NL Join --嵌套循环关联 test04表中有99条,小表为驱动表 Outer table: Card: 99.00 Cost: 1.00 Resp: 1.00 Degree: 1 Bytes: 3 Access path analysis for TEST03 Inner table: TEST03 Alias: TEST03 Access Path: TableScan --全表扫描-嵌套循环关联COST=34242.00 NL Join: Cost: 34320.07 Resp: 34320.07 Degree: 1 Cost_io: 34242.00 Cost_cpu: 2719112301 Resp_io: 34242.00 Resp_cpu: 2719112301 --并行访问代价 Access Path: index (index (FFS)) --索引快速全扫描 Index: IDX_TEST03 resc_io: 52.56 resc_cpu: 11879519 --串行访问代价 ix_sel: 0.000000 ix_sel_with_filters: 1.000000 Inner table: TEST03 Alias: TEST03 Access Path: index (FFS) NL Join: Cost: 5237.77 Resp: 5237.77 Degree: 1 --并行访问 Cost_io: 5204.00 Cost_cpu: 1176099338 Resp_io: 5204.00 Resp_cpu: 1176099338 Access Path: index (AllEqJoinGuess) Index: IDX_TEST03 resc_io: 1.00 resc_cpu: 8171 ix_sel: 0.000011 ix_sel_with_filters: 0.000011 NL Join (ordered): Cost: 100.02 Resp: 100.02 Degree: 1 Cost_io: 100.00 Cost_cpu: 835894 Resp_io: 100.00 Resp_cpu: 835894 Best NL cost: 100.02 --test04为驱动表,小表为驱动表,最后代价100.02 resc: 100.02 resc_io: 100.00 resc_cpu: 835894 --串行方式的代价 IO代价+CPU代价 resp: 100.02 resp_io: 100.00 resc_cpu: 835894 --并行方式的代价 Join Card: 99.000000 = outer (99.000000) * inner (87483.000000) * sel (0.000011) Join Card - Rounded: 99 Computed: 99.00 Outer table: TEST04 Alias: TEST04 resc: 1.00 card 99.00 bytes: 3 deg: 1 resp: 1.00 Inner table: TEST03 Alias: TEST03 resc: 54.34 card: 87483.00 bytes: 5 deg: 1 resp: 54.34 using dmeth: 2 #groups: 1 SORT ressource Sort statistics Sort width: 944 Area size: 1048576 Max Area size: 165355520 Degree: 1 Blocks to Sort: 172 Row size: 16 Total Rows: 87483 Initial runs: 2 Merge passes: 1 IO Cost / pass: 94 Total IO sort cost: 266 Total CPU sort cost: 103773271 Total Temp space used: 2122000 SM join: Resc: 324.32 Resp: 324.32 [multiMatchCost=0.00] SM Join --先排序后合并关联 SM cost: 324.32 --代价:324.32 resc: 324.32 resc_io: 321.00 resc_cpu: 115679712 resp: 324.32 resp_io: 321.00 resp_cpu: 115679712 Outer table: TEST04 Alias: TEST04 resc: 1.00 card 99.00 bytes: 3 deg: 1 resp: 1.00 Inner table: TEST03 Alias: TEST03 resc: 54.34 card: 87483.00 bytes: 5 deg: 1 resp: 54.34 using dmeth: 2 #groups: 1 Cost per ptn: 0.27 #ptns: 1 hash_area: 256 (max=40370) buildfrag: 1 probefrag: 182 ppasses: 1 Hash join: Resc: 55.61 Resp: 55.61 [multiMatchCost=0.00] HA Join --哈希关联 HA cost: 55.61 --代价55.61,最好是哈希代价最小 resc: 55.61 resc_io: 55.00 resc_cpu: 21269591 resp: 55.61 resp_io: 55.00 resp_cpu: 21269591 Best:: JoinMethod: Hash --最后关联方法选择:哈希hash Cost: 55.61 Degree: 1 Resp: 55.61 Card: 99.00 Bytes: 8 --返回记录数+字节 *********************** Best so far: Table#: 0 cost: 1.0008 card: 99.0000 bytes: 297 Table#: 1 cost: 55.6107 card: 99.0000 bytes: 792 *********************** Join order[2]: TEST03[TEST03]#1 TEST04[TEST04]#0
(12)--现在要用test03大表关联test04小表,test03做驱动表
*************** Now joining: TEST04[TEST04]#0 --现在要用test03大表关联test04小表,test03做驱动表 *************** NL Join --嵌套循环关联,test03表中有71969条,大表为驱动表 Outer table: Card: 87483.00 Cost: 54.34 Resp: 54.34 Degree: 1 Bytes: 5 Access path analysis for TEST04 Inner table: TEST04 Alias: TEST04 Access Path: TableScan NL Join: Cost: 118663.99 Resp: 118663.99 Degree: 1 Cost_io: 118522.00 Cost_cpu: 4945675767 Resp_io: 118522.00 Resp_cpu: 4945675767 Access Path: index (index (FFS)) Index: IDX_TEST04 resc_io: 0.27 resc_cpu: 19001 ix_sel: 0.000000 ix_sel_with_filters: 1.000000 Inner table: TEST04 Alias: TEST04 Access Path: index (FFS) NL Join: Cost: 23797.07 Resp: 23797.07 Degree: 1 Cost_io: 23749.00 Cost_cpu: 1674182495 Resp_io: 23749.00 Resp_cpu: 1674182495 Access Path: index (AllEqJoinGuess) Index: IDX_TEST04 resc_io: 0.00 resc_cpu: 1050 ix_sel: 0.010101 ix_sel_with_filters: 0.010101 NL Join (ordered): Cost: 56.98 Resp: 56.98 Degree: 1 Cost_io: 54.00 Cost_cpu: 103736669 Resp_io: 54.00 Resp_cpu: 103736669 Best NL cost: 56.98 --嵌套循环关联最后代价56.98 resc: 56.98 resc_io: 54.00 resc_cpu: 103736669 resp: 56.98 resp_io: 54.00 resc_cpu: 103736669 Join Card: 99.000000 = outer (87483.000000) * inner (99.000000) * sel (0.000011) Join Card - Rounded: 99 Computed: 99.00 Outer table: TEST03 Alias: TEST03 resc: 54.34 card 87483.00 bytes: 5 deg: 1 resp: 54.34 Inner table: TEST04 Alias: TEST04 resc: 1.00 card: 99.00 bytes: 3 deg: 1 resp: 1.00 using dmeth: 2 #groups: 1 SORT ressource Sort statistics Sort width: 944 Area size: 1048576 Max Area size: 165355520 Degree: 1 Blocks to Sort: 172 Row size: 16 Total Rows: 87483 Initial runs: 2 Merge passes: 1 IO Cost / pass: 94 Total IO sort cost: 266 Total CPU sort cost: 103773271 Total Temp space used: 2122000 SORT ressource Sort statistics Sort width: 944 Area size: 1048576 Max Area size: 165355520 Degree: 1 Blocks to Sort: 1 Row size: 14 Total Rows: 99 Initial runs: 1 Merge passes: 0 IO Cost / pass: 0 Total IO sort cost: 0 Total CPU sort cost: 34860544 Total Temp space used: 0 SM join: Resc: 325.32 Resp: 325.32 [multiMatchCost=0.00] SM Join --先排序后合并关联 SM cost: 325.32 --代价325.32 resc: 325.32 resc_io: 321.00 resc_cpu: 150540255 resp: 325.32 resp_io: 321.00 resp_cpu: 150540255 SM Join (with index on outer) Access Path: index (FullScan) Index: IDX_TEST03 resc_io: 195.00 resc_cpu: 18885281 ix_sel: 1.000000 ix_sel_with_filters: 1.000000 Cost: 195.54 Resp: 195.54 Degree: 1 Outer table: TEST03 Alias: TEST03 resc: 195.54 card 87483.00 bytes: 5 deg: 1 resp: 195.54 Inner table: TEST04 Alias: TEST04 resc: 1.00 card: 99.00 bytes: 3 deg: 1 resp: 1.00 using dmeth: 2 #groups: 1 SORT ressource Sort statistics Sort width: 944 Area size: 1048576 Max Area size: 165355520 Degree: 1 Blocks to Sort: 1 Row size: 14 Total Rows: 99 Initial runs: 1 Merge passes: 0 IO Cost / pass: 0 Total IO sort cost: 0 Total CPU sort cost: 34860544 Total Temp space used: 0 SM join: Resc: 197.54 Resp: 197.54 [multiMatchCost=0.00] Outer table: TEST03 Alias: TEST03 resc: 54.34 card 87483.00 bytes: 5 deg: 1 resp: 54.34 Inner table: TEST04 Alias: TEST04 resc: 1.00 card: 99.00 bytes: 3 deg: 1 resp: 1.00 using dmeth: 2 #groups: 1 Cost per ptn: 0.39 #ptns: 1 hash_area: 256 (max=40370) buildfrag: 182 probefrag: 1 ppasses: 1 Hash join: Resc: 55.74 Resp: 55.74 [multiMatchCost=0.00] --哈希关联,代价=55.74 Outer table: TEST04 Alias: TEST04 resc: 1.00 card 99.00 bytes: 3 deg: 1 resp: 1.00 Inner table: TEST03 Alias: TEST03 resc: 54.34 card: 87483.00 bytes: 5 deg: 1 resp: 54.34 using dmeth: 2 #groups: 1 Cost per ptn: 0.27 #ptns: 1 hash_area: 256 (max=40370) buildfrag: 1 probefrag: 182 ppasses: 1 Hash join: Resc: 55.61 Resp: 55.61 [multiMatchCost=0.00] HA Join HA cost: 55.61 swapped resc: 55.61 resc_io: 55.00 resc_cpu: 21269591 resp: 55.61 resp_io: 55.00 resp_cpu: 21269591
(13)最终选择最优计划
Join order aborted: cost > best plan cost
***********************
(newjo-stop-1) k:0, spcnt:0, perm:2, maxperm:2000
*********************************
Number of join permutations tried: 2
*********************************
Consider using bloom filter between TEST04[TEST04] and TEST03[TEST03] with ??
kkoBloomFilter: join ndv:0 reduction:1.000000 (limit:0.500000) rejected because no single-tables predicates
Enumerating distribution method (advanced)
--- Distribution method for
join between TEST04[TEST04](serial) and TEST03[TEST03](serial); jm = 1; right side access path = IndexFFS
---- serial Hash-Join -> NONE
(newjo-save) [0 1 ]
Trying or-Expansion on query block SEL$1 (#0)
Transfer Optimizer annotations for query block SEL$1 (#0)
id=0 frofkksm[i] (sort-merge/hash) predicate="TEST03"."OBJECT_ID"="TEST04"."OBJECT_ID"
id=0 frosand (sort-merge/hash) predicate="TEST03"."OBJECT_ID"="TEST04"."OBJECT_ID"
Final cost for query block SEL$1 (#0) - All Rows Plan:
Best join order: 1 --最终代价选择55.6107,用test04小表驱动表
Cost: 55.6107 Degree: 1 Card: 99.0000 Bytes: 792
Resc: 55.6107 Resc_io: 55.0000 Resc_cpu: 21269591
Resp: 55.6107 Resp_io: 55.0000 Resc_cpu: 21269591
kkoqbc-subheap (delete addr=0x7f3ee8e5fb58, in-use=37024, alloc=49272)
kkoqbc-end:
:
call(in-use=10896, alloc=82128), compile(in-use=63976, alloc=64984), execution(in-use=3376, alloc=4032)
kkoqbc: finish optimizing query block SEL$1 (#0)
apadrv-end
:
call(in-use=10896, alloc=82128), compile(in-use=64960, alloc=69128), execution(in-use=3376, alloc=4032)
Starting SQL statement dump
user_id=107 user_name=TEST module=SQL*Plus action=
sql_id=267m33n2qpu6a plan_hash_value=1655735759 problem_type=3
----- Current SQL Statement for this session (sql_id=267m33n2qpu6a) -----
select count(*) from test03,test04 where test03.object_id=test04.object_id
sql_text_length=75
sql=select count(*) from test03,test04 where test03.object_id=test04.object_id
(14)执行计划
----- Explain Plan Dump ----- ----- Plan Table ----- ============ Plan Table ============ --------------------------------------------+-----------------------------------+ | Id | Operation | Name | Rows | Bytes | Cost | Time | --------------------------------------------+-----------------------------------+ | 0 | SELECT STATEMENT | | | | 56 | | | 1 | SORT AGGREGATE | | 1 | 8 | | | | 2 | HASH JOIN | | 99 | 792 | 56 | 00:00:01 | | 3 | INDEX FULL SCAN | IDX_TEST04| 99 | 297 | 1 | 00:00:01 | | 4 | INDEX FAST FULL SCAN | IDX_TEST03| 85K | 427K | 54 | 00:00:01 | --------------------------------------------+-----------------------------------+ Predicate Information: ---------------------- 2 - access("TEST03"."OBJECT_ID"="TEST04"."OBJECT_ID") 选择的执行计划和上面分析结果是相匹配的。
(15)看看我们真实的执行计划的样子
不能说完全相似,只能说一模一样。
09:12:00 TEST@mesorcl>set autotrace trace exp 09:12:04 TEST@mesorcl>select count(*) from test03,test04 where test03.object_id=test04.object_id; Elapsed: 00:00:00.00
Execution Plan ---------------------------------------------------------- Plan hash value: 1655735759 ------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 8 | 56 (2) | 00:00:01 | | 1 | SORT AGGREGATE | | 1 | 8 | | | |* 2 | HASH JOIN | | 99 | 792 | 56 (2) | 00:00:01 | | 3 | INDEX FULL SCAN | IDX_TEST04 | 99 | 297 | 1 (0) | 00:00:01 | | 4 | INDEX FAST FULL SCAN| IDX_TEST03 | 87483 | 427K | 54 (0) | 00:00:01 | ------------------------------------------------------------------------------------- Predicate Information (identified by operation id): ---------------------------------------------------
2 - access("TEST03"."OBJECT_ID"="TEST04"."OBJECT_ID")
4、小结
这说明我们的优化器在对比完不同代价后选择的执行计划是最优的,如果我们在实际工作中,遇到了执行计划选择错误的情景,我们可以通过10053事件来做详细的分析。
四、案例4:——当统计信息不准备确实的比较
当统计信息不准确时,CBO可能产生错误的执行计划,请给出这样的一个例子,在10053 trace中找到CBO出错的位置,并给出必要的文字说明。
1、创建表和索引
(1)创建80000条的数据
create table test05 as select * from dba_objects where rownum<=80000; select count(*) from test05; COUNT(*) ---------- 80000
(2)创建索引
create index idx_test05 on test05(object_id);
2、为了让CBO产生错误的执行计划,我把test05数据分布变的倾斜一些
(1)原数据量是80000
(2)更改数据-将object<70000的改为值等于1
update test05 set object_id=1 where object_id<=79000; commit;
我们更改了78187行,现在object_id=1占97%
(3)更改数据-将object>1的改为值等于2
update test05 set object_id=2 where object_id>1;
(4)现在object_id等于1和等于2的值分布
09:37:46 TEST@mesorcl>select count(*) from test05 where object_id=1; COUNT(*) ---------- 78187 09:38:33 TEST@mesorcl>select count(*) from test05 where object_id=2; COUNT(*) ---------- 1813
3、收集表的统计信息
begin dbms_stats.gather_table_stats( ownname => 'TEST', tabname => 'TEST05', cascade => true); end; /
4、查看执行计划信息
(1)获取执行计划-object=1
set autotrace on exp select count(object_name) from test05 where object_id=1;
COUNT(OBJECT_NAME)
------------------
78187
Execution Plan
----------------------------------------------------------
Plan hash value: 3596450589
-----------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 28 | 320 (1) | 00:00:04 |
| 1 | SORT AGGREGATE | | 1 | 28 | | |
|* 2 | TABLE ACCESS FULL| TEST05 | 78109 | 2135K | 320 (1) | 00:00:04 |
-----------------------------------------------------------------------------
全表扫描78109,还是比较准确的,说明表分析生效了
Predicate Information (identified by operation id): --------------------------------------------------- 2 - filter("OBJECT_ID"=1)
(2)获取执行计划-object=2
select count(object_name) from test05 where object_id=2;
COUNT(OBJECT_NAME)
------------------
1813
Execution Plan
----------------------------------------------------------
Plan hash value: 2196373514
-------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 28 | 35 (0) | 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | 28 | | |
| 2 | TABLE ACCESS BY INDEX ROWID | TEST05 | 1884 | 52752 | 35 (0) | 00:00:01 |
|* 3 | INDEX RANGE SCAN | IDX_TEST05 | 1884 | | 7 (0) | 00:00:01 |
-------------------------------------------------------------------------------------------
对于比较少的行走索引也是正确的
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("OBJECT_ID"=2)
4、再次更改表的数据倾斜度
(1)将表前60000行的object_id的值改为3;修改object_id分布
update test05 set object_id=3 where rownum<=70000;
(2)object_id的值从78187变成了8214
select count(object_name) from test05 where object_id=1; COUNT(object_name) ---------- 8857 commit; 提交 Commit complete.
(3)object_id的值从78187变成了8214
select count(object_name) from test05 where object_id=1; COUNT(OBJECT_NAME) ------------------ 8214 Execution Plan ---------------------------------------------------------------------------- Plan hash value: 3596450589 ----------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ----------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 28 | 320 (1) | 00:00:04 | | 1 | SORT AGGREGATE | | 1 | 28 | | | |* 2 | TABLE ACCESS FULL | TEST05 | 78109 | 2135K | 320 (1) | 00:00:04 | ----------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - filter("OBJECT_ID"=1)
依然选择走全表扫描,我们从记录数的变化上就可以知道应该走索引效率更高些; 就像object_id=2的执行计划一样INDEX RANGE SCAN代价更小些,为什么CBO会选择了错误的执行计划呢? 这是因为我们虽然修改了记录值但没有及时更新test05表的对象统计信息,CBO还是使用了当初最早的统计信息,所以在计算COST的时候还是认为走全表扫描的代价最优。
(4)更新统计信息
下面我们再把对象统计信息重新统计一下,得出最新的代价列表进行筛选。 --------------------------------------------------------------------------- begin dbms_stats.gather_table_stats( ownname => 'TEST', tabname => 'TEST05', cascade => true); end; /
(5)启动10053事件
alter session set events '10053 trace name context forever,level 1'; Session altered.
(6)观察执行计划
set autotrace on exp; select count(object_name) from test05 where object_id=1;
COUNT(OBJECT_NAME) ------------------ 8214 Execution Plan ---------------------------------------------------------- Plan hash value: 2196373514 ------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 28 | 150 (0) | 00:00:02 | | 1 | SORT AGGREGATE | | 1 | 28 | | | | 2 | TABLE ACCESS BY INDEX ROWID | TEST05 | 8378 | 229K | 150 (0) | 00:00:02 | |* 3 | INDEX RANGE SCAN | IDX_TEST05 | 8378 | | 29 (0) | 00:00:01 | ------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 3 - access("OBJECT_ID"=1)
经过对比CBO最终选择了索引,当我们更新完统计信息,CBO选择了正确的执行计划。