
1.5 引导SQL语句使用智能扫描
在针对数据库中的单独SQL语句进行优化时,可能常会遇到一些特定的SQL语句未使用智能扫描操作。如果经过测试可以确认该SQL语句智能扫描比传统的索引扫描更加高效,那么可能需要使用一些特殊的手段,让这类SQL语句进行智能扫描操作。
常见的手段有如下几种。
1.使用parallel提示
当SQL语句并行运行时,基本上都会使用智能扫描,因为并行查询时,并行的子进程默认会选择直接路径读取的方式来访问数据块。同样,使用并行的SQL语句,一般情况下也会选择使用全表扫描方式。
下面通过示例进行说明,见代码清单1.23。
代码清单1.23 parallel提示触发智能扫描



从以上示例可以看出,当SQL语句中涉及的表test.middle在内存中已经缓存了66%的数据块时,该SQL语句无法使用串行的直接路径读取方式来进行数据扫描,而相同的SQL语句,仅仅是添加了parallel提示之后,就可以进行直接路径读取,最终也能够进行智能扫描。
2.使用full提示
当SQL语句中带有查询条件并且表中存在索引时,Oracle优化器很可能会优先使用索引进行数据查询。此时可以使用/∗+full(a)∗/提示强制该SQL语句进行全表扫描,因为只有全表扫描才有机会触发智能扫描。
3.将索引置于invisible
当SQL语句中带有查询条件并且表中存在索引时,Oracle优化器很可能会优先使用索引进行数据查询。此时如果确定全表扫描后智能扫描效率高于索引扫描,可以选择删除索引。在删除索引之前,通常先将索引置于invisible状态。
4.忽略提示
很多应用程序开发人员喜欢在SQL语句中包含一些提示,以此来控制SQL语句的执行计划,但往往是这种人为的提示,可能导致智能扫描无法执行。如果无法修改SQL语句中的代码,可以在会话级别设置隐含参数_optimizer_ignore_hints来忽略SQL语句中的提示,见代码清单1.24。
代码清单1.24 隐含参数_optimizer_ignore_hints忽略提示



5.减小db_cache_size大小
减小db_cache_size的大小,其实也就是间接地调整_small_table_threshold隐含参数,使SQL语句更容易进行串行的直接路径读取。
6.设置“特大表”与“小表”参数
直接调整_small_table_threshold和_very_large_object_threshold隐含参数,使SQL语句更容易进行串行的直接路径读取。
7.调整统计信息中的数据块个数
使用DBMS_STATS.SET_TABLE_STATS函数或DBMS_STATS.SET_INDEX_STATS函数手动修改表或索引的统计信息中的数据块个数。
该方式必须配合参数_direct_read_decision_statistics_driven一起使用,具体原因可参考前面章节。
8.设置_serial_direct_read隐含参数
该隐含参数用来控制是否允许串行的直接路径读取,参数值可设置为auto、always和never,具体含义如表1.4所示。
表1.4 _serial_direct_read隐含参数值说明

查询数据库当前_serial_direct_read隐含参数设置,代码如下。

如果需要强制会话进行直接路径读取,可执行如下语句。

如果需要强制会话不进行直接路径读取,可执行如下语句。

注意:_serial_direct_read隐含参数不建议在数据库系统级别设置,因为并不是所有的业务程序都适合直接路径读取或智能扫描操作,在数据库系统级别设置该参数,可能会导致一些业务程序的性能异常糟糕。
下面通过示例来验证在会话级别设置_serial_direct_read隐含参数为always时,SQL语句的性能表现,见代码清单1.25。
代码清单1.25 _serial_direct_read隐含参数对直接路径读取的影响(1)

SQL语句访问的表对象介于“小表”与“特大表”之间,当内存中缓存的数据块个数大于数据块总数的50%时,在会话级别设置_serial_direct_read隐含参数为always,同时收集该SQL语句的10046事件,以便观察_serial_direct_read隐含参数对直接路径读取的影响。
代码清单1.25 _serial_direct_read隐含参数对直接路径读取的影响(2)

对生成的10046事件使用tkprof工具进行格式化后,从输出可以看出,该SQL语句的主要等待事件为cell smart table scan,这也间接说明了该SQL语句进行了直接路径读取,而正常情况下,如果不设置该参数,根据串行直接路径读取算法,该SQL语句是不可能使用直接路径读取的方式来访问数据块的。
下面继续观察如果清空内存中的数据块,然后设置_serial_direct_read隐含参数会有什么表现。
代码清单1.25 _serial_direct_read隐含参数对直接路径读取的影响(3)

使用alter system flush buffer_cache语句清空缓存中已经缓存的数据块后,在会话级别设置_serial_direct_read隐含参数为always,同时收集该SQL语句的10046事件,来观察_serial_direct_read隐含参数对直接路径读取的影响。
代码清单1.25 _serial_direct_read隐含参数对直接路径读取的影响(4)

对生成的10046事件使用tkprof工具进行格式化后,从输出可以看出,该SQL语句同样进行了cell smart table scan操作,也即进行了直接路径读取。出现这个结果是在情理之中,因为根据串行直接路径读取算法,当表对象缓存在内存中的数据块比例比较低时,该SQL语句本来就应该使用直接路径读取的方式来访问数据块。
同样,当表对象为“特大表”或“小表”时,在会话级别设置_serial_direct_read隐含参数为always,则无论内存中缓存的数据块有多少,该SQL语句都会使用直接路径读取的方式来访问数据块。