【问题标题】:SP using table/index with volatile statistics that differ at compile and run timeSP 使用具有在编译和运行时不同的可变统计信息的表/索引
【发布时间】:2017-03-25 19:48:51
【问题描述】:

我是一名长期的 MSSQL 开发人员,自 Oracle 7 以来第一次发现自己重新回到 PL/SQL。我正在寻找一些关于大型导出存储过程的调优建议,该过程偶尔会运行缓慢且不可重复地运行缓慢在某些点。这发生在一些静态工作表周围,它会截断、填充并用作导出的一部分。大纲中的代码通常如下所示:

create or replace Procedure BigMultiPurposeExport as (

-- about 2000 lines of other code

INSERT WORK_TABLE_5 SELECT WHATEVER1 FROM WHEREVER1;
INSERT WORK_TABLE_5 SELECT WHATEVER2 FROM WHEREVER2;
INSERT WORK_TABLE_5 SELECT WHATEVER3 FROM WHEREVER3;
INSERT WORK_TABLE_5 SELECT WHATEVER4 FROM WHEREVER4;
-- WORK_TABLE_5 now has 0 to ~500k rows whose content can vary drastically from run to run
-- e.g. one hourly run exports 3 whale sightings, next exports all tourist visits to Kenya this decade

-- about 1000 lines of other code

INSERT OUTPUT_TABLE_3
SELECT THIS, THAT, THE_OTHER
FROM BUSINESS_TABLE_1 BT1
INNER JOIN BUSINESS_TABLE_2 ON etc -- typical join on indexed columns
INNER JOIN BUSINESS_TABLE_3 ON etc -- typical join on indexed columns
INNER JOIN BUSINESS_TABLE_4 ON etc -- typical join on indexed columns
LEFT OUTER JOIN WORK_TABLE_1 ON etc -- typical join on indexed columns
LEFT OUTER JOIN WORK_TABLE_2 ON etc -- typical join on indexed columns
LEFT OUTER JOIN WORK_TABLE_3 ON etc -- typical join on indexed columns
LEFT OUTER JOIN WORK_TABLE_4 ON etc -- typical join on indexed columns
LEFT OUTER JOIN WORK_TABLE_5 WT5 ON BT1.ID = WT5.BT1_ID AND WT5.RECORD_TYPE = 21 
-- join above is now supported by indexes on BUSINESS_TABLE_1 (ID) and WORK_TABLE_5 (BT1_ID, RECORD_TYPE), originally wasn't
LEFT OUTER JOIN WORK_TABLE_6 ON etc -- typical join on indexed columns
LEFT OUTER JOIN WORK_TABLE_7 ON etc -- typical join on indexed columns

-- about 4000 lines of other code
)

对 OUTPUT_TABLE_3 的最终插入通常在 10 秒内运行,但有时在某些客户服务器上会在我们默认的 99 分钟内超时。然后我们让他们取消 tiemout 并在周五晚上运行它,它完成但需要 16 个小时。

我将问题缩小到 WORK_TABLE_5 的连接,它不支持索引,并在连接项上放置了一个索引。下一次运行耗时 4 秒。但是成功是断断续续的,当客户彻底改变他们的导出选择(即彻底改变 WORK_TABLE_5 中的数据)时,他们偶尔会遇到一些缓慢的运行。如果我们在导出超时后更新统计信息并重建索引,它会在下一次尝试时运行良好。

所以,我想知道如何最好地处理使用静态索引截断/填充静态工作表、一夜之间更新的统计信息以及在统计信息与运行时完全不同时编译的存储过程。

关于我想更好地理解的事情,我有几个一般性问题:

  1. 工作表中数据的性质是否会显着影响查询计划?编译存储过程时,Oracle 是否形成了它的查询计划?如果我们在表为空的情况下编译存储过程,然后在运行时使用一个有 50 万行的表,我们会得到一个非常不合适的查询计划吗?

  2. 我希望如果这是一个临时脚本,那么在从中选择之前更新问题表的统计信息将消除零星的减速。但是,如果我要更新存储过程中的统计信息,该存储过程是使用与运行时不同的统计信息编译的呢?

  3. 您还想添加什么...

感谢您的任何建议。我希望我对 MSSQL 的先入之见没有让我离题太远。

这发生在 Oracle 11g 中,但代码部署给使用 Oracle 10 到 12 的各种客户,如果可能的话,我想迎合所有这些客户。

-- 乔尔

【问题讨论】:

  • 比较 99 分钟跑和 4 秒跑的解释计划有什么可疑之处吗?
  • 第一次,在我建立索引之前,是的——它进行了嵌套表扫描,花了 16 个小时。添加索引修复了我的测试。后来使用索引运行,在客户系统上产生了更多零星错误,我无法重现它们或查看查询计划。但大概他们的查询计划中一定有一些狡猾的东西,否则他们就不会超时。我在这里真正想了解的是:SP 编译时间和 SP 运行时间之间的表/索引统计信息的巨大差异会导致执行不良吗?如果是这样,我该怎么办?

标签: oracle oracle11g


【解决方案1】:

表或索引大小的巨大差异肯定会导致性能问题。解决方案是将统计数据收集添加到过程中,而不是依赖默认的统计作业。

如果您从第 7 版开始就不再使用 Oracle,那么最重要的新特性就是基于成本的优化器。 Oracle 现在根据表、索引、列、表达式、系统统计信息、大纲、指令、动态采样等的优化器统计信息构建查询执行计划。如果您是一名全职 Oracle 开发人员,您可能应该花一天时间阅读有关优化器的信息统计数据。官方文档中以Managing Optimizer StatisticsDBMS_STATS开头。

最终存储过程应如下所示:

--1: Insert into working tables.
insert into work_table...

--2: Gather statistics on working tables.
dbms_stats.gather_table_stats('SCHEMA_NAME', 'WORK_TABLE', ...);

--3: Use working tables.
insert into other_table select * from work_table...

统计功能如此之多,很难确切知道在上面的第二步中要使用哪些参数。以下是一些您可能会觉得有用的功能的猜测:

  1. DEGREE - 人们避免在流程中收集统计信息的一个原因是时间太长了。您可以通过设置程度来显着提高运行时间。虽然这也使用了更多的资源。
  2. NO_INVALIDATE - 很难知道查询的统计信息何时“设置”。收集统计信息通常会很快使基于旧统计信息的执行计划失效。但不总是。如果您想 100% 确定下一个查询使用的是您要设置的最新统计信息NO_INVALIDATE=>FALSE
  3. ESTIMATE_PERCENT 在 11g 及更高版本中,您肯定希望使用默认值,它使用更快的算法。在 10g 及以下版本中,您可能需要将该值设置为较低的值以使收集速度足够快。

虽然 Oracle 10g 及更高版本带有默认的统计信息收集作业,但您不能依赖它们,原因如下:

  1. 它们是按计划进行的,可能不会在正确的时间运行。如果某个流程显着更改了数据,则需要立即获取新的统计信息,而不是在晚上 10 点。如果有很多表需要分析,那么这项工作可能不会在一天之内全部完成。
  2. 许多 DBA 禁用了这些作业。这很荒谬,而且几乎总是一个错误。但是您会发现许多 DBA 放弃了这项工作,因为他们认为他们可以做得更好。许多 DBA 不喜欢使用自动任务和设置首选项,而是喜欢将整个事情扔掉,并用随着时间的推移而腐烂的自定义过程取而代之。

【讨论】:

  • 谢谢乔恩,这是极好的信息(和智慧)。我会陷入参考文献中。
猜你喜欢
  • 2014-02-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-18
  • 2012-01-19
相关资源
最近更新 更多