1.1
存储概要
存储概要的作用是,在执行环境或对象统计信息中存在更改时,提供稳定的执行计划。为此,这个功能也称为计划稳定性(plan stability)。在Oracle文档中记录了体现该功能优势的两个重要场景。第一个是从基于规则的优化器(RBO)向基于成本的优化器(CBO)的迁移。第二个场景是将Oracle数据库升级到新版本。
在这两个场景中,目的都是在应用使用旧配置或版本时存储关于执行计划的信息,然后使用该信息来提供与新的配置或版本相同的执行计划。不幸的是,实际上即使正确地使用存储概要(stored outline),你仍能看到执行计划在改变。或许是由于这个原因,我从未见过哪个数据库大范围地使用存储概要。因此,实际上存储概要仅会用在某些具体的SQL语句上。
注意 从11.1版本之后,存储概要不再支持SQL计划管理(SQL plan management)
1.1.1 工作原理
1.1.1.1 什么是存储概要
存储概要是与SQL语句相关联的对象,其作用是在为SQL语句生成执行计划时影响查询优化器。更具体地说,存储概要是一组hint,或者更准确地说,是所有能强制查询优化器始终为给定SQL语句生成特定执行计划的hint组合。
注意 并不是所有hint都可以保存在存储概要中。要想知道不能保存哪些hint, 可以执行以下查询:
select name FROM v$sql_hint WHERE
version_outline IS NULL;
尽管大多数无法保存到存储概要中的hint不会影响执行计划(例如gather_plan_statistics), 但有些还是会的(例如materialize和inline)。因此,有些执行计划因无法在SQL语句中指定hint而不能通过存储概要固定。
存储概要的优势之一是,当它应用于某个SQL语句时,你并不需要为了应用存储概要而修改SQL语句。存储概要保存在数据字典里,并且查询优化器会自动选择它们。图11-2显示了在选择期间执行的基本步骤。
首先,会将SQL语句中的空格移除,进行标准化,并将非文字字符串转换为大写。作为结果的SQL语句签名 (SQL语句文本的散列值)会被计算。接着,根据签名,在数据字典里执行查找。每当找到包含同样签名的存储概要时,就会执行检查来确保这个SQL语句是最优的,并且与绑定存储概要的SQL语句是等价的。这一步很重要,因为签名是散列值,可能会产生冲突。如果测试成功,那么hint组成的存储概要就会包含在生成的执行计划里。
1.1.1.1 创建存储概要
可以使用两种方法来创建存储概要。数据库自动创建和手工创建。如果想为指定会话甚至整个系统执行的每条SQL语句创建存储概要,可以使用第一种方法。然而,就像前面提到的,通常没有必要这么做。因此,经常会手工创建它们。
要激活自动创建,需要将初始化参数create_stored_outlines设置为TRUE或者指定一个类别(category)。使用类别的目的是要集合多个存储概要来实现统一管理。将初始化参数设置为TRUE时会使用默认类别,其名称为DEFAULT。可以在会话级别和系统级别动态更改该初始化参数。要禁用自动创建,需要将初始化参数设置为FALSE。
要手工创建存储概要,必须使用CREATE
OUTLINE语句。下面的SQL语句,摘录自outline_from_text.sql脚本,展示了名为outline_from_text的存储概要的创建,该存储概要与test类别相关联,并基于ON子句中指定的查询:
CREATE OR REPLACE OUTLINE outline_from_text
FOR CATEGORY test
ON select * FROM t WHERE n = 1970;
一旦创建好,就可以通过user_outlines和user_outline_hints视图来显示存储概要的信息和它们的属性(对于这两个视图,也存在以all、dba开头的视图,同时,在12.1多租户环境下还有以cdb开头的视图)。Useroutlines视图显示除了hint以外的信息。下面的查询显示的信息为上一个SQL语句创建的存储概要:
select category, sql_text, signature FROM
user_outlines WHERE name='OUTLINE_FROM_TEXT';
select hint FROM user_outline_hints WHERE
name='OUTLINE_FROM_TEXT';
也可以通过引用库缓存里的游标来手工创建存储概要。下面的例子,摘录自outline_from_sqlarea.sql脚本生成的输出,显示了如何从库缓存里选择游标并且通过dbms_outln包下的create_outline过程创建存储概要:
select hash_value,child_number FROM v$sql
WHERE sql_text ='select * FROM t WHERE n
=1970';
BEGIN
dbms_outln.create_outline(hash_value =>'308120306',
child_number =>
0,
category =>'test');
END;
/
警告create_outline过程不会基于与引用的游标相关联的执行计划创建存储概要。相反,它接受与游标相关联的SQL语句的文本并重解析它。因此,与存储概要相关联的执行计划并不需要和与游标相关联的执行计划一致。例如,一个不同的执行环境可以很容易导致另一个执行计划。
如下所示,create_outline过程仅接受三个参数。这代表存储概要的名称是自动生成的。要找出系统生成的名称,需要查询视图,比如user_outlines。下面的查询返回最后创建的存储概要名:
select name from user_outlines where
timestamp =(select max(timestamp) FROM user_outlines);
系统自动生成的存储概要名称是可以自定义的。