使用绑定变量可以减少SQL PARSE,但是使用绑定变量有一个不好的地方,就是对于访
问具有倾斜的列,可能使用错误的执行计划。在Oracle 9i之前,如果WHERE 条件里面全
部使用绑定变量,那么只能使用固定的选择性参数来确定执行计划。
=操作和>=操作的选择性为5%,范围扫描的选择性为25%。缺省值的方式可能生成不好的执
行计划。所以Oracle 9i就出现了一个新的技术,bind peeking。什么是bind peeking呢
?当SQL第一次执行的时候,优化器会根据绑定变量来确定执行计划(如果存在柱状图)
。BIND PEEKING只有当该SQL第一次执行的时候,进行HARD PARSE的时候才进行,第二次
调用该SQL,就不会再次进行BIND PEEKING。这种情况下,就存在另外一个风险,如果某
个列的倾斜性很厉害,那么使用BIND PEEKING就是不安全的,因为不同的参数代入,只能
走第一次执行时的执行计划,那么执行计划就像掷色子一样,要靠运气了。碰到这种情况
,应用就不应该使用绑定变量,而应该改为直接值了。
这时可以使用刷新一下共享池alter system flush shared_pool;
或者alter session set "_optim_peek_user_binds"=false;
我们可以通过隐含的参数来调整数据库默认的bind peeking行为:
_OPTIM_PEEK_USER_BINDS。 如果我们想关闭Bind Variable Peeking,我们可以设置该参
数为 False 即可。
SQL>alter session set "_optim_peek_user_binds"=false
使用了Bind Var能提高性能主要是因为这样做可以尽量避免不必要的硬分析(Hard Parse)
而节约了时间,同时节约了大量的CPU资源。
当一个Client提交一条Sql给Oracle后,Oracle 首先会对其进行解析(Parse),然后
将解析结果提交给优化器(Optimiser)来进行优化而取得Oracle认为的最优的Query Plan
,然后再按照这个最优的Plan来执行这个Sql语句(当然在这之中如果只需要软解析的话会
少部分步骤)。
当Oracle接到 Client提交的Sql后会首先在共享池(Shared Pool)里面去查找是否有之前
已经解析好的与刚接到的这一个Sql完全相同的Sql(注意这里说的是完全相同,既要求语
句上的字符级别的完全相同,又要求涉及的对象也必须完全相同)。当发现有相同的以后
解析器就不再对新的Sql在此解析而直接用之前解析好的结果了。这里就节约了解析时间
以及解析时候消耗的CPU资源。尤其是在OLTP中运行着的大量的短小Sql,效果就会比较明
显了。因为一条两条Sql的时间可能不会有多少感觉,但是当量大了以后就会有比较明显
的感觉了。
但是,使用绑定变量的一个缺点是,给出的执行计划并不一定就是SQL在真正应用程序里
所使用的执行计划。这时我们就可以通过 event 10053 事件来查看。