ORACLE 深入解析10053事件

简介:
新年新说:
新年伊始,2012年过去了,我们又踏上了2013年的,回顾2012我们付出了很多,辛勤和汗水换来了知识和友谊,当我们技术成长的时候我才发现长路漫漫,唯心可敬。一份耕耘一份收获,走技术之路是艰辛的 孤独的 漫长的,在此向刚入门的小伙子们,说一说心得体会。做好心理准备,可能你为了小小的虚荣心,为了生活所迫,才走上此路,但你也要走的洒脱 走的稳健,当你站在第一个里程碑时回顾来时路,你会发现你的收获是值得的,你的付出是有意思的,你才能有继续走下去的勇气。我要感谢 Alantany  tigerfish  海哥  张老师  飚哥  寅总 dingjun  晶晶 mm 蓓蓓 还有 好多好多帮助过我的人们,我的成长离不开你们的鼓励。飙完泪之后开始上干货吧:)
      
ORACLE 深入解析10053事件
本次我们主要讲解oracle 10053事件和实验,好多朋友可能对这个事件不是很熟悉,因为在日常运维中用到的不是很多。Oracle 1004610053 都是非官方trace sql的方法,在官方文档上是找不到相关资料的,但在MOS上可以找到。sql_trace是官方推荐的trace sql的方法,在官方文档上是可以查询出来的。
10053事件:用来描述oracle如何选择执行计划的过程,然后输出到trace文件里,共我们参考,因为我们经常看执行计划怎么执行的消耗了哪些资源,而不是常看执行计划怎么选择出来了的。
10053场景:当SQL语句执行时走的是错误的执行计划,而又找不到原因时,这时请用10053来分析一下原因。
10053特点:
1)只可以了解oracle执行计划的选择过程
2)无法获知代价的计算公式,因为这是oracle内部的商业机密,而且每个oracle版本的优化器计算公式都不相同差距还是蛮大的,不同版本的同一个语句的代价也不一样,优化器现在还不是很成熟,还有待完善。
3)在这个里面我们重点要了解的是“代价”是如何计算出来的,然后我们才能了解执行计划是如何选择的。
4)在10053中可以了解哪些因素影响sql的执行代价
5oracle 8i cost等价IO资源消耗   9i以后cost等价IO+CPU+网络+等待事件+其他代价
一般IO资源的权重比较大 CPU权重较小
10053内容:
参数区:初始化参数,隐含参数,这些参数可以左右oracle工作方式
SQL区:执行的SQL语句,是否使用绑定变量,是否进行了转换操作
系统信息区:操作系统统计信息  cpu主频 CPU执行时间 IO寻址时间 单块读时间 多块读时间
对象统计信息区:
数据访问方式:访问方式不一样计算代价的方法也不一样,全表扫描 走索引 多表关联 代价都不同
关联查询:把每张表都作为驱动表去组合,择优选择“代价”最小的关联方式,与哪个表在前无关系
代价的最后修正:oracle会对选择出来的代价再进行最后的修正,使其更准确一些,更合理一些
选择出最终执行计划:这个过程是非常快速的,毫秒级就搞定啦
实验环境
LEO1@LEO1> select * from v$version;                     这是我的oracle edition
BANNER
--------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - 64bit Production
PL/SQL Release 11.2.0.1.0 - Production
CORE    11.2.0.1.0      Production
TNS for Linux: Version 11.2.0.1.0 - Production
NLSRTL Version 11.2.0.1.0 - Production

