【问题标题】:How can I improve this oracle 11g plan selection without changing query?如何在不更改查询的情况下改进此 oracle 11g 计划选择?
【发布时间】:2020-08-29 19:21:58
【问题描述】:

我有一个用于一组 oracle 11g 数据库表的数据重新填充方案,它需要:

  • 删除并重新创建所有表。滴就像DROP TABLE TCA_SECURITY_DAY_BAR CASCADE CONSTRAINTS PURGE
  • 加载静态数据 - 即查找表,然后加载一些与日期相关的数据
  • 在加载过程的不同时间,我散布了收集统计信息命令,我发现这些命令大大加快了加载过程本身的各种查询。但是,我对自己的选择没有信心,因为它们是通过猜测选择的。

    • 目标表聚集调用。示例:

    dbms_stats.gather_table_stats(                            
        ownname => 'TCA',                                     
        tabname => 'TCA_SECURITY_DAY_BAR',               
        estimate_percent => DBMS_STATS.AUTO_SAMPLE_SIZE,      
        block_sample => TRUE,                                 
        method_opt => 'FOR ALL INDEXED COLUMNS SIZE 1',       
        cascade => TRUE                                       
    );                                                        
    
    • 架构调用:示例:

    dbms_stats.gather_schema_stats(
        ownname => 'TCA',
        estimate_percent => NULL,
        block_sample => TRUE,
        method_opt => 'FOR ALL INDEXED COLUMNS SIZE 1',
        cascade => TRUE,
        granularity => 'ALL',
        options => 'GATHER'                                                                                                                     
    )                                                                                                                                      
    

在重新加载过程之后,我运行了一些报告,并且我有一个问题查询类,当它可以/应该是亚秒级时需要 7 秒。我已经对查询进行了改进,例如使用参数并在 where 子句中更具体。带着这个问题,我不是试图改进查询,而是试图改进引擎选择的计划,或者至少了解它的神秘行为。

查询是从列表达式列表(未分组、聚合)生成的,我将chunk 列表分成若干列(例如 3、4、5),以查看报告中的最佳视觉效果。对于一组给定的连接子句,选择的列表达式的数量和其他一些因素(一天中的时间、缓存、温度,谁知道)会影响优化器是选择快速计划还是慢速计划。

以下是包含 1 个分组列 multiplier 和 5 个非分组聚合表达式的示例查询:

SELECT
    TO_.multiplier
    -- multiplier.bracket
    ,   min(TCA.TCA_SECURITY.symbol)
    -- symbol.min
    ,   min(TO_.px_limit)
    -- px_limit.min
    ,   min(TCA.TCA_SECURITY_DAY_BAR.px_open_day)
    -- px_open_day.min
    ,   min(TCA.TCA_SECURITY_DAY_BAR.px_high_day)
    -- px_high_day.min
    ,   min(TCA.TCA_SECURITY_DAY_BAR.px_low_day)
    -- px_low_day.min
FROM TCA_ORDER TO_
    LEFT JOIN TCA_ORDER_SECURITY ON (TO_.ID = TCA_ORDER_SECURITY.ORDER_ID)
    LEFT JOIN TCA.TCA_SECURITY TCA_SECURITY ON (TCA_ORDER_SECURITY.SECURITY_ID = TCA_SECURITY.ID)
    LEFT JOIN TCA.TCA_SECURITY_DAY_BAR ON (
        TCA_SECURITY.ID = TCA_SECURITY_DAY_BAR.SECURITY_ID
        AND TO_.TRADE_DATE = TCA_SECURITY_DAY_BAR.TRADE_DATE
    )

WHERE TRADING_UNIT_ID IN (621)
GROUP BY TO_.multiplier

观察:

  • 在将查询与不同数量的列进行比较时,我没有在两者之间运行统计信息,并且我相信它们没有运行(因为设置是每晚执行一次)。
  • 一旦出现慢查询模式,它就会在一段时间内保持一致。
  • 最初,我发现 5 列的查询总是很慢(> 7 秒)
  • 如果我只有 4 个,那就快了。
  • 第二天 5 突然快了。但是选择 4 列很慢。
  • 当我将记录的运行缓慢的查询复制/粘贴到 SQL Developer 时,它们很快
  • 截至今天早上 - 3 快,4 慢,5 快

我知道架构设置为每晚运行统计信息,但我真的不知道用于此的确切命令。如果那些夜间统计数据收集电话不是特别的,我可以自己按需调用它们,这可能会有所帮助 - 我只需要知道如何准确地模仿它。

为了尝试排除故障,在我的 kotlin/jdbc 代码中,我使用了解释上次计划运行的调用。这是一个 diff,显示了一个 5 列快速和 4 列慢的查询。很明显,由于某种原因,它在慢速情况下选择了“NESTED LOOPS OUTER”,我不知道为什么。

因此,我正在寻找对可能发生的情况的解释以及在不更改查询的情况下影响优化器选择的建议。

【问题讨论】:

  • 请在每列前面加上它来自的表(或表别名)。
  • @TheImpaler:更新查询以显示列源。

标签: sql oracle optimization jdbc sql-execution-plan


