绑定变量信息对于使用绑定变量,并且在解析计划时启用了绑定变量窥视特性的语句,在使用DBMS_XPLAN显示执行计划时,选择’ADVANCED’预定义格式作为参数或者加入’PEEKED_BINDS’控制字符串,可以在输出中看到以下内容:
其中,数字1为关联的操作ID,:A为绑定变量名(括号中为变量数据类型,对于字符类型,还有其字符集的ID号),最后为解析计划时,该变量所窥视到的数值。
绑定变量(Bind Variable)是PLSQL的一个重要特性。我们在描述SQL的处理过程中提到:SQL被提交到Oracle后,会被哈希化,检查该语句是否已经存在于内存中,以决定是否进行硬解析。而语句的细微差别(如大小写、注释、空格等)都会导致产生不同的哈希值,引起硬解析。而硬解析是一个相当消耗CPU的过程。
通常,在应用中,同一条语句在不同的会话中可能会使用不同的数值作为参数。例如,一个系统登录模块,不同的用户登录时,会输入不同的用户名、密码作为参数,引发系统执行用于查询用户信息的语句。这样,任何一个用户都会导致这条语句得到一个不同的哈希值,从而导致对其进行硬解析。而绑定变量使Oracle避免了此类重复的硬解析。
使用绑定变量的语句进行解析时,变量并不会代入具体数据,而是以:VARIABLE的形式出现在语句中,在语句执行时,再将变量代入。绑定变量的引入,可以帮助系统减少硬解析。但是,我们之前提到,CBO是对数据敏感的优化器,在使用绑定变量对语句进行执行计划选择时,如果不考虑实际数据的分布性,可能会导致不能获取到最优的执行计划。
例如,某张表上有一个字段COL1,COL1上建有索引,但其数据分布非常不均衡:其99%数值为A,%1的数值为其他,如B、C…在对该表以字段COL1进行条件查询时,如果查询数值为A的数据记录,则使用全表扫描比使用索引访问效率更高(全表扫描是多数据块读,一次读入多个数据块;索引扫描一次读入单个数据块,并且需要访问索引和表两个对象);在查询其他数据时,使用索引访问会使语句的性能更好。如果在解析语句时,没有考虑实际的参数值,就可能会导致优化器选择一个错误的执行计划。为了解决这个问题,在Oracle 9i中引入了绑定变量窥视(Bind VariablePeeking)特性。即在解析含有绑定变量的语句时,会“窥视”其具体数值以获取最优的执行计划。不过,这一特性并不完善。
以上述例子为例,如果解析语句时,窥视到的数值为A,相应执行计划则为全表扫描。但如果该语句的其他执行参数为非A数值,那么该执行计划则会导致这些执行出现性能问题。事实上,在9i和10g的系统,绑定变量这一缺陷导致的性能问题屡见不鲜,而我们的解决手段通常是禁用绑定变量窥视特性(参数_optim_peek_user_ binds控制),使用存储概要(StoredOutline)、SQL配置文件(SQL Profile)或提示等方法强制改变执行计划。在11g中,自适应游标共享(Adatpive Cursor Sharing)特性可以解决这一问题:它会比较绑定变量不同数值的执行计划的效率,相应的选择最优的执行计划。