【发布时间】: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 中的数据)时,他们偶尔会遇到一些缓慢的运行。如果我们在导出超时后更新统计信息并重建索引,它会在下一次尝试时运行良好。
所以,我想知道如何最好地处理使用静态索引截断/填充静态工作表、一夜之间更新的统计信息以及在统计信息与运行时完全不同时编译的存储过程。
关于我想更好地理解的事情,我有几个一般性问题:
工作表中数据的性质是否会显着影响查询计划?编译存储过程时,Oracle 是否形成了它的查询计划?如果我们在表为空的情况下编译存储过程,然后在运行时使用一个有 50 万行的表,我们会得到一个非常不合适的查询计划吗?
我希望如果这是一个临时脚本,那么在从中选择之前更新问题表的统计信息将消除零星的减速。但是,如果我要更新存储过程中的统计信息,该存储过程是使用与运行时不同的统计信息编译的呢?
您还想添加什么...
感谢您的任何建议。我希望我对 MSSQL 的先入之见没有让我离题太远。
这发生在 Oracle 11g 中,但代码部署给使用 Oracle 10 到 12 的各种客户,如果可能的话,我想迎合所有这些客户。
-- 乔尔
【问题讨论】:
-
比较 99 分钟跑和 4 秒跑的解释计划有什么可疑之处吗?
-
第一次,在我建立索引之前,是的——它进行了嵌套表扫描,花了 16 个小时。添加索引修复了我的测试。后来使用索引运行,在客户系统上产生了更多零星错误,我无法重现它们或查看查询计划。但大概他们的查询计划中一定有一些狡猾的东西,否则他们就不会超时。我在这里真正想了解的是:SP 编译时间和 SP 运行时间之间的表/索引统计信息的巨大差异会导致执行不良吗?如果是这样,我该怎么办?