提要:
查询特定的要求:
智能扫描只可用于完整的表或索引扫描。
智能扫描只能用于直接路径读取:
直接路径读取会自动用于并行查询。
直接路径读取可以用于串行查询。
默认情况下不使用它们进行小型表的串行扫描。
使用 _serial_direct_read=TRUE 可强制执行直接路径读取。
为了查询可以获得Exadata卸载能力的优势,优化器必须决定使用全表扫描或者快速全索引扫描来执行语句,这里的用词比较笼统,一般来说,这两个词对应的是执行计划中的TABLE ACCESS FULL和INDEX FAST FULL SCAN。在Exadata中,这些类似的操作的命名做了些许更改以表明访问的是Exadata存储。新的操作名称是TABLE ACCESS STORAGE FULL和INDEX STORAGE FAST FULL SCAN。请注意,也有一些细微的变化,比如 MAT_VIEW ACCESS STORAGE FULL事件也表示可以使用智能扫描。不过你应该知道,事实上就算执行计划里面显示 TABLE ACCESS STORAGE FULL操作,也并不意味着查询就一定执行了智能扫描,它仅仅意味着前提已经满足。我们将在本章稍后探讨如何确认一个语句是否确实通过智能扫描实现了卸载操作。
直接路径读是什么意思?下面两段话可以解释
智能扫描除了必须是全扫描之外,还需要读取操作是通过Oracle直接路径读取机制来执行的。直接路径读取已经存在很长一段时间了,传统上,这种读取机制是被服务于并行查询的从属进程(Slave Process)使用的。因为并行查询最初预计将用于访问非常大量的数据(通常大到无法全部放入Oracle缓冲区),所以决定并行从属进程直接读取数据,然后放入自己的内存中(也被称为程序全局区或者PGA)。直接路径读取机制完全跳过了标准的将数据库放入缓冲区这样的Oracle缓存机制,这对海量数据是非常有效的,因为它消除了那些额外的没有帮助的工作(缓存全表扫描获取到的却又可能不会被再次用到的数据),让这些块不至于将其他的数据块刷新出缓冲区。像我们之前提到的,kcfis(Kernel File Intelligent Storage)函数是被kcbldrget( Kernel Block Direct Read GET)函数调用的,所以,智能扫描只有在使用直接路径读取机制的时候才会执行。
除了并行从属进程,只要条件允许,直接路径读取也可能用在非并行SQL语句中。有一个隐含参数_SERIAL_DIRECT_READ可以控制此功能。当此参数设置为默认值AUTO时,Oracle自动判断是否要为非并行扫描使用直接路径读取。计算基于几个因素,包括对象大小、Buffer Cache大小,以及在Buffer Cache中已存在了多少该对象的数据块。另外,还有一个隐含参数(_SMALL_TABLE_THRESHOLD)定义了如果要使用串行直接路径读取,那么表至少要为多大。对于非串行扫描决定是否要使用直接路径读取机制的算法并未公开,虽然串行直接路径读取的功能早已存在,但是只是在最近才成为比较普遍的现象。Oracle数据库11gR2在计算是否对于非并行扫描使用直接路径读取上做了一些修改,新修改的算法使Oracle 11gR2比以前的数据库版本要更频繁地采用直接路径读取。这也许是因为有了Exadata智能扫描,因此希望尽可能地触发直接路径读取,但是这样的算法在非Exadata平台上可能略显激进。
备注 My Oracle Support文档:793845.1中包含如下表述。
在11g中,关于在串行表扫描中是使用直接路径读取还是使用缓存读取,我们做了探索性的改动。在10g中,对于大表的串行扫描默认是通过缓存的,在11g中,决定是直接读取还是通过缓存读取,要基于表大小、缓冲区大小和其他多种统计信息。因为避免了闩锁(Latche),因此直接路径读取比离散读(Scattered Read)更快,对其他进程影响更小。
完全索引扫描和快速完全索引扫描的区别:
完全索引扫描: 这个性能感觉是最差的,只能单块读取,而且还要通过表去获得数据,但是因为他是按索引的顺序读取的,所以不再需要排序. 比如 select * from table order by name, 如果name 上有索引,很显然这个时候一般有2个计划, 第一个,全表扫描,然后sort, 还有一个情况,完全索引扫描就OK. 因为此时不需要排序了.
索引全扫描是根据叶节点链来进行的。进行索引全扫描首先要从根开始,找到叶节点链上的第一个数据块,然后沿着叶节点链进行扫描,由于叶节点链是根据索引键值排序的,因此这样扫描出来的数据本身就是排序的,数据读出后不需要再次排序。这种扫描方式和索引快速全扫描相比,首先要找到索引的根,然后通过枝节点找到第一个叶节点,然后再顺着叶节点链扫描整个索引。索引全扫描的IO成本比索引快速全扫描要大很多,读取根节点和叶节点的成本相对不大,不过由于顺着叶节点链扫描整个索引的时候无法使用多块读,而只能使用单块读,因此这种扫描方式的IO开销要远大于索引快速全扫描。这种索引扫描,我们如果对会话进行跟踪,会发现大量的db file sequential read等待。
快速完全索引扫描:
属先这个扫描是不用通过表得到数据的.这个是重点. 那么不通过表得到数据,很明显只能通过索引得到数据了. 那就更明显了,那你要拿的字段必须在索引中,否者如何不通过表的到数据?通常比如:select name from table; 如果name上有索引,有可能走FFS.但是如果是select name,id from table,假如id没有索引,那么不管怎么样,都是不可能走FFS的.对索引的扫描可以根据该索引的extent来进行,采用多块读的方式进行。因此在这类操作中,我们可以看到会话会大量的出现db file scattered read等待。
后者是我们希望在EXADATA上出现的索引扫描方式