【解决方案1】:
  1. 如果您要在
    之后触发相同的查询 删除/重新创建/填充并且您的数据不会发生很大变化,您可以 你的 SQL 计划基线。基本建好计划,发力预言机 在执行相同的查询时使用它。更多您可以在这里找到: https://docs.oracle.com/database/121/TGSQL/tgsql_spm.htm#TGSQL94621
  2. 如果选项 1) 不适合您,您也可以启用动态 采样。这基本上会在运行时收集一小部分的统计信息 如果尚未收集统计信息,则为行的子集。 :https://oracle-base.com/articles/12c/dynamic-statistics-12cr1

此外,为了快速收集表格的统计信息,您可以将“estimate_percent”设置为较小的数字(5-10%),这将快速收集统计信息,但可能会与自动收集统计信息发生冲突。

【讨论】:

  • 感谢您提供有用的提示。我不认为 1 会起作用,因为我只关注一小组列表达式,而且我有大量的一般应用程序。如果可以的话,我正在尝试更好地使用优化器。我尝试了 2 号(我在 11g 上),但没有任何区别。
  • 如果查询完全相同,并且如果统计信息没有更改但执行计划不同,那么数据库会话中的一些 CBO 参数差异可能是解释:docs.oracle.com/cd/E28271_01/server.1111/e16638/… -(无绑定变量:所以没有绑定变量偷看)。您应该尝试使用/*+ gather_plan_statistics */select * from table(dbms_xplan.display_cursor(null,null,'ALLSTATS')); 获得真正的执行计划统计信息
  • @pifor 我正在这样做。但是在添加这个的过程中,我发现慢查询现在很快Dohh!我不知道是什么触发了改进。我正在研究结果集缓存 - 但看起来“RESULT CACHE”会列为计划中的操作。我已经选择了会话参数,除了(optimizer_mode 'all_rows' vs 'first_rows_10' 但不同之处在于我在阳光下尝试了所有东西)之外,所有参数都是相同的。就好像一些时间和魔法发生了,事情很快。我希望在我的重做脚本中拥有这种魔力。
【解决方案2】:

如果你有一个未知的统计问题,第一步是回到默认值:

dbms_stats.gather_table_stats(
    ownname => 'TCA',
    tabname => 'TCA_SECURITY_DAY_BAR'
);

由于 11g 引入了新的独特值数 (NDV) 优化,默认值可能会更快、更准确。但是更改 ESTIMATE_PERCENTBLOCK_SAMPLE 可能会禁用该优化。

如果这几天统计数据仍然不好,请查看DBA_TAB_COLUMNS.HISTOGRAM,看看它们是否正在发生变化。默认统计作业仅收集已在相关查询中使用的列的直方图。如果您删除该表,Oracle 将忘记该表上的所有先前查询,并且在重新运行查询之前不会收集相关的直方图。 (为了解决这个问题,您可以在重新创建表后运行一些虚拟的SELECT 查询,而不是搞乱METHOD_OPT。)


编辑:如何查找更改

您可以使用如下查询来查看查询的执行计划是否随时间发生变化:

select begin_interval_time, plan_hash_value, dba_hist_sqlstat.*
from dba_hist_sqlstat
join dba_hist_snapshot
    on dba_hist_sqlstat.snap_id = dba_hist_snapshot.snap_id
where sql_id = '<your SQL_ID>'
order by begin_interval_time desc;

计划更改的最常见原因是统计数据更改。您可以检查 DBA_TABLES.LAST_ANALYZED 和 DBA_OPTSTAT_OPERATIONS 中的意外统计数据变化。

缓存是性能变化的另一个可能原因。 Oracle 最重要的缓存存储块,即来自表或索引的 8 KB 数据块。另一个查询可能会从表中读取并缓存块,这可能会意外地帮助您的查询运行得更快。

自适应重新优化可以动态地改变计划的工作方式,但 11g 中没有那么多选项。不过,您应该查看执行计划的“注释”部分,看看是否有任何奇怪的计划管理正在进行。 (您发布了很多很好的执行计划细节,但底部缺少“注意”部分。)

NESTED LOOP 最适合连接一小部分行,而 HASH JOIN 是查询大部分行的最佳选择。最好是我们可以为 Oracle 提供良好的统计信息,并让它自动决定哪种连接类型是最好的。但是,如果您不想再研究这个问题,最终可以作弊并使用/*+ USE_HASH(TABLE1 TABLE2) */ 之类的提示。我们不能总是找到每个性能问题的根本原因。

最后,要更详细地了解发生了什么变化,请查看 DBA_HIST_ACTIVE_SESS_HISTORY。它包含每 10 秒的样本,该样本显示什么查询正在运行哪个操作,以及查询正在等待什么事件。这需要一些工作,但您可以使用该视图来比较多次查询运行之间的几乎所有详细信息,并检查系统活动。

这可能是很多令人沮丧的工作。 Oracle 的计划在 99.999% 的情况下都是正确的,并且他们提供了处理异常值的工具,但这并不总是那么简单。

【讨论】:

  • 感谢您的评论。有道理-我按照您的建议设置了gather_table_stats。我还将gather_schema_stats 设置为仅传递所有者。 reload 后,4 col 查询仍然很慢。不过,我真正想了解的是,为什么在一天中的一段时间后,它们会突然加速。是查询运行的次数吗?服务器上是否有寻找最佳计划的后台工作? oracle 可以从中学习的类似计划(即连接子句)是否运行得非常快? NESTED LOOP 听起来很慢,为什么有时会选择它?
猜你喜欢
  • 2015-07-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-16
  • 2011-10-13
  • 1970-01-01
  • 2019-04-06
  • 1970-01-01
相关资源
最近更新 更多