ORACLE绑定变量隐式转换导致性能问题

   年后一次系统升级后,监控数据库的工具DPA发现数据库的Total Wait时间突然飙增,如下截图所示,数据库的总体等待时间对比升级前飙增了非常多


外就是发现出现了较多的等待事件,主要有latch: cache buffers chains、 latch: shared pool 、db
file scattered read。根据这边的监控发现TOP
SQL里面从升级前的0次变为了一天的一万多次(有些甚至更多),分析过后我们就找开发人员了解一下系统升级变跟的内容和改动


发人员坚定的告诉们介绍,他只是将他负责的那个模块里面那些拼接SQL(Literal
SQL)语句改写成了绑定变量(因为我们系统大量使用拼接SQL的方式,硬解析非常严重),所以我们一直建议他们使用绑定变量。由于改为绑定变量,所以
DPA以前没有捕获这些SQl,后面因为执行次数激增,所以捕获了这些SQL,也发现其执行次数明显变化了,例如有些SQL语句的执行次数上万了。


面经过分析、跟踪过后发现修改为绑定变量的SQL的实际执行计划变成全表扫描了。这也能解释为什么db file scattered
read等待事件出现,因为全表扫描的缘故。下面就其中的一个SQL语句做分析,如下所示,我们在Toad或SQL
Developer工具里面查看预估执行计划时,其执行计划都是走索引扫描(Index Scan),但是实际执行计划就是走全表扫描

 

 

实际执行计划(截图来自WORKLOAD REPOSITORY SQL Report )


开始我们以为是绑定变量的窥探机制造成(使用SQL首次运行时的值来生成执行计划。后续再次运行该SQL语句则使用首次执行计划来执行),但是分析过后发
现,SC_NO这个字段建有唯一索引,不存在所谓的数据倾斜的情况。非常的纠结,纳闷,不解。同事用10046跟踪了SQL语句(其实是跟踪某个自己在
Toad里面执行的SQL语句,这也是问题一直没有发现的原因),想不明白为什么,隐隐怀疑是数据库的bug来着,直到后面我在sqltrpt.sql查
看某些SQL的调优优化建议,突然看到下面信息:

3- Restructure SQL finding (see plan 1 in explain plans section)

---------------------------------------------------------------------------

The predicate SYS_OP_C2C("SC_NO")=:B1 used at line ID 5 of the execution plan contains an implicit data type conversion on indexed column "SC_NO".This implicit data type conversion prevents the optimizer from selecting indices on table "SC_HD".

Recommendation

--------------------------------------------------------------------------

- Rewrite the predicate into an equivalent form to take advantage of indices.

 


时豁然开朗,肯定是开发人员在使用绑定变量时,使用了不一致的数据类型,导致了隐式转换(implicit data type
conversion),于是联系开发人员确认,要了程序里面的代码,果然如此,SC_NO的数据类型为VARCHAR2,但是在代码里面绑定变量的类型
为OracleType.NVarChar。悲剧的是几乎所有绑定变量都由于开发人员疏忽,都给错了数据类型。所以出现这么严重的情况

...........................................................
param = new OracleParameter(":scNo", OracleType.NVarChar);
            param.Value = Server.UrlDecode(joNo).ToUpper();
            paramsList.Add(param);
...........................................................

 

那么我们下面我们模拟一下绑定变量数据类型不一致,出现隐式转换导致不走索引的情况

SQL> alter system flush shared_pool;
 
System altered.
 
SQL> set autotrace on;
SQL> variable sc_no nvarchar2(20);
SQL> exec :sc_no :='A01Adfddf01I';
 
PL/SQL procedure successfully completed.
 
SQL> select  count(1) from sc_hd 
  2  where sc_no =:sc_no 
  3    and jo_status<>'l2' 
  4    and status<>'x';
 
  COUNT(1)
----------
         0
 
 
Execution Plan
----------------------------------------------------------
Plan hash value: 326413811
 
----------------------------------------------------------------------------------------
| Id  | Operation                    | Name    | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |         |     1 |    16 |     3   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE              |         |     1 |    16 |            |          |
|*  2 |   TABLE ACCESS BY INDEX ROWID| SC_HD   |     1 |    16 |     3   (0)| 00:00:01 |
|*  3 |    INDEX UNIQUE SCAN         | SC_HEAD |     1 |       |     2   (0)| 00:00:01 |
----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - filter("JO_STATUS"<>'l2' AND "STATUS"<>'x')
   3 - access("SC_NO"=:SC_NO)
 
 
Statistics
----------------------------------------------------------
       2082  recursive calls
          6  db block gets
     109260  consistent gets
     108647  physical reads
          0  redo size
        514  bytes sent via SQL*Net to client
        492  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
         48  sorts (memory)
          0  sorts (disk)
          1  rows processed
 
SQL> 

 

 

此时,你查看实际执行计划,就会发现其走全表扫描。如下所示

 


果此时你用一模一样的SQL(空格,字符大小一致,如下所示),在TOAD里面执行,即使你给绑定变量赋予的是VARCHAR2类型的数据,也会发现其实
际执行计划走全表扫描,这个是因为绑定变量窥探,使用SQL首次运行时的值来生成执行计划。后续再次运行该SQL语句则使用首次执行计划来执行的缘故

