
1.2 智能扫描前提条件
看完了上面的一些测试数据,是不是有点热血沸腾的感觉,觉得Exadata的智能扫描特性简直就是一件“神器”?在这里有必要先泼点凉水,Exadata的智能扫描特性可不是无条件工作的,它必须满足一定的条件才会触发。
1.2.1 前提条件
必须满足什么样的条件才有可能触发智能扫描呢?想要让SQL语句触发智能扫描,必须先满足以下3个先决条件。
■ 全扫描。
■ 直接路径读取。
■ 数据存放在Exadata上。
先来说说第二个先决条件,为什么一定要直接路径读取才能进行智能扫描呢?这其实是由数据访问时的内部函数调用顺序决定的。当一个会话正在进行智能扫描操作时,通过oradebug工具获取当前正在执行的函数调用,见代码清单1.9。
代码清单1.9 智能扫描的函数调用

在代码清单1.9中,进程号3109所对应的数据库会话在执行智能扫描时涉及函数调用,其中kcfis_read()函数即智能扫描函数,kcbldrget()函数即直接路径读取函数。从函数的调用顺序可以看出,kcbldrget()函数发生之后才会调用kcfis_read()函数。所以要想触发智能扫描,则必须满足直接路径读取条件。
下面详细讲解这3个触发智能扫描的条件。
1.全扫描
这里所说的“全扫描”不仅是指TABLE ACCESS FULL,还包括INDEX FAST FULL SCAN和BITMAP INDEX FAST FULL SCANS,即智能扫描的先决条件之一是必须“全表扫描”或者“索引快速全扫描”。
在很多介绍Exadata的文档或书籍中,大多提及的是TABLE ACCESS FULL,这基本上是共识,大家都能认可,但很少有人提及INDEX FAST FULL SCAN和BITMAP INDEX FAST FULL SCANS也有可能触发智能扫描。
下面示例展示INDEX FAST FULL SCAN也可进行智能扫描操作。首先将隐含参数_very_large_object_threshold的值设置为1(至于为什么设置_very_large_object_threshold隐含参数,后文会单独进行讲解),然后在会话级别开启autotrace观察SQL语句的执行计划,见代码清单1.10。
代码清单1.10 验证索引快速全扫描也能智能扫描(1)

从以上的代码输出可以看出,该SQL语句使用的是索引快速全扫描(INDEX STORAGE FAST FULL SCAN)方式,扫描的索引名为IDX_TEST_OBJECT_ID,执行完该语句花费的时间为3.94s。
执行该SQL语句的同时,对该SQL语句设置10046事件,然后使用tkprof工具来格式化10046事件生成的日志。
代码清单1.10 验证索引快速全扫描也能智能扫描(2)

从该SQL语句格式化后的10046事件日志可以看出,该SQL语句使用INDEX STORAGE FAST FULL SCAN索引扫描,对应的主要等待事件为cell smart index scan,说明该SQL语句进行了智能扫描操作。
相同的SQL语句在禁用智能扫描的情况下,执行效率会有什么变化?下面继续进行测试工作。在以下代码中,通过在会话级别设置_serial_direct_read隐含参数为never(意味着强制关闭直接路径读取,也即强制SQL语句禁止使用智能扫描),来观察相同的SQL语句此刻的等待事件及执行效率有哪些变化。
代码清单1.10 验证索引快速全扫描也能智能扫描(3)

从以上代码输出可以看出,在会话级别设置隐含参数_serial_direct_read=never,手动强制不允许该相同的SQL语句采用直接路径读取,也即禁用智能扫描,此时该语句花费了17s才运行完成。
在执行该SQL语句的同时,同样对该SQL语句设置10046事件,然后使用tkprof工具来格式化10046事件生成的日志。
代码清单1.10 验证索引快速全扫描也能智能扫描(4)

可见,在禁用智能扫描时,该SQL语句的主要等待事件为cell multiblock physical read,同时伴随着cell single block physical read,但没有cell smart index scan。
相同的SQL语句,仅仅是设置了不同的系统参数,故意让第一次执行时使用智能扫描,而第二次执行时不使用智能扫描,两次的执行效率出现明显的变化,具体如下所示。
代码清单1.10 验证索引快速全扫描也能智能扫描(5)

