SQL调优指南笔记19:Influencing the Optimizer

Posted dingdingfish

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SQL调优指南笔记19:Influencing the Optimizer相关的知识,希望对你有一定的参考价值。

本文为SQL Tuning Guide第19章“Influencing the Optimizer”的笔记。

重要基本概念

  • driving table
    The table to which other tables are joined. An analogy from programming is a for loop that contains another for loop. The outer for loop is the analog of a driving table, which is also called an outer table.
    其他表连接到的表。 编程的一个类比是一个包含另一个 for 循环的 for 循环。 外层for循环类似于驱动表,也称为外表。

优化器默认值适用于大多数操作,但不是全部。

在某些情况下,您可能有优化器未知的信息,或者需要针对特定类型的语句或工作负载调整优化器。 在这种情况下,影响优化器可能会提供更好的性能。

19.1 Techniques for Influencing the Optimizer

您可以使用多种技术影响优化器,包括 SQL 配置文件、SQL 计划管理、初始化参数和提示。

下图显示了影响优化器的主要技术。

上图中的重叠方块显示 SQL 计划管理同时使用初始化参数和提示。 SQL 配置文件在技术上还包括提示。

您可以使用以下技术来影响优化器:

表 19-1 优化器技术

技术描述
初始化参数参数在数据库实例和会话级别影响许多类型的优化器行为。
提示提示是 SQL 语句中的注释指令。 提示控制范围广泛的行为。
DBMS_STATS此软件包更新和管理优化器统计信息。 统计数据越准确,优化器的估计就越好。 本章不涉及 DBMS_STATS。
SQL 配置文件SQL 配置文件是包含特定于 SQL 语句的辅助统计信息的数据库对象。 从概念上讲,SQL 配置文件之于 SQL 语句就像一组对象级统计信息之于表或索引。 SQL 配置文件可以更正在 SQL 调优期间发现的次优优化器估计。
SQL 计划管理和存储大纲SQL 计划管理是一种预防性机制,它使优化器能够自动管理执行计划,确保数据库仅使用已知或经过验证的计划。 本章不涉及 SQL 计划管理。

19.2 Influencing the Optimizer with Initialization Parameters

本章解释了哪些初始化参数会影响优化,以及如何设置它们。

19.2.1 About Optimizer Initialization Parameters

Oracle 数据库提供初始化参数来影响优化器行为的各个方面,包括游标共享、自适应优化和优化器模式。

下表列出了一些最重要的优化器参数。 请注意,此表不包括近似查询初始化参数

表 19-2 控制优化器行为的初始化参数

初始化参数描述
CURSOR_INVALIDATION为 DDL 语句提供默认的游标失效级别。

IMMEDIATE 为 DDL 设置与 Oracle Database 12c 第 2 版 (12.2) 之前的版本相同的游标失效行为。 这是默认设置。

DEFERRED 允许应用程序利用 DDL 减少的游标失效而无需进行任何应用程序更改。 延迟失效减少了游标失效的数量,并随着时间的推移分散了重新编译的工作量。 出于这个原因,游标在重新编译之前可能会以次优计划运行,并且可能会产生很小的执行时间开销。
CURSOR_SHARING将 SQL 语句中的文字值转换为绑定变量。 转换这些值可以改善游标共享,并且会影响 SQL 语句的执行计划。 优化器根据绑定变量的存在而不是实际的文字值生成执行计划。

设置为 FORCE 以在共享现有游标或游标计划不是最佳时启用新游标的创建。 设置为 EXACT 以仅允许具有相同文本的语句共享相同的游标。
DB_FILE_MULTIBLOCK_READ_COUNT指定在全表扫描或索引快速全扫描期间在单个 I/O 中读取的块数。 优化器使用此参数的值来计算全表扫描和索引快速全扫描的成本。 较大的值会导致全表扫描的成本较低,这可能会导致优化器选择全表扫描而不是索引扫描。

此参数的默认值对应于数据库可以有效执行的最大 I/O 大小。 此值取决于平台,对于大多数平台而言为 1 MB。 因为该参数以块表示,所以它被设置为等于可以有效执行的最大 I/O 大小除以标准块大小的值。 如果会话数非常大,则多块读取计数值会减小,以避免缓冲区缓存被太多的表扫描缓冲区淹没。
OPTIMIZER_ADAPTIVE_PLANS控制自适应计划。 适应性计划有替代选择。 优化器在运行时根据查询执行时收集的统计信息来决定计划。