1.验证全表扫描的成本计算公式,贴出执行计划和计算公式。
LEO1@LEO1> col sname for a20
LEO1@LEO1> col pname for a20
LEO1@LEO1> col pual1 for a30
LEO1@LEO1> col pual2 for a30
LEO1@LEO1> select * from sys.aux_stats$;    查看操作系统统计信息
SNAME              PNAME                  PVAL1    PVAL2
-------------------- -------------------- ---------- ---------------------------------------------------
SYSSTATS_INFO        STATUS                             COMPLETED
SYSSTATS_INFO        DSTART                             08-15-2009 00:49
SYSSTATS_INFO        DSTOP                              08-15-2009 00:49
SYSSTATS_INFO        FLAGS                         1
SYSSTATS_MAIN        CPUSPEEDNW            2657.0122  
SYSSTATS_MAIN        IOSEEKTIM                    10   
SYSSTATS_MAIN        IOTFRSPEED                 4096
SYSSTATS_MAIN        SREADTIM      
SYSSTATS_MAIN        MREADTIM      
SYSSTATS_MAIN        CPUSPEED      
SYSSTATS_MAIN        MBRC      
SYSSTATS_MAIN        MAXTHR
SYSSTATS_MAIN        SLAVETHR
说明
aux_stats$sys管理员用户下的一个基表后缀为$,必须写schema才能查询到,所谓的基表就是给动态性能视图提供数据的原始表,由于基表非常重要,oracle规定不允许直接访问和修改基表,如果你比较了解这些那么另说了。这个表中记录了“操作系统统计信息”。Oracle会利用操作系统统计信息来修正执行计划的代价,也就是说这些信息是影响代价计算的因素之一。
注意:如果oracle收集了操作系统统计信息,那么CBO采用工作量统计模式计算代价
      如果oracle没有收集操作系统统计信息,那么CBO采用非工作量统计模式计算代价,看上面MBRC没有参数值就说明还没有收集操作系统统计信息
这两个模式计算代价的公式是不同的。
SNAME:是指操作系统统计信息
PNAMEparameter name 参数名
PVAL1:参数值
PVAL2:参数值
参数解释
FLAGS:标志
CPUSPEEDNW:非工作量统计模式下CPU主频,直接来自硬件
IOSEEKTIMIO寻址时间(毫秒),直接来自硬件
IOTFRSPEEDIO传输速率(字节/毫秒)
SREADTIM:读取单个数据块的平均时间
MREADTIM:读取多个数据块的平均时间
CPUSPEED:工作量统计模式下CPU主频,根据当前工作量评估出一个合理值
MBRCoracle收集完统计信息后评估出的一次多块读可以读几个数据块db_file_multiblock_read_count
MAXTHR:最大IO吞吐量(字节/秒)
SLAVETHR:平均IO吞吐量(字节/秒)
后面这6个参数是在oracle收集完统计信息后才能得出的参数值,有什么用呢?我来解释一下下
CBO在计算SQL语句的代价时,需要使用数据库对象例如表 索引 等对象统计数据,还要使用操作系统统计数据例如CPU周期 IO速度 数据块读时间等,选择花费时间最少的执行计划为最佳执行计划。
Oracle使用dbms_stats.gather_system_stats存储过程来收集操作系统统计信息,收集来的数据存放在sys.aux_stats$表中,如果我们做了收集操作那么会有统计数据,如果没有做就没有统计数据,这两种计算代价的方法是不同的,后续会讲。
dbms_stats.gather_system_stats语法
execute  dbms_stats.gather_system_stats
gathering_mode varchar2 default ‘noworkload’
interval integer default null,
stattab varchar2 default null,
statid varchar2 default null,
statown varchar2 default null);
解释
gathering_mode 参数,默认值“noworkload”,还可以设置为“workload”含义
noworkload:非工作量统计模式,收集上来的数据都是来自硬件
workload:工作量统计模式,收集上来的数据需要在特定的数据库负载间隔内统计出来的,这样的数据才能真实反映出数据库的操作系统参数(需要执行sql测评出来)
interval:可以指定收集统计信息的时间间隔,例如 5 收集5分钟的统计信息
命令:execute dbms_stats.gather_system_stats(‘noworkload’,5);  
STARTSTOP关键字自己决定何时开始何时结束收集统计信息
命令:execute dbms_stats.gather_system_stats(‘start’);
上下两条指令间隔3分钟执行,然后把这3分钟的统计信息写入到sys.aux_stats$表里面
execute dbms_stats.gather_system_stats(‘stop’);
注意:上面有个MBRC参数我想多聊一下,它是初始化参数db_file_multiblock_read_count的简写中文翻译“一次读多少个数据块or一次多块读可以读几个数据块”,如果收集了统计信息那么CBO会用MBRC计算代价,如果没有收集统计信息CBO会用这个初始化参数db_file_multiblock_read_count计算代价。
LEO1@LEO1> show parameter db_file_multiblock_read_count     这是我机器上参数默认值
NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------------------
db_file_multiblock_read_count            integer       79
LEO1@LEO1> show parameter db_block_size                   我们的一个块大小为8k
NAME                                 TYPE        VALUE
------------------------------------ ----------- ---------------------------------------------
db_block_size                           integer      8192
这个参数值并不是无限大的,大多数平台下的oracle都是128。一般oracle block size =8k
128*8=1M,也就是说1M是大多数操作系统一次最大IO的限制,如果还有其他限制要从这1M里面扣除,初始化参数db_file_multiblock_read_count的最大值之所以定为128,也是为了保守策略。
79*8k=632K
测试
LEO1@LEO1> drop table leo1 purge;                      清空环境
Table dropped.
LEO1@LEO1> create table leo1 as select * from dba_objects;   创建leo1
Table created.
LEO1@LEO1> begin
     dbms_stats.gather_table_stats(                      收集表的统计信息
          ownname=>'leo1',                            用户名
          tabname=>'leo1',                             表名
          cascade=>true,                               级联操作
          estimate_percent=>null,                       全表采样
          method_opt=>'for all columns size 1');   不作直方图分析,减小代价计算的影响