select  count(1) from sc_hd 
where sc_no =:sc_no 
  and jo_status<>'l2' 
  and status<>'x';

 

如果空格不一致,或大小写,或换行不一致,你又会发现其实际执行计划走索引了,这也是当初我们在不了解应用程序源代码的情况,被这个情况给折腾疯了的情况,以为是数据库的bug引起的。其实还是因为绑定变量的数据类型与实际字段的数据类型不一致而引起的。

时间: 2025-01-14 13:45:37

ORACLE绑定变量隐式转换导致性能问题的相关文章

从Java的类型转换看MySQL和Oracle中的隐式转换(二)

说起数据类型转换,在开发中如此,在数据库中也是如此,之前简单对比过MySQL和Oracle的数据类型转换情况,可以参见MySQL和Oracle中的隐式转换 http://blog.itpub.net/23718752/viewspace-1787973/ 不过当时写完之后,有个读者随口问了一句为什么,为什么呢?似乎自己还是一知半解,说是规则,无规矩不成方圆,倒也无可非议,不过我觉得还是要再看看,看看还能有哪些收获,接下来的内容我就不能保证正确性了,希望大家明辨,也希望提出意见,毕竟就是希望把问题

SQL Server中提前找到隐式转换提升性能的办法

原文:SQL Server中提前找到隐式转换提升性能的办法     http://www.cnblogs.com/shanksgao/p/4254942.html 高兄这篇文章很好的谈论了由于数据隐式转换造成执行计划不准确,从而造成了死锁.那如果在事情出现之前发现了这类潜在的风险岂不是更好?     那么我们来看一个简单的例子,如代码清单1所示.   1: SELECT * 2: FROM HumanResources.Employee 3: WHERE NationalIDNumber = 2

MySQL和Oracle中的隐式转换

今天在处理一个问题的时候,需要根据其他部门提供的sql语句对一个表中的数据进行了筛查. 语句类似下面的形式 > SELECT MAX_LEVEL,LOGOUT_TIME,CURRENT_DATE AS NOWTIME,cn_master FROM t_test_october_back_a WHERE ID in ( 100, 200, 300, 400, 500) ; +-----------+---------------+------------+-----------+ | MAX_LE

Oracle隐式转换会影响物化视图查询重写

今天有人问我一个物化视图查询重写的问题,最后发现问题其实和物化视图的功能没有多大的关系,而是隐式转换导致的问题. 还是通过例子来说明这个问题: SQL> create table t ( 2  id number, 3  time date, 4  other varchar2(4000)) 5  partition by range (time) 6  (partition p1 values less than (to_date('2008-1-1', 'yyyy-mm-dd')), 7  

Oracle中的数据类型隐式转换(implicit conversion)

和其他的关系型数据库一样,oracle中也能进行一些隐式的数据转换,这对我们写SQL语句有非常用,我们可以不必麻烦地手动转化很多类型的字符.虽然前面我们介绍了一些使用例如to_char,to_date的函数进行强制转换的方法,但是隐式转换也还是不错的. Oracle可以隐式地进行一些变量类别之间转化,例如从字符串转换到数值,看下面的例子. SQL> select ename,sal from emp where sal = '1100′;   ENAME SAL ------– ---- SMI

Oracle的隐式转换

都说Oracle存在NUMBER和VARCHAR2类型的隐式转换,严格意义上需要避免,但为何需要避免,从下面的实验进行验证. 1. 创建测试表和索引 create table tn (id number, name varchar2(1)); create index idx_tn on tn (id); create index idx_tn on tn (name); 分别对NUMBER类型的id字段,VARCHAR2类型的name字段创建索引. 2. 查看VARCHAR2->NUMBER的

关于隐式转换

      昨天,一个读者向我提交了一个问题,请我就SQL server 隐式转换发表一些看法.当SQL server遇到一个不匹配类型的表达式的时候,它有两种选择.它使用隐式转换并能够执行或者转换错误而导致执行失败.在深入隐式转换之前,让我们假定错误的情形.       如果一个隐式转换不可能实现,SQL server可能产生两种可能的错误.如果两种数据类型不能完全兼容(简言之,在两种数据类型之间不能实现隐式或显式转换),SQL server产生下列错误: DECLARE @a INT DEC

Java基础之隐式转换vs强制转换_java

Java中,经常可以遇到类型转换的场景,从变量的定义到复制.数值变量的计算到方法的参数传递.基类与派生类间的造型等,随处可见类型转换的身影.Java中的类型转换在Java编码中具有重要的作用. 在定义变量时,有许多要注意的问题,一不小心就会出现损失精度或者不兼容类型等问题. 例如:     1.定义长整型数据时,必须加后缀l或L               long l =123456789012345L     2.定义单精度类型时(7-8位有效数字),必须加后缀 f 或 F         

数据类型的隐式转换

昨天检查程序,发现执行一条sql语句的buffer gets异常的高(看执行计划是正常的),这个表大小10M,而且谓词使用主关键字,跟踪程序才发现,开发人员使用的绑定变量与查询条件关键字段的类型不一致.这个问题竟然隐藏这么久,程序是第三方的,我无法修改代码.我想既然存在隐式转换,如果我建立这个字段的函数型索引是否可以暂时解决这个问题. 以下是测验的例子: 1.建立测验例子以及主索引.CREATE TABLE hideconv AS SELECT to_char(ROWNUM) cserial,