默认情况下,此参数为 true,表示启用了自适应计划。 将此参数设置为 false 将禁用以下功能:

- 嵌套循环和哈希连接选择
- 星形变换位图裁剪
- 自适应并行分布方法
OPTIMIZER_ADAPTIVE_REPORTING_ONLY控制自动重新优化和自适应计划的报告模式(请参阅“自适应查询计划”)。 默认情况下,报告模式为关闭 (false),这意味着启用了自适应优化。

如果设置为 true,则自适应优化在仅报告模式下运行。 在这种情况下,数据库收集自适应优化所需的信息,但不采取任何行动来更改计划。 例如,自适应计划始终选择默认计划,但如果参数设置为 false,数据库会收集有关数据库将使用哪个计划的信息。 您可以使用 DBMS_XPLAN.DISPLAY_CURSOR 查看报告。
OPTIMIZER_ADAPTIVE_STATISTICS控制自适应统计。 当查询谓词过于复杂而无法单独依赖基表统计信息时,优化器可以使用自适应统计信息。

默认情况下,OPTIMIZER_ADAPTIVE_STATISTICS 为 false,这意味着禁用了以下功能:
- SQL 计划指令
- 统计反馈
- 自适应动态采样
OPTIMIZER_MODE在数据库实例启动时设置优化器模式。 可能的值为 ALL_ROWS、FIRST_ROWS_n 和 FIRST_ROWS。
OPTIMIZER_INDEX_CACHING使用嵌套循环控制索引探针的成本分析。 值 0 到 100 的范围表示缓冲区缓存中索引块的百分比,这会修改优化器对嵌套循环和 IN 列表迭代器的索引缓存的假设。 值 100 表示 100% 的索引块很可能在缓冲区缓存中找到,因此优化器会相应地调整索引探测或嵌套循环的成本。 设置此参数时要小心,因为执行计划可能会更改以支持索引缓存。
OPTIMIZER_INDEX_COST_ADJ调整索引探测的成本。 取值范围为 1 到 10000。默认值为 100,表示优化器将索引评估为基于正常成本模型的访问路径。 值 10 表示索引访问路径的成本是索引访问路径正常成本的十分之一。
OPTIMIZER_INMEMORY_AWARE此参数启用 (TRUE) 或禁用 (FALSE) 所有 Oracle Database In-Memory (Database In-Memory) 优化器功能,包括 IM 列存储、表扩展、Bloom 过滤器等的成本模型。 将该参数设置为 FALSE 会导致优化器在 SQL 语句优化期间忽略表的 INMEMORY 属性。
OPTIMIZER_REAL_TIME_STATISTICS当 OPTIMIZER_REAL_TIME_STATISTICS 初始化参数设置为 true 时,Oracle 数据库会在常规 DML 操作期间自动收集实时统计信息。 默认设置为 false,表示禁用实时统计。
OPTIMIZER_SESSION_TYPE确定数据库是否在自动索引验证期间验证语句。 默认值为 NORMAL,表示语句已验证。 CRITICAL 优先于 NORMAL。

通过在会话中将 OPTIMIZER_SESSION_TYPE 初始化参数设置为 ADHOC,您可以在此会话中暂停自动索引查询。 自动索引过程不会识别索引候选,也不会创建和验证索引。 此控件对于临时查询或测试新功能可能很有用。
OPTIMIZER_CAPTURE_SQL_QUARANTINE启用或禁用 SQL Quarantine 配置的自动创建。要使 SQL Quarantine 在资源管理器终止查询后自动创建配置,请将 OPTIMIZER_CAPTURE_SQL_QUARANTINE 初始化参数设置为 TRUE(默认值为 FALSE)。
OPTIMIZER_USE_INVISIBLE_INDEXES启用或禁用不可见索引的使用。
QUERY_REWRITE_ENABLED启用或禁用优化器的查询重写功能。