end;
/
  2    3    4    5    6    7    8    9  
PL/SQL procedure successfully completed.
LEO1@LEO1> show parameter db_file_multiblock_read_count      
NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
db_file_multiblock_read_count           integer     79
LEO1@LEO1> alter session set db_file_multiblock_read_count=16;    把多块读参数修改成16方便计算
Session altered.
LEO1@LEO1> show parameter db_file_multiblock_read_count
NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
db_file_multiblock_read_count           integer     16
LEO1@LEO1> select * from sys.aux_stats$;         没有收集操作系统统计信息
SNAME                PNAME                     PVAL1 PVAL2
-------------------- -------------------- ---------- ------------------------------
SYSSTATS_INFO        STATUS                          COMPLETED
SYSSTATS_INFO        DSTART                          08-15-2009 00:49
SYSSTATS_INFO        DSTOP                           08-15-2009 00:49
SYSSTATS_INFO        FLAGS                         1
SYSSTATS_MAIN        CPUSPEEDNW            2657.0122
SYSSTATS_MAIN        IOSEEKTIM                    10
SYSSTATS_MAIN        IOTFRSPEED                 4096
SYSSTATS_MAIN        SREADTIM
SYSSTATS_MAIN        MREADTIM
SYSSTATS_MAIN        CPUSPEED
SYSSTATS_MAIN        MBRC
SYSSTATS_MAIN        MAXTHR
SYSSTATS_MAIN        SLAVETHR
我们没有收集操作系统统计信息,所以CBO采用了非工作量统计模式(noworkload)来计算代价
LEO1@LEO1> select blocks from user_tables where table_name='LEO1';   LEO1表总数据块为1051
    BLOCKS
------------------
      1051
LEO1@LEO1> set autotrace trace explain
LEO1@LEO1> select * from leo1;
Execution Plan
----------------------------------------------------------
Plan hash value: 2716644435
--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      | 71968 |  6817K|   233   (1)| 00:00:03 |
|   1 |  TABLE ACCESS FULL| LEO1  | 71968 |  6817K|   233   (1)| 00:00:03 |
--------------------------------------------------------------------------
全表扫描的成本等于233,其中CPU代价占整个权重百分比的1%
###################################################################################
成本的计算公式如下:
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采用非工作量统计模式计算代价我们现在处于“非工作量统计模式”
#SRds=0,因为是全表扫描,单块读为0,全都使用的是多块读
#MRds= 表的块数/多块读参数=1051/16=65.6875
mreadtim=ioseektim+db_file_multiblock_count*db_block_size/iotftspeed=10+16*8192/4096=42
sreadtim=ioseektim+db_block_size/iotfrspeed=10+8192/4096=12
CPUCycles 等于 PLAN_TABLE里面的CPU_COST
LEO1@LEO1> explain plan for select * from leo1;
Explained.
LEO1@LEO1> select cpu_cost from plan_table;
  CPU_COST
-----------------
  38430873
