简答题 2.  什么是自适应游标共享?
【正确答案】游标共享(Cursor Sharing)是指共享游标(Shared Cursor)之间的共享,游标共享可以实现重用存储在子游标(Child Cursor)中的解析树和执行计划而不用从头开始做硬解析,从而提高系统性能。特别对于同一类型的目标SQL更应该实现游标共享,而使用绑定变量就可以实现游标共享。
   由于很多OLTP类型的应用系统的开发人员在开发阶段并未意识到硬解析的危害,所以也没有使用绑定变量,等到系统上线后才发现问题。此时若要使用绑定变量,则意味着绝大多数SQL都得改写,但这个代价就太大了,所以Oracle引入了常规游标共享。
   先介绍一下相关的几个概念:
   1)安全的谓词条件是指如果一个谓词条件所在的目标SQL的执行计划并不随该谓词条件的输入值的变化而变化,那么该谓词条件就是安全的。比如,对于主键列施加等值查询的谓词条件,无论传入的主键值是什么,其执行计划都是固定的,不会变化。
   2)不安全的谓词条件是指如果目标SQL的执行计划可能会随着谓词条件的输入值的不同而发生变化,那么该谓词条件就是一个不安全的谓词条件。Oracle数据库中典型的不安全的谓词条件有范围查询(使用了>、>=、<、<=、BETWEEN的谓词条件),使用了带通配符(%)的LIKE,以及对有直方图统计信息的目标列施加的等值查询等。
   3)同一类型SQL是指除SQL文本中对应的输入值不同外,其他部分都一模一样的目标SQL,例如,“SELECT ENAME FROM EMP WHERE EMPNO=7369”和“SELECT ENAME FROM EMP WHERE EMPNO=7370”就是同一类型的SQL。
   下面分别来介绍常规游标共享和自适应游标共享这两个方面。
   1)常规游标共享。常规游标共享是在Oracle 8i中引入的。常规游标共享可以做到既有效降低系统硬解析的数量,又对应用透明,即常规游标共享可以做到在应用不改一行代码的情况下,使那些仅仅是SQL文本中的WHERE条件或者INSERT语句的VALUES子句中的具体输入值不同的目标SQL彼此之间共享解析树和执行计划。当开启了常规游标共享后,Oracle在实际解析目标SQL之前,会先用系统产生的绑定变量来替换目标SQL的SQL文本中WHERE条件或者INSERT中的VALUES子句中的具体输入值,这样替换后实际执行的SQL就已经是使用了绑定变量的改写后的等价SQL。Oracle数据库里系统产生的绑定变量的命名规则是“:"SYS_B_n"(n=0,1,2,......)”。例如,原目标SQL为“SELECT ENAME FROM EMP WHERE EMPNO=7369”,如果开启了常规游标共享,那么Oracle做替换后的等价改写形式就是“SELECT ENAME FROM EMP WHERE EMPNO=:"SYS_B_0"”。
   Oracle数据库中的常规游标共享受参数CURSOR_SHARING的控制,其值可以被设置为EXACT、SIMILAR或FORCE,它们各自的含义如下:
   ①EXACT表示Oracle不会用系统产生的绑定变量来替换目标SQL的SQL文本中WHERE条件或者INSERT语句的VALUES子句中的具体输入值,EXACT是CURSOR SHARlNG的默认值。
   ②SIMILAR表示Oracle会用系统产生的绑定变量来替换目标SQL的SQL文本中WHERE条件或者INSERT语句的VALUES子句中的具体输入值。在这种情况下,Oracle只会对那些它认为是安全的谓词条件在替换后重用解析树和执行计划,对于它认为的不安全的谓词条件,既便用系统产生的绑定变量替换后的SQL文本是一模一样的,对于每一个不同的输入值,Oracle都会执行一次硬解析,即此时会出现一个Parent Cursor下挂一堆Child Cursor的现象,而这些Child Cursor中存储的解析树和执行计划很可能是一样的(需要注意的是,因为自适应游标共享的引入,这种行为不再适用于Oracle 11g及其后续的版本)。在Oracle 12c以及后续的版本中SIMILAR将过时,不再被继续支持。因为当CURSOR_SHARING设成SIMILAR后会带来一系列的问题,并且有太多与SIMILAR相关的Bug。
   ③FORCE和SIMILAR一样,FORCE表示Oracle会用系统产生的绑定变量来替换目标SQL的SQL文本中WHERE条件或者INSERT语句的VALUES子句中的具体输入值。但和SIMILAR不同的是,当CURSOR_SHARING的值为FORCE时,替换后同一类型的SQL总是会无条件地重用之前硬解析时的解析树和执行计划(需要注意的是,因为自适应游标共享的引入,这种行为不再适用于Oracle 11g及其后续的版本)。
   下面给出一些游标不能共享的示例:
   第一组,表名大小写,空格不同:
   
   第二组,输入值不同:
   
   第三组,表所属用户不同:
           
   2)自适应游标共享。绑定变量窥探的缺点是使用了绑定变量的目标SQL只会沿用之前硬解析时所产生的解析树和执行计划,即使这种沿用完全不适合于当前的情形。从Oracle 10g开始,Oracle会自动收集直方图统计信息,这意味着与之前的版本相比,Oracle有更大的概率会知道目标列实际数据的分布情况,也就是说绑定变量窥探的副作用将会更加明显。当Oracle执行绑定变量窥探操作时绑定变量所对应的输入值是否具有代表性就至关重要了(这里“代表性”是指该输入值所对应的执行计划和该SQL在大多数情况下的执行计划相同),因为这会直接决定此目标SQL在硬解析时所选择的执行计划,进而决定后续以软解析/软软解析重复执行时所沿用的执行计划。
   为了解决上述绑定变量窥探所带来的问题,Oracle在11g中引入了白适应游标共享(Adaptive Cursor Sharing,ACS)。自适应游标共享可以让使用了绑定变量的目标SQL在启用了绑定变量窥探的前提条件下,不再像之前那样必须刻板地只沿用之前硬解析时所产生的解析树和执行计划,而是让目标SQL在其可能的多个执行计划之间“自适应”地做出选择。白适应游标共享的核心就在其能“自适应”地选择执行计划,从而就在一定程度上避免了绑定变量窥探的副作用。Oracle只需要在它认为目标SQL的执行计划可能发生变化时,触发该SQL再做一次硬解析就好了。因为一旦触发了硬解析这个动作,Oracle就会将目标SQL重新再解析一遍,其中就包括对该SQL再做一次绑定变量窥探。显然,再做一次绑定变量窥探后所对应的执行计划就是当前情形下CBO认为的最优执行计划,这个执行计划很可能和该SQL硬解析时所产生的执行计划不一样了。也就是说,一个简单的适时触发目标SQL再做一次硬解析的动作就在一定程度上缓解了绑定变量窥探所带来的副作用。
   那么Oracle会在什么时候触发上述硬解析动作?或者说这里的“适时触发”的具体含义是什么?总的来说,Oracle会根据执行目标SQL时所对应的runtime统计信息(比如所耗费的逻辑读和CPU时间,对应结果集的行数等)的变化,以及当前传入的绑定变量输入值所在的谓词条件的可选择率,来综合判断是否需要触发目标SQL的硬解析动作。
   先介绍Oracle数据库中与自适应游标共享相关的Bind Sensitive和Bind Aware,见表。
   
【答案解析】