TRUE 是默认值,它使优化器能够利用物化视图来提高性能。 FALSE 禁用优化器的查询重写功能,并指示优化器不要使用物化视图重写查询,即使未优化查询的估计查询成本较低。 FORCE 启用优化器的查询重写功能,并指示优化器使用物化视图重写查询,即使未优化查询的估计查询成本较低。
OPTIMIZER_USE_SQL_QUARANTINE确定优化器在为 SQL 语句选择执行计划时是否考虑 SQL Quarantine 配置。 要禁用现有 SQL 隔离配置,请将 OPTIMIZER_USE_SQL_QUARANTINE 设置为 FALSE(默认值为 TRUE)。
QUERY_REWRITE_INTEGRITY确定强制执行查询重写的程度。

默认情况下,完整性级别设置为 ENFORCED。 在这种模式下,必须验证所有约束。 数据库不使用依赖于非强制约束的查询重写转换。 因此,如果您使用 ENABLE NOVALIDATE RELY,某些类型的查询重写可能不起作用。

要在约束处于 NOVALIDATE 模式时启用查询重写,完整性级别必须为 TRUSTED 或 STALE_TOLERATED。 在 TRUSTED 模式下,优化器相信维度和 RELY 约束中声明的关系是正确的。 在 STALE_TOLERATED 模式下,优化器使用有效但包含陈旧数据以及包含新数据的物化视图。 此模式提供最大的重写能力,但会产生产生不准确结果的风险。
RESULT_CACHE_MODE控制数据库是对所有查询使用 SQL 查询结果缓存,还是仅对带有结果缓存提示的查询使用 SQL 查询结果缓存。当设置为 MANUAL(默认)时,您必须使用 RESULT_CACHE 提示来指定将特定结果存储在缓存中。当设置为 FORCE 时,数据库将所有结果存储在缓存中。相应的选项 MANUAL TEMP 和 FORCE TEMP 指定查询结果可以驻留在临时表空间中,除非被提示禁止。

设置此参数时,请考虑结果缓存如何处理 PL/SQL 函数。数据库使用与跟踪 PL/SQL 函数的数据依赖性相同的机制使结果缓存中的查询结果无效,但否则允许缓存包含 PL/SQL 函数的查询。因为 PL/SQL 函数结果缓存失效不跟踪所有类型的依赖项(例如对序列、SYSDATE、SYS_CONTEXT 和包变量),所以在调用此类函数的查询中不加选择地使用查询结果缓存可能会导致结果发生变化,即是,不正确的结果。因此,在选择启用结果缓存时要考虑正确性和性能,尤其是在将 RESULT_CACHE_MODE 设置为 FORCE 时。
RESULT_CACHE_MAX_RESULT指定任何单个结果可以使用的 RESULT_CACHE_MAX_SIZE 的百分比。 默认值为 5,但您可以指定 1 到 100 之间的任何百分比值。
RESULT_CACHE_MAX_TEMP_RESULT指定一个缓存查询可以消耗的临时表空间内存的最大百分比。 默认值为 5。此参数只能在系统级别修改。
RESULT_CACHE_MAX_TEMP_SIZE指定结果缓存可以在 PDB 中消耗的最大临时表空间内存量。 此参数只能在系统级别修改。

默认值为 RESULT_CACHE_MAX_SIZE 的默认值或初始化值的 10 倍。 任何低于 5 的正值都舍入为 5。指定的值不能超过 SYS 模式中当前估计的总空闲临时表空间的 10%。 值 0 禁用该功能。
RESULT_CACHE_REMOTE_EXPIRATION指定依赖于远程数据库对象的结果保持有效的分钟数。 默认值为 0,这意味着数据库不应使用远程对象缓存结果。 将此参数设置为非零值可能会产生陈旧的答案,例如,如果远程数据库修改了结果中引用的表。
STAR_TRANSFORMATION_ENABLED使优化器能够为星型查询花费星型转换(如果为真)。 星形转换结合了各个事实表列上的位图索引。

19.2.2 Enabling Optimizer Features

OPTIMIZER_FEATURES_ENABLE 初始化参数(或提示)控制一组与优化器相关的功能,具体取决于数据库版本。

该参数接受与版本号相对应的有效字符串值列表之一,例如 11.2.0.2 或 12.2.0.1。 您可以使用此参数在数据库升级后保留优化器的旧行为。 例如,如果您将 Oracle Database 12c 第 1 版 (12.1.0.2) 升级到 Oracle Database 12c 第 2 版 (12.2.0.1),则 OPTIMIZER_FEATURES_ENABLE 参数的默认值将从 12.1.0.2 更改为 12.2.0.1。