第一次执行时,进行了智能扫描操作,节省了75%的IO,平均执行时间为3s,而第二次执行时,没有进行智能扫描操作,没有节省任何IO操作,平均执行时间高达20s。
有些资料或书籍中提到,执行计划为index full scan时也能进行智能扫描操作。下面通过具体的示例来验证这一观点是否正确,见代码清单1.11。
代码清单1.11 验证索引全扫描不能智能扫描



从以上示例可以看出,当SQL语句的执行计划进行INDEX FULL SCAN扫描时,即使设置了强制直接路径读取,SQL语句也无法进行智能扫描操作,也即INDEX FULL SCAN不满足触发智能扫描的条件。
关于INDEX FULL SCAN和INDEX FAST FULL SCAN的区别,在本书中不再进行详细讲解,有兴趣的读者可查阅相关资料。
2.直接路径读取
智能扫描除了要求全扫描操作之外,还要求执行读操作时必须采用Oracle的直接路径读取机制。
在Oracle 11g版本之前,并行查询的子进程默认使用的就是直接路径读取机制。Oracle推出并行查询功能的初衷,是想通过它访问数量庞大的数据,而这么多的数据不适合存放在SGA的BufferCache中,因此并行服务器将数据直接读入PGA,从而绕过了SGA。
从Oracle 11g开始,串行执行的SQL语句也同样有可能采用直接路径读取机制了。对Exadata而言,这是个大好消息,最直接的改变就是智能扫描时不一定要求SQL语句开并行。而对传统架构的Oracle数据库而言,这种算法的改变就显得稍稍有点激进了。在很多传统架构的Oracle数据库中,可能会遇到直接路径读取导致IO耗尽,数据库性能急剧下降的情况。例如,从数据库的AWR报告中可以看出性能急剧下降阶段,direct path read等待事件对数据库性能的影响非常严重,如图1.6所示。

图1.6 direct path read等待事件影响数据库性能
因此,在传统架构的Oracle数据库中,基本上都会设置10949事件,来关闭掉Oracle 11g数据库中串行直接路径读取的新特性。具体命令如下。

3.Exadata存储
Exadata智能扫描的前提条件,除了前面提到的“全扫描”和“直接路径读取”之外,还有一个必要的条件,就是数据必须存储在Exadata的存储服务器上,如果数据存放在外挂的非Exadata的存储服务器所创建的磁盘组上,则不会进行智能扫描。
其实,除了数据必须存储在Exadata的存储服务器上之外,还需要将ASM磁盘组的cell.smart_scan_capable属性参数设置成true。由于在Exadata环境中该属性的默认值就是true,所以简单来讲,就是要求数据必须存储在Exadata的存储服务器上,见代码清单1.12。
代码清单1.12 查询ASM磁盘组的cell.smart_scan_capable属性参数值

下面通过示例展示ASM磁盘组的cell.smart_scan_capable属性对智能扫描操作的影响,见代码清单1.13。
代码清单1.13 ASM磁盘组的cell.smart_scan_capable属性对智能扫描操作的影响(1)

只有当ASM磁盘组的cell.smart_scan_capable属性为默认的true时,SQL语句才有可能进行智能扫描。从以上代码输出可以看出,执行这条SQL语句花费了6s。
下面将ASM磁盘组的cell.smart_scan_capable属性设置为false,然后在相同的会话执行相同的SQL语句。这里在SQL语句中稍稍做了点变动,这点变动不会造成执行计划的变化,影响执行的结果集,但会重新进行编译,生成另外一个SQL_ID,目的是展示ASM磁盘组的cell.smart_scan_capable属性变化所带来的影响。
代码清单1.13 ASM磁盘组的cell.smart_scan_capable属性对智能扫描操作的影响(2)

从以上代码输出可以看出,执行相同的SQL语句,当ASM磁盘组的cell.smart_scan_capable属性设置为false后,运行时间从以前的6s增长到现在的33s。
查询这两条SQL语句是否发生过智能扫描。
代码清单1.13 ASM磁盘组的cell.smart_scan_capable属性对智能扫描操作的影响(3)