cpuspeed 等于 CPUSPEEDNW= 2657.0122
COST=65.6875*42/12+38430873/2657.0122/12/1000(毫秒换算成秒)=229.90625+1.20532=231.11157
229.90625 IO代价
1.20532   CPU代价
手工计算出来的COST用四舍五入等于232,和我们看到的233有差别,这是由于隐含参数_table_scan_cost_plus_one参数造成的
LEO1@LEO1> conn / as sysdba        切换到sys用户才能查看隐含参数
SYS@LEO1> 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%';  2    3    4    5  
NAME                     VALUE        DESCRIB
----------------------------------------------------------------------------------------------------------------------------------------------
_table_scan_cost_plus_one    TRUE         bump estimated full table scan and index ffs cost by one
根据该参数的描述,在table full scanindex fast full scan的时候会将cost+1  232+1=233
我们把_table_scan_cost_plus_one参数禁用看看cost变化
SYS@LEO1> alter session set "_table_scan_cost_plus_one"=false;    禁用
Session altered.
SYS@LEO1> 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%';  2    3    4    5    生效
NAME                     VALUE        DESCRIB
----------------------------------------------------------------------------------------------------------------------------------------------
_table_scan_cost_plus_one    FALSE        bump estimated full table scan and index ffs cost by one
SYS@LEO1> select * from leo1.leo1;
Execution Plan
----------------------------------------------------------
Plan hash value: 2716644435
--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      | 71968 |  6817K|   232   (1)| 00:00:03 |
|   1 |  TABLE ACCESS FULL| LEO1  | 71968 |  6817K|   232   (1)| 00:00:03 |
--------------------------------------------------------------------------
这次得到的COST等于232,与计算值正好匹配,这是禁用隐含参数的结果
SYS@LEO1> alter session set db_file_multiblock_read_count=32;   我们修改一下多块读参数
Session altered.
SYS@LEO1> select * from leo1.leo1;
Execution Plan
----------------------------------------------------------
Plan hash value: 2716644435
--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      | 71968 |  6817K|   204   (1)| 00:00:03 |
|   1 |  TABLE ACCESS FULL| LEO1  | 71968 |  6817K|   204   (1)| 00:00:03 |
--------------------------------------------------------------------------
#SRds=0,因为是全表扫描,单块读为0,全都使用的是多块读
#MRds= 表的块数/多块读参数=1051/32=32.84375
mreadtim=ioseektim+db_file_multiblock_count*db_block_size/iotftspeed=10+32*8192/4096=74
sreadtim=ioseektim+db_block_size/iotfrspeed=10+8192/4096=12
CPUCycles=38430873
cpuspeed 等于 CPUSPEEDNW= 2657.0122
COST=32.84375*74/12+38430873/2657.0122/12/1000(毫秒换算成秒)= 202.53645+1.20532=203.74177
四舍五入等于204,与执行计划中COST=204相一致
小结:从实验中可以得出,oracle 11gR2中,全表扫描非工作量统计模式下COST计算公式依然和9i/10g一样,没有变化。同时我们也看到了IO成本占整个代价权重的极大部分,是影响SQL效率的主要因素,需要我们多关注。

2.给出B-tree索引 Unique scan的成本计算公式,贴出执行计划和计算公式。
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
实验
LEO1@LEO1> drop table leo2 purge;                       清理环境
Table dropped.
LEO1@LEO1> create table leo2 as select * from dba_objects;   创建leo2
Table created.
LEO1@LEO1> create index idx_leo2 on leo2(object_id);        创建idx_leo2
Index created.
LEO1@LEO1> begin
     dbms_stats.gather_table_stats(                      收集表的统计信息
          ownname=>'leo1',                            用户名
          tabname=>'leo2',                             表名
          cascade=>true,                               级联操作
          estimate_percent=>null,                       全表采样
          method_opt=>'for all columns size 1');   不作直方图分析,减小代价计算的影响
end;
/
  2    3    4    5    6    7    8    9  