为了向后兼容,您可能不希望执行计划因为新版本中的新优化器特性而改变。 在这种情况下,您可以将 OPTIMIZER_FEATURES_ENABLE 设置为较早的版本。 如果升级到新版本,并且想要启用新版本中的功能,则无需显式设置 OPTIMIZER_FEATURES_ENABLE 初始化参数。

警告:Oracle 不建议将 OPTIMIZER_FEATURES_ENABLE 初始化参数显式设置为早期版本。 为避免执行计划更改可能导致 SQL 性能下降,请考虑改用 SQL 计划管理。

本教程假定以下内容:

  • 您最近将数据库从 Oracle Database 12c 第 1 版 (12 1.0.2) 升级到 Oracle Database 12c 第 2 版 (12.2.0.1)。
  • 您希望保留早期版本中的优化器行为。
SQL> SHOW PARAMETER optimizer_features_enable
 
NAME                                 TYPE        VALUE
------------------------------------ ----------- --------
optimizer_features_enable            string      12.2.0.1

SQL> ALTER SYSTEM SET OPTIMIZER_FEATURES_ENABLE='12.1.0.2';

19.2.3 Choosing an Optimizer Goal

优化器的目标是优化器对资源使用的优先级。

使用 OPTIMIZER_MODE 初始化参数,您可以设置以下优化器目标

  • 最佳吞吐量(默认)

当您将 OPTIMIZER_MODE 值设置为 ALL_ROWS 时,数据库使用最少的资源来处理语句访问的所有行。

对于 Oracle Reports 等批处理应用程序,优化以获得最佳吞吐量。 通常,吞吐量在批处理应用程序中更为重要,因为用户只关心应用程序完成所需的时间。 响应时间不太重要,因为用户不会在应用程序运行时检查单个语句的结果。

  • 最佳响应时间

当您将 OPTIMIZER_MODE 值设置为 FIRST_ROWS_n 时,数据库会以最佳响应时间为目标进行优化,以返回前 n 行,其中 n 等于 1、10、100 或 1000。

对于 Oracle Forms 或 SQL*Plus 中的交互式应用程序,优化响应时间。 通常,响应时间很重要,因为交互式用户正在等待查看语句访问的第一行或多行。

本教程假定以下内容:

  • 主应用程序是交互式的,因此您希望为数据库实例设置优化器目标以最小化响应时间。
  • 仅对于当前会话,您希望运行报告并优化吞吐量。
SHOW PARAMETER OPTIMIZER_MODE

NAME                                 TYPE        VALUE
------------------------------------ ----------- --------
optimizer_mode                       string      ALL_ROWS

在实例级别,针对响应时间进行优化。

SHOW PARAMETER OPTIMIZER_MODE

NAME                                 TYPE        VALUE
------------------------------------ ----------- --------
optimizer_mode                       string      ALL_ROWS

ALTER SYSTEM SET OPTIMIZER_MODE='FIRST_ROWS_10';

ALTER SESSION SET OPTIMIZER_MODE='ALL_ROWS';

19.2.4 Controlling Adaptive Optimization

在 Oracle 数据库中,自适应查询优化是优化器根据运行时收集的统计信息调整执行计划的过程。

设置以下初始化参数时启用自适应计划:

  • OPTIMIZER_ADAPTIVE_PLANS 为 TRUE(默认)
  • OPTIMIZER_FEATURES_ENABLE 为 12.1.0.1 或更高版本
  • OPTIMIZER_ADAPTIVE_REPORTING_ONLY 为 FALSE(默认)

如果 OPTIMIZER_ADAPTIVE_REPORTING_ONLY 设置为 true,则自适应优化在仅报告模式下运行。 在这种情况下,数据库会收集自适应优化所需的信息,但不会更改计划。 自适应计划始终选择默认计划,但数据库会收集有关执行的信息,就像参数设置为 false 一样。

设置以下初始化参数时启用自适应统计:

  • OPTIMIZER_ADAPTIVE_STATISTICS 为 TRUE(默认为 FALSE)
  • OPTIMIZER_FEATURES_ENABLE 为 12.1.0.1 或更高版本

