我是一名长期的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分钟内超时。然后我们让他们把领带关掉并在周五晚上运行,结束但需要16个小时。
我将问题缩小到了没有索引支持的WORK_TABLE_5的连接,并在连接术语上添加了索引。下一次跑步耗时4秒。但成功是间歇性的,当客户大幅改变他们的出口选择时(即大幅改变WORK_TABLE_5中的数据),客户偶尔会有一些缓慢的运行。如果我们在超时导出后更新统计信息并重建索引,则在下次尝试时运行正常。
所以,我想知道如何最好地处理使用静态索引截断/填充静态工作表,一夜之间更新统计信息,以及在统计信息与运行时不同时编译的存储过程。
我有一些关于我想要更好理解的事情的一般性问题:
工作表中数据的性质是否会对查询计划产生重大影响?在编译存储过程时,Oracle是否形成其查询计划?如果我们用表空编译存储过程然后在运行时使用一个500k行的表,我们能得到一个非常不合适的查询计划吗?
我希望如果这是一个临时脚本,那么在选择问题表之前更新问题表的统计数据将消除零星的减速。但是,如果我要更新存储过程中的统计信息,这是使用运行时的不同统计信息编译的呢?
您还想添加其他内容......
感谢您的任何建议。我希望我的MSSQL先入之见并没有让我离开太远。
这种情况在Oracle 11g中发生,但代码部署到使用Oracle 10到12的各类客户,如果可能的话,我希望能够满足所有这些需求。
- 乔尔
答案 0 :(得分:0)
表或索引大小的巨大差异绝对会导致性能问题。解决方案是将统计信息收集添加到过程,而不是依赖于默认统计信息作业。
如果您从版本7开始远离Oracle,那么最重要的新功能是基于成本的优化工具。 Oracle现在根据表,索引,列,表达式,系统统计信息,大纲,指令,动态采样等的优化器统计信息构建查询执行计划。如果您是全职Oracle开发人员,您应该花一天时间阅读关于优化器统计信息。从官方文档中的Managing Optimizer Statistics和DBMS_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...
有如此多的统计功能,很难确切知道在上面的第二步中使用了哪些参数。以下是您可能会发现有用的一些功能的猜测:
NO_INVALIDATE=>FALSE
的最新统计信息。虽然Oracle 10g及更高版本带有默认的统计信息收集作业,但由于以下几个原因,您无法依赖它们: