Oracle自适应游标共享

李召雷
原创 3710       2016-06-17  

自适应游标共享(下面简称为ACS)是Oracle 11g引入的新特性。目的是为了解决具有绑定变量的sql当相关列数据分布倾斜时不能根据绑定值生成恰当的执行计划的问题。这句话有点长,拆解开来说明:首先绑定变量的引入降低了硬解析,每次都直接使用第一次绑定变量窥视产生的执行计划但这带来了一个问题,就是当列数据倾斜时,该执行计划很可能并不合理,造成执行效率低下

为了解决以上问题,Oracle引入了ACS自适应游标共享默认是启用的并且不能被禁用,但不能被应用到绑定变量个数超过14的sql。

需要强调的是,ACS是对柱状图强依赖的,也就是说一定要有非常准确的统计信息和柱状图,ACS才能正常运作


首先给出下面几个概念

bind-sensitive:绑定变量相关的列上存在柱状图,并且进行绑定变量窥视,生成的游标即标记为bind-sensitive。

bind-aware:对于一个bind-sensitive游标,当判断不同的绑定值应该选择不同的执行计划时,则生成一个新游标并标记为bind-aware。如果游标被标记为bind-aware,则在下次执行时,对于新的绑定值会生成新的游标并把原始游标设为不可共享(不可共享游标将不再可用并最终age out出内存)。

游标合并:对于一个bind-aware游标,如果新生成的执行计划和现有的某个游标的相同,则会合并这两个游标,并将较早的一个置为不可共享,同时会扩大这个新游标的选择范围来容纳新的绑定值。


▲接下来以实例进行说明:

创建表并收集统计信息

设置一下环境

次对oc_date=20160531进行查询

Oracle选择了INDEX RANGE SCAN,执行计划表现正常。

这里说明一下后面两个查询的字段含义:

IS_SENSITIVE:用来标记游标是否bind-sensitive,值为Y/N。

IS_AWARE:用来标记游标是否bind-aware,值为Y/N。

IS_SHARE:用来标记游标是否可共享,值为Y/N。

LAST_CAPTURED:绑定变量捕捉时间。

VALUE_STRING:绑定变量值。

注意上面几个字段值的变化。

IS_SENSITIVE为Y,说明该游标是bind-sensitive,也就是绑定变量的不同可能会影响执行计划,Oracle将监视该游标


次查询:oc_date=20160530

很明显这次应该走TABLE FULL SCAN,但Oracle还是选择了前一次的执行计划,也就是重用了游标0。需要关注的几个字段值也没有发生改变,但BUFFER_GETS发生了比较大的增长。


次查询:oc_date=20160530

这次执行计划终于选择正确了。

Oracle通过监视bind-sensitive游标,发现前面两次的执行不同的绑定值确实会产生比较大的数据访问差异,因此本次执行的时候生成了一个新的子游标,并且标记为bind-aware。


次查询:oc_date=20160531

执行计划和第一次一样,但重新生成了一个子游标2,并将原始游标(游标0)标记为不可共享

之所以这么做,前面在给出bind-aware概念时已经做了说明。


次查询:oc_date=20160530

执行计划正常,重用了子游标1

这里为了说明游标合并,先对视图v$sql_cs_selectivity做个查询。

次查询:oc_date=20160529

又生成了一个新的子游标3,并把游标2置为不可共享

其实游标2和游标3的执行计划是一样的,Oracle为什么要这样做呢?先来查一下视图v$sql_cs_selectivity,看有什么变化。

显然游标3的范围比游标2来得大,这就是前面提到的游标合并,Oracle创建了一个范围更大的游标来代替原有的游标。从整个测试过程来看,Oracle不断地完善着执行计划的选择

顺便提一句,ACS增加了个新视图,v$sql_cs_selectivity、v$sql_cs_histogram、v$sql_cs_statistics,用来存储相关信息供ACS使用。


前面说过,ACS强烈依赖于统计信息,下面来看一下在没有统计信息的情况下Oracle是怎么做的。

次查询:oc_date=20160531

注意,此时IS_SENSITIVE字段为N

次查询:oc_date=20160530

游标0被重用。

次查询:oc_date=20160530

还是重用游标0,并且在第二次和第三次执行的时候绑定变量都没有进行过捕捉。

这里我们耐心地等待一下,等下一次Oracle可以捕捉绑定变量再看一下会不会有什么变化。

次查询:oc_date=20160530

还是重用游标0,尽管重新捕捉了绑定变量。

到此可以得出结论,在没有统计信息的情况下,Oracle不会使用ACS,并且始终重用第一次生成的执行计划(Oracle只做了软解析)。那么如何让Oracle重新生成一个正确的执行计划呢?我认为应该需要逼迫Oracle重新硬解析,换句话说就是要把这个游标清理出内存


上面的测试非常“顺利”,每次都是期望的结果。但有些时候也会莫名产生新的游标,可以查询视图v$sql_shared_cursor,该视图记录了游标不能共享的原因。

恒生技术之眼原创文章,未经授权禁止转载。详情见转载须知

联系我们

恒 生 技 术 之 眼