本教程假定以下内容:

  • OPTIMIZER_FEATURES_ENABLE 初始化参数设置为 12.1.0.1 或更高版本。
  • OPTIMIZER_ADAPTIVE_REPORTING_ONLY 初始化参数设置为 false(默认)。
  • 您希望出于测试目的禁用自适应计划,以便数据库仅生成报告。
SHOW PARAMETER OPTIMIZER_ADAPTIVE_REPORTING_ONLY

NAME                                 TYPE        VALUE
------------------------------------ ----------- -----
optimizer_adaptive_reporting_only    boolean     FALSE

ALTER SESSION SET OPTIMIZER_ADAPTIVE_REPORTING_ONLY=true;

-- 使用 +REPORT 参数运行 DBMS_XPLAN.DISPLAY_CURSOR。

19.3 Influencing the Optimizer with Hints

优化器提示是 SQL 语句中将指令传递给优化器的特殊注释。详见文档

优化器使用提示来选择语句的执行计划,除非被某些条件阻止。

19.3.1 About Optimizer Hints

提示嵌入在 SQL 注释中。

提示注释必须紧跟 SQL 语句块的第一个关键字。 您可以使用任何一种注释样式:斜杠星号 (/*) 或破折号 (–)。 加号 (+) 提示定界符必须紧跟在注释定界符之后,加号之前不允许有空格,如以下片段所示:

SELECT /*+ hint_text */ ...

加号后面的空格是可选的。 一个语句块只能有一个包含提示的注释,但它可以包含许多以空格分隔的提示。 多个提示至少用一个空格分隔,如以下语句所示:

SELECT /*+ FULL (hr_emp) CACHE(hr_emp) */ last_name FROM employees hr_emp;

19.3.1.1 Purpose of Hints

提示使您能够做出通常由优化器做出的决策。

您可以使用提示来影响优化器模式、查询转换、访问路径、连接顺序和连接方法。 在测试环境中,提示对于测试特定访问路径的性能很有用。 例如,您可能知道索引对某些查询更具选择性,从而产生更好的计划。 下图显示了如何使用提示告诉优化器为特定语句使用特定索引。

提示的缺点是管理、检查和控制的额外代码。 在 Oracle7 中引入了提示,当优化器生成次优计划时用户几乎没有追索权(recourse)。 因为数据库和主机环境中的更改可能会使提示过时或产生负面影响,所以一个好的做法是使用提示进行测试,但使用其他技术来管理执行计划。

Oracle 提供了多种工具,包括 SQL Tuning Advisor、SQL 计划管理和 SQL 性能分析器,以解决优化器无法解决的性能问题。 Oracle 强烈建议您使用这些工具而不是提示,因为它们会随着数据和数据库环境的变化提供新的解决方案。

19.3.1.2 Types of Hints

您可以对表、查询块和语句使用提示。

  • 单表
    单表提示在一个表或视图上指定。 INDEX 和 USE_NL 是单表提示的示例。
SELECT /*+ INDEX (employees emp_department_ix)*/ employee_id, department_id
FROM   employees 
WHERE  department_id > 50;
  • 多表
    多表提示类似于单表提示,只是提示可以指定多个表或视图。 LEADING 是多表提示的一个示例。
SELECT /*+ LEADING(e j) */ *
FROM   employees e, departments d, job_history j
WHERE  e.department_id = d.department_id
AND    e.hire_date = j.start_date;
  • 查询块
    查询块提示对单个查询块进行操作。 STAR_TRANSFORMATION 和 UNNEST 是查询块提示的示例。 以下语句使用查询块提示指定 FULL 提示仅适用于引用员工的查询块:
SELECT /*+ INDEX(t1) FULL(@sel$2 t1) */ COUNT(*)
FROM   jobs t1
WHERE t1.job_id IN (SELECT job_id FROM employees t1);
  • 语句
    语句提示适用于整个 SQL 语句。 ALL_ROWS 是语句提示的一个示例。
SELECT /*+ ALL_ROWS */ * FROM sales;

19.3.1.3 Scope of Hints

当您在语句块中指定提示时,提示将应用于语句块中相应的查询块、表或整个语句。 提示会覆盖任何实例级或会话级参数。

语句块是以下之一:

  • 一个简单的 MERGE、SELECT、INSERT、UPDATE 或 DELETE 语句
  • 复杂语句的父语句或子查询
  • 使用集合运算符(UNION、MINUS、INTERSECT)的查询的一部分

以下查询由两个组件查询和 UNION 运算符组成:

SELECT /*+ FIRST_ROWS(10) */ prod_id, time_id FROM 2014_sales
UNION ALL
SELECT /*+ ALL_ROWS */ prod_id, time_id FROM current_year_sales;

前面的语句有两个块,一个用于每个组件查询。 第一个组件查询中的提示仅适用于其优化,而不适用于第二个组件查询的优化。 例如,在 2015 年的第一周,您查询当年和去年的销售额。 您将 FIRST_ROWS(10) 应用于去年 (2014) 销售额的查询,并将 ALL_ROWS 提示应用于今年 (2015) 销售额的查询。

19.3.2 Guidelines for Join Order Hints

在某些情况下,您可以在 SQL 语句中指定连接顺序提示,以便它不会访问对结果没有影响的行。

连接中的驱动表是其他表连接到的表。 通常,驱动表包含消除表中最高百分比行的过滤条件。 连接顺序会对 SQL 语句的性能产生重大影响。

考虑以下准则:

  • 当索引更有效地检索请求的行时,避免进行全表扫描。
  • 当您可以使用获取少量行的不同索引时,请避免使用从驱动表获取多行的索引。
  • 选择连接顺序,以便稍后在连接顺序中将更少的行连接到表中。

以下示例显示了如何有效地调整连接顺序:

SELECT *
FROM   taba a, 
       tabb b, 
       tabc c
WHERE  a.acol BETWEEN   100 AND   200
AND    b.bcol BETWEEN 10000 AND 20000
AND    c.ccol BETWEEN 10000 AND 20000
AND    a.key1 = b.key1
AND    a.key2 = c.key2;
  1. 选择驱动表和驱动索引(如果有)。
    前面示例中的前三个条件中的每一个都是适用于单个表的筛选条件。 最后两个条件是连接条件。
    过滤条件支配着驱动表和索引的选择。 通常,驱动表包含消除最高百分比行的过滤条件。 相比较而言,因为acol列100到200的范围较窄,而10000和20000的范围比较大,所以taba是驱动表。

  2. 选择最好的联结顺序,尽早找到最好的未使用的过滤器。
    您可以通过首先使用最佳仍未使用的过滤器连接到表来减少以下连接的工作。 因此,如果 bcol BETWEEN 比 ccol BETWEEN 更具限制性(拒绝更高百分比的行),那么如果 tabb 在 tabc 之前连接,则最后一个连接会变得更容易(行更少)。

  3. 您可以使用 ORDERED 或 STAR 提示来强制加入顺序。

19.3.3 Reporting on Hints

解释计划包括一份报告,显示在计划生成期间使用了哪些提示。

19.3.3.1 Purpose of Hint Usage Reports

在 Oracle Database 19c 之前的版本中,可能很难确定优化器为什么不使用提示。 提示使用报告解决了这个问题。

优化器使用提示中编码的指令来选择语句的执行计划,除非条件阻止优化器使用提示。 数据库不会针对它忽略的提示发出错误消息。 提示报告显示使用和忽略了哪些提示,并且通常解释了提示被忽略的原因。 忽略提示的最常见原因如下:

  • 语法错误
    提示可以包含拼写错误或无效参数。 如果多个提示出现在同一个提示块中,并且如果一个提示有语法错误,则优化器会尊重提示错误之前的所有提示,并忽略之后出现的提示。 例如,在提示规范 /*+ INDEX(t1) FULL(t2) MERG(v) USE_NL(t2) */ 中,MERG(v) 有语法错误。 优化器支持 INDEX(t1) 和 FULL(t2),但忽略 MERG(v) 和 USE_NL(t2)。 提示使用报告将 MERG(v) 列为有错误,但未列出 USE_NL(t2),因为它没有被解析。
  • 未解决的提示
    未解决的提示由于语法错误以外的原因而无效。 例如,一条语句指定 INDEX(employees emp_idx),其中 emp_idx 不是表employees 的有效索引名称。
  • 相互矛盾的提示
    即使正确指定了这些提示,数据库也会忽略冲突提示的组合。 例如,一条语句指定了 FULL(employees) INDEX(employees),但索引扫描和全表扫描是互斥的。 在大多数情况下,优化器会忽略这两个相互冲突的提示。
  • 受变换影响的提示
    转换可以使某些提示无效。 例如,一个语句指定 PUSH_PRED(some_view) MERGE(some_view)。 当 some_view 合并到其包含的查询块中时,优化器无法应用 PUSH_PRED 提示,因为 some_view 不可用。

19.3.3.2 User Interface for Hint Usage Reports

该报告包括所有优化器提示的状态。 还包括其他提示的子集,包括 PARALLEL 和 INMEMORY。

报告访问
默认情况下启用提示跟踪。 您可以使用以下 DBMS_XPLAN 函数访问提示使用报告:

  • DISPLAY
  • DISPLAY_CURSOR
  • DISPLAY_WORKLOAD_REPOSITORY
  • DISPLAY_SQL_PLAN_BASELINE
  • DISPLAY_SQLSET

当您在格式参数中指定值 HINT_REPORT 时,上述函数会生成报告。 值 TYPICAL 仅显示最终计划中未使用的提示,而值 ALL 显示已使用和未使用的提示。

报告格式

SELECT /*+ INDEX(t1) FULL(@sel$2 t1) */ COUNT(*)
FROM   jobs t1
WHERE t1.job_id IN (SELECT /*+ FULL(t1) */ job_id FROM employees t1);

SET PAGES 9999
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(format => 'ALL'));

