深入了解Oracle优化器如何使用统计信息不仅对学习Oracle sql优化的原理有帮助,对实际运维中的sql优化也十分有帮助。
Oracle 10053事件是对Oracle优化器的跟踪事件,这个事件除了在我们遇到sql执行计划问题时用它来找到优化器为什么选择次优化或者非优化的执行计划外,还有一个
用途也十分重要,就是用这个事件来了解、探究Oracle 数据库优化器的机制,通过这个事件的跟踪文件,可以弄清除Oracle优化器怎样进行语句转换、使用统计信息、选择join方法、确定
最终的执行计划。
在Oracle优化器确定最终执行计划时,统计信息是非常重要的影响因素。优化器使用哪些统计信息?怎样使用统计信息来计算执行计划每个操作的成本?为什么统计信息的
改变会导致执行计划的异常?通过10053事件获取的跟踪文件可以给我们一个比较直观和具体的答案。
学习中比较有效的途径是从简单到复杂,先从简单、基础的问题着手逐渐理解复杂、高级的问题。基于这个原则,这篇文章只涉及Oracle 优化器对统计信息的使用和访问路径的分析,
对于语句转换、join选择等其它有关执行计划部分不予解释说明。
1 Oracle 10053事件的开启和关闭
Oracle 10053事件有两个级别,级别1的信息比级别2更加详细,增加了优化器的参数信息以及索引的统计状态信息,使用下面这条命令开启这个事件:SQL> ALTER SESSION SET EVENTS='10053 trace name context forever, level 1';
关闭事件则使用下面这条命令:
SQL> ALTER SESSION SET EVENTS '10053 trace name context off';
这个事件跟踪的是优化器对sql语句的解析过程,如果sql语句已经解析过,执行计划已经缓存到数据库的共享池内,则需要使用下面的命令刷新一下共享池,使要跟踪的sql语句重新解析:
SQL> alter system flush shared_pool;
2 实验的场景和解析的sql语句
实验的场景也很简单,在一个100万条数据的表上运行一条基于等值条件查询的sql语句,表的定义如下:SQL> select * from model_tab where rnum>=100000 and rownum=1;
D RNUM
---------- ----------
99999 100000
这个表rnum列上建有非唯一的索引,要执行的sql语句如下:
select * from model_tab where RNUM=200;
实验过程也比较简单,按照顺序执行下面四条命令即可:
alter system flush shared_pool;
ALTER SESSION SET EVENTS='10053 trace name context forever, level 1';
select * from model_tab where RNUM=200;
ALTER SESSION SET EVENTS '10053 trace name context off';
关闭10053事件后,查询v$diag_info视图获得当前会话的跟踪文件,这个文件里的内容就是10053事件对优化器的跟踪信息。这个事件的跟踪文件里面包含的信息非常多,少则数百行,
多则上千行,这里只截取文件关于统计信息和访问路径分析的部分予以分析。
3 10053事件跟踪文件里的统计信息
这个简单的sql语句的跟踪文件里,统计信息由两部分,第一部分是系统统计信息:-----------------------------
SYSTEM STATISTICS INFORMATION
-----------------------------
Using dictionary system stats.
Using NOWORKLOAD Stats
CPUSPEEDNW: 3107 millions instructions/sec (default is 100) --NOWORKLOAD下的cpuspeed
IOTFRSPEED: 4096 bytes per millisecond (default is 4096)
IOSEEKTIM: 10 milliseconds (default is 10)
MBRC: NO VALUE blocks (default is 8)--
可以看到,优化器使用的非工作负载(NOWORKLOAD)的统计信息,优化器可能使用两种系统统计信息,一种是非工作负载及空载状态下采集的,另一种是应用(application)状态下采集
的,是在应用真实负载下采集的。优化器会优先使用应用状态统计信息,如果应用状态下的统计信息不可用,则采用空载下的统计信息。
这里四条数据分别是空载下的cpu速度(CPUSPEEDNW),即cpu每秒执行的指令数,io传输速度(IOTFRSPEED),io(数据块)查找时间(IOSEEKTIM),多块读时每次读的块数(MBRC),
MBRC没有值,采用缺省值8.
统计信息里面的第二部分是基本统计信息,是关于表和索引的:
***************************************
BASE STATISTICAL INFORMATION
***********************
Table Stats::
Table: MODEL_TAB Alias: MODEL_TAB
#Rows: 1000000 SSZ: 0 LGR: 0 #Blks: 2114 AvgRowLen: 10.00 NEB: 0 ChainCnt: 0.00 ScanRate: 0.00 SPC: 0 RFL: 0 RNF: 0 CBK: 0 CHR: 0 KQDFLG: 1
#IMCUs: 0 IMCRowCnt: 0 IMCJournalRowCnt: 0 #IMCBlocks: 0 IMCQuotient: 0.000000
Index Stats::
Index: IDX_RNUM Col#: 2
LVLS: 2 #LB: 2226 #DK: 1000000 LB/K: 1.00 DB/K: 1.00 CLUF: 2072.00 NRW: 1000000.00 SSZ: 0.00 LGR: 0.00 CBK: 0.00 GQL: 0.00 CHR: 0.00 KQDFLG: 8192 BSZ: 1
KKEISFLG: 1
try to generate single-table filter predicates from ORs for query block SEL$1 (#0)
finally: "MODEL_TAB"."RNUM"=200
表的统计信息里面有行数(#Rows),块数(#Blks),行平均长度(AvgRowLen)等,索引的统计信息有索引级数(LVLS),叶子块数(#LB),不同的键值数(#DK),每个
键值的平均叶子块(LB/K: 1.00),每个键值的平均数据块(DB/K: 1.00),聚合因素(CLUF)等。这些信息都是优化器在计算访问路径成本时需要用到的。
4 优化器对访问路径的分析
=====================================
Access path analysis for MODEL_TAB
***************************************
SINGLE TABLE ACCESS PATH
Single Table Cardinality Estimation for MODEL_TAB[MODEL_TAB]
SPD: Return code in qosdDSDirSetup: NOCTX, estType = TABLE
kkecdn: Single Table Predicate:"MODEL_TAB"."RNUM"=200
Column (#2): RNUM(NUMBER)
AvgLen: 5 NDV: 1000000 Nulls: 0 Density: 0.000001 Min: 1.000000 Max: 1000000.000000
Estimated selectivity: 1.0000e-06 , col: #2
Table: MODEL_TAB Alias: MODEL_TAB
Card: Original: 1000000.000000 Rounded: 1 Computed: 1.000000 Non Adjusted: 1.000000
Scan IO Cost (Disk) = 574.000000
Scan CPU Cost (Disk) = 185054724.160000
Cost of predicates:
io = NOCOST, cpu = 50.000000, sel = 0.000001 flag = 2048 ("MODEL_TAB"."RNUM"=200)
Total Scan IO Cost = 574.000000 (scan (Disk))
+ 0.000000 (io filter eval) (= 0.000000 (per row) * 1000000.000000 (#rows))
= 574.000000
Total Scan CPU Cost = 185054724.160000 (scan (Disk))
+ 50000000.000000 (cpu filter eval) (= 50.000000 (per row) * 1000000.000000 (#rows))
= 235054724.160000
Access Path: TableScan
Cost: 580.304537 Resp: 580.304537 Degree: 0
Cost_io: 574.000000 Cost_cpu: 235054724
Resp_io: 574.000000 Resp_cpu: 235054724
****** Costing Index IDX_RNUM
SPD: Return code in qosdDSDirSetup: NOCTX, estType = INDEX_SCAN
SPD: Return code in qosdDSDirSetup: NOCTX, estType = INDEX_FILTER
Estimated selectivity: 1.0000e-06 , col: #2
Access Path: index (AllEqRange)
Index: IDX_RNUM
resc_io: 4.000000 resc_cpu: 28856
ix_sel: 1.0000e-06 ix_sel_with_filters: 1.0000e-06
Cost: 4.000774 Resp: 4.000774 Degree: 1
Best:: AccessPath: IndexRange
Index: IDX_RNUM
Cost: 4.000774 Degree: 1 Resp: 4.000774 Card: 1.000000 Bytes: 0.000000
从上面看到,优化器对表MODEL_TAB的访问路径进行了分析,这个表一共两个访问路径,一个是表扫描,一个是索引范围扫描,表扫描的成本有两项,
扫描成本和表达式计算成本,这两项成本都分别由io成本和cpu成本组成,得到这两个操作的io和cpu成本之后,分别计算表扫描操作的io和cpu总成本,然后计算表扫描
的总成本。在计算总成本时,优化器将cpu成本转换成io成本,转化的公式是:
cpu_to_io=cpu_cost/CPUSPEEDNW/sreadtim;
cpu成本是需要执行的cpu指令数量,除以每毫秒执行的指令数后得到执行需要的时间,除以sreadtim后得到对应的io成本,可以看到io成本是需要执行的io数量。
sreadtim根据公式sreadtim= ioseektim+db_block_size/IOTFRSPEED计算,这个公式计算的是块查找时间加上块的传输之间。
对索引的成本计算优化器给出的信息比较少,直接给出了io和cpu成本,IO成本是4,即4个io,这里可以猜测一下,两级的索引有三层,访问到叶子节点需要3个io,还有一个IO可能是范围扫描
确定扫描终点的io,也可能是回表的io,不是十分确定 。
对这两个路径进行分析后,优化器比较它们的成本,给出了对这个表访问的最佳路径,这里是范围扫描。
5 小结
从这个简单的语句的10053事件跟踪文件里基本可以了解到优化器使用哪些统计信息,如何使用统计信息,怎样进行路径分析。当然这个示例比较简单,这里也没有对优化器如何使用真实负载系统统计信息进行演示。




