当一个SQL出现性能问题时,可以使用SQL_TRACE 或者 10046事件来跟踪SQL,通过生成的trace来了解SQL的执行过程。我们在查看一条SQL的执行计划的时候,只能看到CBO 最终告诉我们的执行计划结果,但是不知道CBO 是根据什么来做的。如果遇到了执行计划异常,可以借助Oracle 10053事件进行跟踪。10053事件是oracle提供的用于跟踪sql语句成本计算的内部事件,它能记载CBO模式下oracle优化器如何计算sql成本,生成相应的执行计划。
通过session级别跟踪:
ALTER SESSION SET EVENTS='10053 trace name context forever, level 1';
或ALTER SESSION SET EVENTS='10053 trace name context forever, level 2';
执行相关sql
explain plan for select count(*) from obj$;
ALTER SESSION SET EVENTS '10053 trace name context off';
对特定session启用跟踪:
通过调用 DBMS_SYSTEM. SET_EV包实现
PROCEDURE SET_EV
Argument Name Type In/Out Default?
------------------------------ ----------------------- ------ --------
SI BINARY_INTEGER IN
SE BINARY_INTEGER IN
EV BINARY_INTEGER IN
LE BINARY_INTEGER IN
NM VARCHAR2 IN
查询v$sessiowww.pizei.comn 视图获取进程信息
SQL> select sid,serial#,username from v$session where username is not null;
SID SERIAL# USERNAME
125 25 SYS
执行跟踪
exec dbms_system.SET_EV(125,25,10053,1,'');
结束跟踪
exec dbms_system.SET_EV(125,25,10053,0,'');
查询系统对应session trace文件
select value from v$diag_info where name = 'Default Trace File';
经过开发人员确认该sql在测试库(LINUX+ 12.2.0.1 单机环境)执行只需要几秒即可(数据量相差不大)完成,其中bs_loan_card_addition、bs_loan_card、bs_loan_contract_addition三张表的数据量都在200万行左右。
获取到生产环境该sql执行计划如下:
测试环境该sql执行计划如下:
首先怀疑统计信息不准确导致CBO在页游访问BS_LOAN_CARD表的时候选择错误,本应该选择BS_LOAN_CARD_I3索引(因为CUSTOMER_NO"='1000193229’的选择性很好)而这也是开发设计这个索引的原因。但结果是选择了BS_LOAN_CARD_I0索引(对应的是BS_LOAN_CARD.LOAN_CARD_NO,该列唯一的),而过滤条件根本没有这个列,他只是作为关联条件与bs_loan_card_addition表进行连接。
因此首先检查统计信息,意外发现BS_LOAN_CARD.LOAN_CARD_NO,列上居然存在一个HYBIRD类型的直方图,理论上来说该值的唯一性非常好,不应该收集直方图,因此直接删除了该列上的直方图,再次检查发现执行计划仍未改变。
尝试使用10053对该SQL执行计划的产生过程进行跟踪,发现如下信息:
而且结合执行计划
这个执行计划简单理解来说就是首先对BS_LOAN_CARD_ADDITION进行全表扫描,然后在这个所得的结果集里面每一行的LOAN_CARD_NO列拿出来到BS_LOAN_CARD里面匹配,这就是第6部里面出现了一个"B"."LOAN_CARD_NO"=:B1原因,而这个就是oracle改写后的结果。而从其选择对BS_LOAN_CARD_ADDITION进行全表扫描就不可避免的导致了效率会很低。而且其后的rows 估算为2444K,这个是贴合实际的,所以之前的删除直方图不会有结果。而测试上的改写结果明显跟这个不一样,排除统计信息的影响,那么就开始怀疑cbo内部的算法选择问题,再结合那个可疑 的 “CBQT bypassed forquery block UPD$1 (#0): Disabled by parameter. ”提示,怀疑优化器参数在两个环境中有区别,
一:_optimizer_cost_based_transformation设为linear(默认值),其有如下值:
"exhaustive", "iterative","linear", "on", "off"。
本例中该参数就是默认值,该参数可控制是否允许CBO进行改写
二:_optimizer_squ_bottomup 参数值为true(默认值).
而生产环境中恰好相反为false,所以生产的trace中会有Disabled by parameter 字眼
_optimizer_squ_bottomup enables unnesting of subquery in a bottom-upmanner;
该参数默认为true,即开启子查询自底向上的展开功能(也就是类似unnest hint的功能),unnest称之为对子查询展开,顾名思义,就是不让子查询孤单地嵌套(nest)在里面。