查看oracle执行计划

  订单查询有个分页查询的功能,在线上查询20条数据时速度较慢,耗时在9s左右。后来定位问题是因为一条sql执行过慢。

    系统使用的是mybatis,配置的sql如下:

[sql] view plain copy
 
  1. SELECT * FROM (SELECT T1_.*, rownum ROWNUM_ FROM (  
  2. SELECT  O.* FROM TRADE_ORDER O  
  3. WHERE 1=1  
  4.  AND O.CREATE_TIME <![CDATA[ >= ]]> #{startDate}  
  5. AND O.CREATE_TIME <![CDATA[ < ]]> #{endDate}  
  6.  ORDER BY O.CREATE_TIME DESC  
  7. ) T1_ WHERE ROWNUM <= (#{offset}+#{size})) WHERE ROWNUM_ >= (#{offset}+1)  

    由于mybatis执行sql时使用的是PreparedStatement(绑定参数),因此oracle在解析这条sql时,使用的是软解析。这里说明下硬解析和软解析的区别。

    1、硬解析每次都会对sql进行解析,无法共享执行计划。

    2、软件系会使用一个共享池,对于相同的sql共享执行计划。只有使用了绑定参数的sql才可能会被认为是相同的sql。比如上面那条sql,在oracle执行时,会使用统一的执行计划。

    现在,我们分析该sql为什么会这么慢。

    背景:

    1、在表TRADE_ORDER上的CREATE_TIME上建立了索引。

    2、该表目前有1000w+数据。

    3、CREATE_TIME列中数据比较分散。

    分析:

    1、执行该sql花了9s的时间,一般千万数据量的表做全表扫描时差不多也就这么长时间。

    2、找到该sql的sql id,查看执行计划。

查询sql id:

[sql] view plain copy
 
  1. select * from V$sql t where t.sql_text like 'select * from (select t1.*, rownum ROWNUM_ from ( select o.* from trade_order o%'  

根据sql id查看执行计划:

[sql] view plain copy
 
  1. select plan_table_output from table(dbms_xplan.display_cursor('9zqcz386hrnvk'));  

执行计划为:

    从执行计划可以看出:

    1、create_time没有走索引,而是执行了全表扫描。

    2、2与4的筛选重复了。

    怎么避免全表扫描呢?从执行计划中可以看出,绑定参数进行了计算。

    在编写sql时,尽量避免参数在数据库端进行运算,应该将运算过程放在应用端进行。

    修改sql为:

[sql] view plain copy
 
  1. SELECT * FROM (SELECT T1_.*, rownum ROWNUM_ FROM (  
  2. SELECT  O.* FROM TRADE_ORDER O  
  3. WHERE 1=1  
  4.  AND O.CREATE_TIME <![CDATA[ >= ]]> #{startDate}  
  5. AND O.CREATE_TIME <![CDATA[ < ]]> #{endDate}  
  6.  ORDER BY O.CREATE_TIME DESC  
  7. ) T1_ WHERE ROWNUM <= #{end}) WHERE ROWNUM_ >= #{start}  

    这次执行1s左右就返回了结果。

    按照上面步骤,查看执行计划:


    可以看出,已经正常的走了索引,执行速度提升很多。

    总结:在编写sql时,尽量避免参数在数据库端进行运算,应该将运算过程放在应用端进行。特别是对于绑定参数,在sql层面进行参数运算,可能会超出想象。

原文地址:https://www.cnblogs.com/wuer888/p/7650792.html