本帖最后由 newkid 于 2014-4-23 04:47 编辑
新的优化技术
在查询优化的过程中,Oracle会使用一系列复杂的技术对SQL语句进行变换。查询优化的这个阶段的目标是为了将原来的SQL语句变换为一个语义上等价、但是处理起来更加高效的SQL语句。Oracle 12c数据库引入了几种新的查询优化方法。
部分连接取值
部分连接取值是这样一种优化技术,它是在连接顺序的生成过程中被使用的。这种技术的目标是为了避免产生重复的行,如果不用这种技术,这些重复只能在计划中随后用一个DISTINCT操作符来去除。通过早些用一个内连接(INNER JOIN)或者半连接(SEMI-JOIN)来取代DISTINCT操作符,这一步骤产生的行数将会减少。这应该会使得计划的总体性能得到改善,因为随后的步骤只需在缩小的行的集合上进行操作。这种优化可以应用在如下类型的查询块:MAX(),MIN(), SUM(distinct), AVG (distinct), COUNT (distinct), 以及UNION, MINUS, INTERSECT 操作符的分支, [NOT] EXISTS 子查询等等。
考虑如下的DISTINCT查询:
SELECT DISTINCT order_id
FROM orders o, customers c
WHERE o.customer_id = c.customer_id
AND order_id < 2400;
(译者注:这个例子举得不好。order_id在orders中应该是唯一的,customer_id在customers表中也应该是唯一的,所以连接之后order_id在结果中也应该是唯一的,DISTINCT完全多余)
在Oracle 11g数据库中,ORDERSh CUSTOMERS之间的连接是一个哈希连接(HASH JOIN),必须在去除重复行的排序发生前被完全取值。
(图29. Oracle 11g数据库要求在ORDERS 和 CUSTOMERS之间完全的连接,然后用unique sort去除重复)
有了部分连接取值,ORDERS 和 CUSTOMERS之间的连接被转换为一个半连接,这意味着一旦在CUSTOMERS表中找到一个匹配的CUSTOMER_ID,查询就会转移到下一个CUSTOMER_ID。通过将哈希连接转换为半连接,流入SORT UNIQUE 的行数大大减少,因为重复的行已经被去除了。变换过的SQL的计划如图30所示。(译者注:变换之后是HASH UNIQUE不是SORT UNIQUE)
(图 30. Oracle 12c数据库的计划显示了ORDERS 和 CUSTOMERS之间完全的半连接,没有产生重复的行)
接受空值的半连接
应用开发者在包含EXISTS子查询的SQL语句中加入IS NULL谓词是不罕见的。加入额外的IS NULL谓词是因为 EXISTS子查询产生的半连接结果会去除具有空值的行,正如内连接(INNER JOIN)所做的一样。
考虑如下的查询:
SELECT p.prod_id,s.quantity_sold,s.cust_id
FROM products p, sales s
WHERE p.prod_list_price > 11
AND p.prod_id = s.prod_id
AND (s.cust_id IS NULL
OR EXISTS (SELECT 1
FROM customers c
WHERE c.cust_id = s.cust_id
AND c.country_id = 'US'
)
);
这里的假定是在s.cust_id 列上可能有空值,而我们想要返回那些行。在Oracle 12c数据库之前,EXISTS子查询无法被展开,因为它出现在一个带有IS NULL谓词的OR谓词(析取谓词)中。因为无法被展开,导致不理想的计划被产生,子查询被作为过滤操作应用在SALES和PRODUCTS表的连接之后。
(图 31. Oracle 11g数据库的计划显示EXISTS 自查询被作为过滤操作应用于连接之后)
在Oracle 12c数据库中,一种新型的半连接被引入,称为接受空值的半连接。这种新连接扩展了半连接的算法,在连接的左边的表的连接列上检查空值。在这个例子中检查会发生在s.cust_id。如果列包含空值,那么相应的SALES表上的行被返回,否则半连接被执行以确定该行是否满足连接条件。接受空值的半连接计划如下图32所示。
(图32. Oracle 12c数据库的计划显示 EXISTS子查询被展开,在customers和sales之间使用了接受空值的半连接)
标量子查询的展开
标量子查询是出现在SQL语句的SELECT子句的子查询。因为标量子查询不能被展开,所以一个相关的标量子查询(它引用了子查询之外的列)必须为外层查询产生的每一行被取值。考虑下面的查询:
SELECT c.cust_id, c.cust_last_name, c.cust_city,
(SELECT avg(s.quantity_sold)
FROM sales s
WHERE s.cust_id = c.cust_id) avg_quan
FROM customers c
WHERE c.cust_credit_limit > 50000;
在Oracle 11g数据库中,对于CUSTOMERS 表中 CUST_CREDIT_LIMIT大于50000的每一行,在sales表上的标量子查询都必须被执行。SALES表是大表,将它扫描很多次是非常耗费资源的。
(图 33.Oracle 11g数据库的计划显示,对于customers表返回的每一行,标量子查询都必须被取值)
将标量子查询展开并且将其转换为一个连接,就免除了为外层查询的每一行都进行取值的必要性。在Oracle 12c数据库中,标量子查询能够被展开,在这个例子中,SALES表上的标量子查询被转换成一个group-by视图。group-by视图确保每组会返回一行,正如标量子查询一样。查询中同样加入了一个外连接,这是为了确保即使当视图的结果为空时,CUSTOMERS的数据仍然会被返回。转换后的查询如下:
SELECT c.cust_id, c.cust_last_name, c.cust_city, v.avg_quan
FROM customers c,
(SELECT avg(s.quantity_sold) avg_quan, s.cust_id
FROM sales s
GROUP BY s.cust_id) v
WHERE c.cust_credit_limit > 50000
AND c.cust_id = v.cust_id(+);
(图34. Oracle 12c数据库的计划显示标量子查询已经被展开成外连接和GROUP BY视图)
多表左外连接
在Oracle 12c数据库之前,如果在外连接的左边有多个表是不合法的,会导致ORA-01417错误。
(图 35. Oracle 11g数据库不支持多表左外连接)
执行这样一个查询的方法是将其翻译为ANSI语法。可是,实现这样的ANSI语法会导致一个横向视图被使用。Oracle无法合并横向视图,所以优化器的计划在连接顺序和连接方法上的选择就受到了限制,这可能导致不理想的计划。
(注:横向视图指的是这样的视图:它引用了不在视图中的表的列)
(图36. ANSI 语法导致带有横向视图的计划,它无法被合并,因此限制了连接顺序)
在Oracle 12c数据库,用Oracle的(+)语法指定的多表左外连接现已被支持。你也能够合并一个外连接的左边的多表视图。能够合并视图就能允许更多的连接顺序和连接方法被考虑,结果是更优的计划会被选中。
(图37. 对新的多表作连接的支持允许视图合并,并且会导致更优的计划)
(译者注:在12c中,即使用ANSI的写法,也可以得到和(+)写法一样的优化计划)
初始化参数
有几个新的初始化参数可以管理Oracle 12c数据库的优化器及其新特性。下面是关于这些新参数的详细介绍。
OPTIMIZER_ADAPTIVE_FEATURES
对于新的自适应查询优化功能的使用,包括自适应连接,SQL计划指令的创建和使用,是受OPTIMIZER_ADAPTIVE_FEATURES参数控制的。这个参数的缺省值和OPTIMIZER_FEATURES_ENABLE(OFE)相关。如果OFE被设置为12.1.0.1或者更高,那么OPTIMIZER_ADAPTIVE_FEATURES被设为TRUE, 所有的自适应查询优化功能将会启用。如果OFE被设为比12.1.0.1更低,那么OPTIMIZER_ADAPTIVE_FEATURES将会被设为FALSE, 所有的自适应查询优化功能都不会启用。
OPTIMIZER_ADAPTIVE_REPORTING_ONLY
乍一看,执行计划的自适应或者中途变动有些吓人。为了更好地理解多少SQL语句会受到新的自适应计划的影响,你可以将自适应计划开启为只报告模式,方法是将OPTIMIZER_ADAPTIVE_REPORTING_ONLY设置为TRUE(缺省值是FALSE)。在这个模式下,启用自适应连接方法所需的信息被收集,但是不会有任何修改计划的行动。这意味着缺省的计划总是会被使用,但计划在非报告模式下会如何调整的信息也会被收集。
OPTIMIZER_DYNAMIC_SAMPLING
虽然OPTIMIZER_DYNAMIC_SAMPLING参数不是新的,但它确实有了新的级别11, 这个级别控制动态统计信息的生成。当设置为级别11时,优化器会自动确定哪些语句会受益于动态统计信息,即使所有的对象已经有了统计信息。
|