从以上代码输出可以看出,当ASM磁盘组的cell.smart_scan_capable属性设置为false后,本来应该智能扫描的SQL语句已经无法进行智能扫描操作了。
1.2.2 满足条件但不触发智能扫描
在此要说明的是,前面介绍的智能扫描的三大先决条件,并不是充分条件,在某些情况下,即使满足了以上的三大条件,也有可能不触发智能扫描特性。满足条件但不触发智能扫描的情景具体如下。
■ 数据库参数CELL_OFFLOAD_PROCESSING被设置为false。
■ 正在扫描的表或分区太小。
■ 优化器没有使用直接路径读取。
■ 在cluster表上执行扫描。
■ 在索引组织的表上执行扫描。
■ 对压缩的索引执行快速全扫描。
■ 对反转索引执行快速全扫描。
■ 该表已启用行依赖关系或正在提取rowscn。
■ 优化器扫描希望以ROWID顺序返回行。
■ CREATE INDEX命令使用了nosort选项。
■ 正在选择或查询LOB或LONG列。
■ 对表执行版本查询的flashback操作。
■ 查询非混合列压缩的表对象时,访问的列个数超过了255。
■ 表空间被加密,并且CELL_OFFLOAD_DECRYPTION参数被设置为false。为了使Exadata存储单元执行解密,Oracle数据库需要将解密密钥发送到Exadata存储单元。如果将密钥通过网络发送到Exadata存储单元时存在安全问题,那么会禁用存储节点解密功能。
■ 表空间并未完全存储在Oracle Exadata存储服务器上。
■ 谓词评估是在一个虚拟的列上。
上述不触发智能扫描的十几种情景来自于Exadata官方文档。除了Exadata官方文档中提及的情况之外,MOS文档Exadata Smart Scan FAQ(Doc ID 1927934.1)中另外提及了其他几种不会触发智能扫描特性的情况,具体如下。
■ 被访问的表设置了cache属性。
■ 隐含参数_serial_direct_read被设置成never。
■ 串行的DML语句。
■ SQL语句被设置了隔离。
■ 共享模式的会话发起的串行SQL查询语句。
■ table函数中的SQL查询语句。
■ dbms_sql包中的SQL查询语句。
■ PL/SQL触发器中的SQL查询语句。
■ 存储节点的CPU使用率非常高。
下面通过示例来验证表对象设置了cache属性对智能扫描特性的影响,见代码清单1.14。
代码清单1.14 验证表对象设置了cache属性对智能扫描特性的影响(1)

这段代码的含义是当表test.test未设置cache属性时,计算data_object_id字段的平均值花费了4.81s;然后对表设置cache属性,再次计算data_object_id字段的平均值时,仍然花费了4.33s。
代码清单1.14 验证表对象设置了cache属性对智能扫描特性的影响(2)

获取两条SQL语句的SQL_ID,查询发现两条SQL语句都执行了智能扫描,所以花费的时间相当。
这个测试示例与官方的说法有些出入,在进一步说明之前,先来熟悉一下表对象cache属性的工作原理:当使用全表扫描时,则该表中的数据块会放置在LRU列表的最近最少使用的尾部(LRU端),因此很快就被淘汰出局。如果表设置了cache属性,即使对该表使用全表访问,该表对象的块仍然被放置在LRU列表最近最多使用的尾部(MRU段)。
注意:设置了cache属性的对象,并不是立刻就将该对象所涉及的数据块keep到内存里,而只是尽可能地延长该对象驻留内存的时间。它将数据块存放在BufferCache中的default子池中。如果将一张表保留到存放在BufferCache中的keep子池中,则该表的数据块基本上会永久驻留在内存里,除非数据库重启,或新keep到内存的数据将以前的数据块挤出。
在设置cache属性时,并不会立即将数据块缓存到内存中,所以它并不能立即阻止智能扫描。设置cache属性,仅仅是数据块更趋于缓存到内存中,在内存中缓存的时间更久。只有当内存中缓存的数据块达到一定的数量,才有可能会阻止智能扫描,详情参见1.4节。同样,如果将对象驻留到keep子池也是一样的道理,只有当内存中缓存的数据块达到一定的数量,才会阻止智能扫描。因此,“被访问的表设置了cache属性就会导致无法智能扫描”这种说法并不是十分严谨。