PL/SQL procedure successfully completed.
必须要做分析,如果表没有分析,下面统计信息就没有了
LEO1@LEO1> select index_name,blevel,leaf_blocks,clustering_factor,num_rows,distinct_keys from dba_indexes where index_name='IDX_LEO2';
INDEX_NAME   BLEVEL LEAF_BLOCKS CLUSTERING_FACTOR   NUM_ROWS DISTINCT_KEYS
------------------------------ ---------- ----------- ----------------- ---------- ----------------------- -----------------
IDX_LEO2      1      159         1076                71968     71968
BLEVEL:索引层数  1表示就1
LEAF_BLOCKS:索引树的叶子块数  159
CLUSTERING_FACTOR:索引聚簇因子
NUM_ROWS:有索引的行数    71968和数据行数相匹配
DISTINCT_KEYS:不同的索引键值  71968
LEO1@LEO1> select count(*) from leo2;
  COUNT(*)
-----------------
     71968
LEO1@LEO1> select * from leo2 where object_id=10000;
Execution Plan
----------------------------------------------------------
Plan hash value: 2495991774
----------------------------------------------------------------------------------------
| Id  | Operation                   | Name     | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |          |     1 |    97 |     2   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID | LEO2     |     1 |    97 |     2   (0)| 00:00:01 |
|*  2 |   INDEX UNIQUE SCAN         | IDX_LEO2 |     1 |       |     1   (0)| 00:00:01 |
----------------------------------------------------------------------------------------
COST=2,其中CPU代价=0,等值查询与索引的条数无关,消耗CPU资源可以忽略不计
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("OBJECT_ID"=10000)
公式
Unique scan cost = blevel +1
INDEX UNIQUE SCANCOST=1    就是blevelCBO看看需要递归几层索引,与统计信息中的blevel一致
TABLE ACCESS BY INDEX ROWIDCOST=1  通过索引rowid访问表产生的代价
因此最终COST=1+1=2


 本文转自 leonarding151CTO博客,原文链接: http://blog.51cto.com/leonarding/1139275 ,如需转载请自行联系原作者
相关文章
|
29天前
|
存储 开发框架 开发者
QT C++焦点事件:多角度解析实用技巧与方法
QT C++焦点事件:多角度解析实用技巧与方法
128 0
|
4月前
|
JavaScript 前端开发 C++
Javaweb之javascript事件的详细解析
1.6 JavaScript事件 1.6.1 事件介绍 如下图所示的百度注册页面,当我们用户输入完内容,百度可以自动的提示我们用户名已经存在还是可以使用。那么百度是怎么知道我们用户名输入完了呢?这就需要用到JavaScript中的事件了。
42 0
|
3月前
|
Web App开发 前端开发
Chrome 浏览器插件 V3 版本 Manifest.json 文件中 Action 的类型(Types)、方法(Methods)和事件(Events)的属性和参数解析
Chrome 浏览器插件 V3 版本 Manifest.json 文件中 Action 的类型(Types)、方法(Methods)和事件(Events)的属性和参数解析
152 0
|
29天前
|
测试技术 数据库 C++
Qt C++拖放事件探索之旅:多方法深入解析
Qt C++拖放事件探索之旅:多方法深入解析
115 1
|
1月前
|
缓存 Oracle 关系型数据库
oracle 软软解析
oracle 软软解析
13 0
|
6月前
|
设计模式 Go
Go语言事件系统设计解析:发布-订阅模式实战
Go语言事件系统设计解析:发布-订阅模式实战
164 0
|
4月前
|
Android开发 容器
[Android]View的事件分发机制(源码解析)
[Android]View的事件分发机制(源码解析)
36 0
|
4月前
|
缓存 关系型数据库 MySQL
这个错误提示表明Flink CDC在解析MySQL的二进制日志时,找不到对应表的TableMap事件。
这个错误提示表明Flink CDC在解析MySQL的二进制日志时,找不到对应表的TableMap事件。
145 2
|
4月前
|
Java API 容器
java和VB中按键事件处理:解析 KeyCode、KeyAscii; KeyDown/KeyUp/KeyPress 的关系区别及应用
java和VB中按键事件处理:解析 KeyCode、KeyAscii; KeyDown/KeyUp/KeyPress 的关系区别及应用
|
7月前
|
Java API Nacos
Nacos服务健康检查与服务变动事件发布源码解析
Nacos服务健康检查与服务变动事件发布源码解析
61 0

推荐镜像

更多