如何优化流水线、弱类型引用游标的使用
Posted
技术标签:
【中文标题】如何优化流水线、弱类型引用游标的使用【英文标题】:How to optimize usage of pipelined, weakly typed ref cursor 【发布时间】:2013-04-03 22:45:54 【问题描述】:我的程序有问题;当运行“大”集合(800 多个父母,1300 多个孩子)时,它非常很慢(30 - 60 秒)。
基本思想是获取符合特定搜索条件的所有父记录(及其各自的子记录),以及必须计算的 3 条额外信息。
我解决问题的方法是
-
为计算值创建带有附加字段的自定义记录类型。
然后可以将对该记录类型的引用传递给每个函数,由主处理函数控制。
在为每个父记录计算值时,将其附加到记录上。
每个过程GET_PARENT_RECORDS
和GET_CHILD_RECORDS
每次搜索调用一次,每个计算函数运行 N 次(其中 N 是父记录数和/或子记录数)。
问题 1:这是正确的方法吗? (弱类型游标,流水线函数)如果不是,那么我应该如何解决这个问题,假设我可以重做?
问题 2:除非完全重写,否则提供的代码有什么明显可以改进的地方吗?
问题 3:还是有其他问题,因为我注意到,当我运行了几次程序后,同样的慢查询会在 20 秒内返回?
包定义
create or replace
PACKAGE THIS_PKG AS
Type parentCursor IS REF CURSOR;
Type childCursor IS REF CURSOR;
Type ParentRecordType IS RECORD (
other_columns,
Extra_column_A,
Extra_column_B,
Extra_column_C,
Row_num);
--associative array
TYPE ParentArray IS TABLE OF ParentRecordType;
FUNCTION processParents(
p IN THIS_PKG. parentCursor
) RETURN ParentArray
PIPELINED
;
FUNCTION countSomething(some params…)
RETURN INT;
FUNCTION checkCondX (SomeParent IN ParentRecordType)
RETURN VARCHAR2;
FUNCTION checkCondY (SomeParent IN ParentRecordType)
RETURN VARCHAR2;
PROCEDURE GET_PARENT_RECORDS( other_parameters, Parents OUT THIS_PKG.parentCursor);
PROCEDURE GET_CHILD_RECORDS( other_parameters, Children OUT THIS_PKG.childCursor);
END THIS_PKG;
包体
-- omitted
FUNCTION processParents(
p IN THIS_PKG.parentCursor
) RETURN ParentArray
PIPELINED
IS
out_rec ParentArray;
someParent ParentRecordType;
BEGIN
LOOP
FETCH p BULK COLLECT INTO out_rec LIMIT 100;
FOR i IN 1 .. out_rec.COUNT
LOOP
out_rec(i).extra_column_A := countSomething (out_rec(i).field1, out_rec(i).field2);
out_rec(i).extra_column_B := checkCondX(out_rec(i));
out_rec(i).extra_column_C := checkCondY(out_rec(i));
pipe row(out_rec(i));
END LOOP;
EXIT WHEN p%NOTFOUND;
END LOOP;
RETURN;
END processParents;
PROCEDURE GET_PARENT_RECORDS(
some_columns,
Parents OUT THIS_PKG. parentCursor) IS
BEGIN
OPEN Parents FOR
SELECT *
FROM TABLE(processParents (CURSOR(
SELECT *
FROM (
--some select statement with quite a few where clause
--to simulate dynamic search (from pre-canned search options)
)
))) abc
WHERE abc.extra_column_C like '%xyz%' --(xyz is a user given value)
;
END GET_PARENT_RECORDS;
更新 昨天做了一些探索,发现了 Quest Batch SQL Optimizer(来自 Toad)。我插入了包装,这就是我得到的。
批处理优化器结果
复杂查询
有问题的查询
【问题讨论】:
您是否进行了任何分析以指出问题所在? @BobJarvis 由于我不是 DBA,因此我怀疑我是否可以进行全面的分析,并且可能很难利用他的时间。但我确实对查询进行了一些分析,并附上了两个解释计划。你说的是这个吗? 在涉及 SQL(或 PL/SQL)时,永远不要“禁止完全重写”。流水线是一个很棒的功能,但通常可以通过改进的声明性方法来消除它。 【参考方案1】:行处理部分发生了什么?在这些 countSomething、checkCondX/Y 函数上可能会花费大量时间。他们是否也在进行 SQL 调用?我会先检查没有附加谓词的表函数的性能。最好简单地创建一个在 SQL 中而不是在函数中完成这一切的查询 - 如果你能做到这一点,这将比为每一行调用一个函数要快得多。
out_rec(i).extra_column_A := countSomething (out_rec(i).field1, out_rec(i).field2);
out_rec(i).extra_column_B := checkCondX(out_rec(i));
out_rec(i).extra_column_C := checkCondY(out_rec(i));
您提供的解释计划也很有趣,因为优化器认为所有表仅返回 1 行(基数 1)。如果不是这种情况,那么查询计划将不是最优的。可能需要收集统计信息,使用动态采样或在表函数上使用cardinality hints。
最后,查看DBMS_SQLTUNE.REPORT_SQL_MONITOR,它提供了有关您的 sql 的详细报告。除非查询被动态识别为需要监控,否则您需要添加 /*+ MONITOR */ 提示。这提供了更多详细信息,例如返回的行数、执行计数和解释计划中没有的其他有趣的花絮。
SELECT /*+ MONITOR */
FROM slow_query;
-- then run sqltune to get a report
SELECT *
FROM TABLE(DBMS_SQLTUNE.REPORT_SQL_MONITOR());
【讨论】:
【参考方案2】:Quest Batch SQL Optimizer(来自 Toad)或任何其他工具将无法帮助您,因为它们不了解您在函数中所做的事情。问题出在“FETCH p BULK COLLECT INTO out_rec LIMIT 100;”中。传递给 p 的查询质量实际上定义了最终的执行计划和运行时间。流水线不是缓慢的原因。当您多次运行您的过程时,Oracle 使用缓存数据。我最好的建议是:为此特定目的使用 Java 而不是 PL/SQL,它会更容易理解。
【讨论】:
以上是关于如何优化流水线、弱类型引用游标的使用的主要内容,如果未能解决你的问题,请参考以下文章