输出如下:

PLAN_TABLE_OUTPUT                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        
-----------------------------------------------------------------------------
SQL_ID  4u8vcd1hjmjk0, child number 0
-------------------------------------
SELECT /*+ INDEX(t1) FULL(@sel$2 t1) */ COUNT(*) FROM   jobs t1 WHERE 
t1.job_id IN (SELECT /*+ FULL(t1) */ job_id FROM employees t1)
 
Plan hash value: 3101158531
 
----------------------------------------------------------------------------------
| Id  | Operation            | Name      | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------------
|   0 | SELECT STATEMENT     |           |       |       |     4 (100)|          |
|   1 |  SORT AGGREGATE      |           |     1 |    17 |            |          |
|   2 |   NESTED LOOPS       |           |    19 |   323 |     4  (25)| 00:00:01 |
|   3 |    SORT UNIQUE       |           |   107 |   963 |     3   (0)| 00:00:01 |
|   4 |     TABLE ACCESS FULL| EMPLOYEES |   107 |   963 |     3   (0)| 00:00:01 |
|*  5 |    INDEX UNIQUE SCAN | JOB_ID_PK |     1 |     8 |     0   (0)|          |
----------------------------------------------------------------------------------
 
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
 
   1 - SEL$5DA710D3
   4 - SEL$5DA710D3 / "T1"@"SEL$2"
   5 - SEL$5DA710D3 / "T1"@"SEL$1"
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   5 - access("T1"."JOB_ID"="JOB_ID")
 
Column Projection Information (identified by operation id):
-----------------------------------------------------------
 
   1 - (#keys=0) COUNT(*)[22]
   3 - (#keys=1) "JOB_ID"[VARCHAR2,10]
   4 - (rowset=256) "JOB_ID"[VARCHAR2,10]
 
Hint Report (identified by operation id / Query Block Name / Object Alias):
Total hints for statement: 3 (U - Unused (1))
---------------------------------------------------------------------------
 
   4 -  SEL$5DA710D3 / "T1"@"SEL$2"
         U -  FULL(t1) / hint overridden by another in parent query block
           -  FULL(@sel$2 t1)
 
   5 -  SEL$5DA710D3 / "T1"@"SEL$1"
           -  INDEX(t1)
 

48 rows selected. 

报告标题显示报告中的提示总数。 在这种情况下,该语句总共包含 3 个提示。 如果提示未使用、未解决或有语法错误,则标题指定它们的编号。 在这种情况下,只有 1 个提示未使用。

该报告在计划中出现的对象(例如,查询块和表)下显示提示。 每个对象之前都有一个数字,用于标识计划中对象首次出现的行。 例如,前面的报告显示了适用于以下不同对象的提示:T1@SEL$2 和 T1@SEL$1。 表 T1@SEL$2 出现在计划第 4 行的查询块 SEL$5DA710D3 中。 表 T1@SEL$1 出现在计划第 5 行的同一查询块中。

提示可能指定不正确或与最终计划中不存在的对象相关联。 如果查询块未出现在最终计划中,则报告为其分配行号 0。在前面的示例中,没有提示具有行号 0,因此所有查询块都出现在最终计划中。

报告显示提示的文本。 该提示还可能具有以下注释之一:

  • E 表示语法错误。
  • N 表示未解决的提示。
  • U 表示最终计划中没有使用相应的提示。

在前面的示例中,U - FULL(t1) 表示查询块 SEL$5DA710D3 出现在最终计划中,但未应用 FULL(t1) 提示。

在每个对象中,未使用的提示出现在开头,然后是使用过的提示。 例如,报告首先显示未使用的 FULL(t1) 提示,然后显示已使用的 FULL(@sel$2 t1)。 对于许多未使用的提示,该报告解释了优化器未应用提示的原因。 在前面的示例中,报告指出未使用 FULL(t1) 的原因如下:提示被父查询块中的另一个覆盖。

19.3.3.3 Reporting on Hint Usage: Tutorial

您可以使用 DBMS_XPLAN 显示函数来报告提示使用情况。

默认情况下启用提示使用报告。 显示带有提示信息的计划的步骤与正常显示计划的步骤相同。

本教程假定以下内容:

  • employees.employee_id 列上存在一个名为 emp_emp_id_pk 的索引。(HR Sample Schema中已包含)
  • 您想查询特定员工。
  • 您想使用 INDEX 提示来强制优化器使用 emp_emp_id_pk。
EXPLAIN PLAN FOR
  SELECT /*+ INDEX(e emp_emp_id_pk) */ COUNT(*) 
  FROM   employees e 
  WHERE  e.employee_id = 5;
  
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY(format => 'ALL'));


Explained.


PLAN_TABLE_OUTPUT                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Plan hash value: 2637910222
 
------------------------------------------------------------------------------------
| Id  | Operation          | Name          | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |               |     1 |     4 |     0   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE    |               |     1 |     4 |            |          |
|*  2 |   INDEX UNIQUE SCAN| EMP_EMP_ID_PK |     1 |     4 |     0   (0)| 00:00:01 |
------------------------------------------------------------------------------------
 
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
 
   1 - SEL$1
   2 - SEL$1 / "E"@"SEL$1"
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   2 - access("E"."EMPLOYEE_ID"=5)
 
Column Projection Information (identified by operation id):
-----------------------------------------------------------
 
   1 - (#keys=0) COUNT(*)[22]
 
Hint Report (identified by operation id / Query Block Name / Object Alias):
Total hints for statement: 1
---------------------------------------------------------------------------
 
   2 -  SEL$1 / "E"@"SEL$1"
           -  INDEX(e emp_emp_id_pk)

32 rows selected. 

提示报告部分显示 INDEX(e emp_emp_id_pk) 提示的查询块为 SEL$1。 表标识符是 E@SEL$1。 计划行的行号为 2,对应于表 E@SEL$1 在计划表中出现的第一行。

19.3.3.4 Hint Usage Reports: Examples

这些示例显示了各种类型的提示使用报告。

以下示例都显示了对 hr 模式中的表的查询。

示例 19-2 语句级未使用提示
计划表的以下查询指定了 TYPICAL 的格式值,它只显示未使用的提示

EXPLAIN PLAN FOR
  SELECT /*+ INDEX_RS(e emp_manager_ix) */ COUNT(*)
  FROM   employees e
  WHERE  e.job_id < 5;
  
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY(format => 'TYPICAL'));


Explained.


PLAN_TABLE_OUTPUT                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     

以上是关于SQL调优指南笔记19:Influencing the Optimizer的主要内容,如果未能解决你的问题,请参考以下文章

SQL调优指南笔记9:Joins

SQL调优指南笔记1:Introduction to SQL Tuning

SQL调优指南笔记6:Explaining and Displaying Execution Plans

SQL调优指南笔记11:Histograms

SQL调优指南笔记11:Histograms

SQL调优指南笔记10:Optimizer Statistics Concepts