Performance 为什么PLSQL比SQL*Plus慢
我有几个Oracle查询在通过SQL*PLUS运行时表现良好。但是,当它们作为PL/SQL包的一部分执行时,它们需要更长的时间 我们的DBA通过PLSQL观察到这些查询需要10分钟,通过SQL*Plus需要10秒 有没有人对在哪里查找错误配置有任何指示 客户端-Windows 2000 服务器-Linux(Oracle企业版) 谢谢 -- 决议: 我希望我能接受每个人的答案。他们中有几个人很有帮助Performance 为什么PLSQL比SQL*Plus慢,performance,oracle,plsql,Performance,Oracle,Plsql,我有几个Oracle查询在通过SQL*PLUS运行时表现良好。但是,当它们作为PL/SQL包的一部分执行时,它们需要更长的时间 我们的DBA通过PLSQL观察到这些查询需要10分钟,通过SQL*Plus需要10秒 有没有人对在哪里查找错误配置有任何指示 客户端-Windows 2000 服务器-Linux(Oracle企业版) 谢谢 -- 决议: 我希望我能接受每个人的答案。他们中有几个人很有帮助 查询正在转换数据类型 执行计划不一致。 (提示修复了该问题。) DBA正在查看光标移动的时间 打
- 查询正在转换数据类型
- 执行计划不一致。 (提示修复了该问题。)
- DBA正在查看光标移动的时间 打开而不是查询时间。
select * from mytable where id = '1234';
select * from mytable where id = p_id;
但在PL/SQL中,您正在运行:
select * from mytable where id = '1234';
select * from mytable where id = p_id;
p_id被定义为一个数字。这将在ID列上强制设置一个TO_编号,并阻止Oracle使用索引。最有可能的是,运行时间更长的不是查询,而是在
PL/SQL
中处理它们的开销
在PL/SQL
脚本中处理查询结果时,会发生上下文切换。它需要在Oracle
进程之间传递大量数据,而且速度相当慢
像这样的代码:
DECLARE
cnt INTEGER := 0;
CURSOR cr_main IS
SELECT 1 AS id
FROM dual
CONNECT BY
level <= 1000000;
BEGIN
FOR res IN cr_main
LOOP
cnt := cnt + res.id;
END LOOP;
DBMS_OUTPUT.put_line(cnt);
END;
DECLARE
cnt INTEGER := 0;
CURSOR cr_main IS
SELECT 1 AS id
FROM dual
CONNECT BY
level <= 1000000;
BEGIN
FOR res IN cr_main
LOOP
cnt := cnt + res.id;
END LOOP;
DBMS_OUTPUT.put_line(cnt);
END;
只需0.5
秒即可完成
当您从SQL
调用PL/SQL
时,也会发生上下文切换,如下所示:
SELECT plsql_function(column)
FROM mytable
SELECT plsql_function(column)
FROM mytable
or when a trigger fires.
或者当触发器触发时
我们的DBA已经观察到这些查询通过PLSQL需要10分钟,通过PL/PSQL需要10秒
如果DBA不想为您解决这个问题,我可以理解,但是如果您的DBA确实看到了这两种情况,并且还没有为您提供两种情况的解释计划,那么他确实不是一个很好的DBA
可能没有错误配置,我自己也有过这种情况——所有绑定变量,没有常量,没有提示。直接运行-性能良好。把它放进去开始…结束-砰,慢得要命。事实证明,有时候查询只是使用PL/SQL(即Oracle 9.2)中不同的执行计划
我的解决方案—在PL/SQL版本使用与SQL相同的计划之前一直使用提示
其他可能的问题:
我建议您调整您的语句,使其在PL/SQL中的效果与在SQL中的效果一样好。关注正确绑定变量(使用FORALL和BULK-COLLECT),同时检查执行计划并进行单元测试。您真的在这里比较同类吗?您是在PL/SQL(最佳情况)中执行原始SQL语句,还是使用显式或隐式游标返回值并进行处理?有很大的区别。引用和扩展Quassnoi:
最有可能的是,运行时间更长的不是查询,而是在PL/SQL中处理它们的开销 在PL/SQL脚本中处理查询结果时,会发生上下文切换。它需要在Oracle进程之间传递数据负载,速度非常慢 像这样的代码:
DECLARE
cnt INTEGER := 0;
CURSOR cr_main IS
SELECT 1 AS id
FROM dual
CONNECT BY
level <= 1000000;
BEGIN
FOR res IN cr_main
LOOP
cnt := cnt + res.id;
END LOOP;
DBMS_OUTPUT.put_line(cnt);
END;
DECLARE
cnt INTEGER := 0;
CURSOR cr_main IS
SELECT 1 AS id
FROM dual
CONNECT BY
level <= 1000000;
BEGIN
FOR res IN cr_main
LOOP
cnt := cnt + res.id;
END LOOP;
DBMS_OUTPUT.put_line(cnt);
END;
解决上下文切换问题的一种方法是使用批量收集。如果要收集大量行,那么在PL/SQL语句中使用大容量收集到某种类型的集合中可以大大加快SQL的速度。我们也遇到了类似的问题。更新查询在PL/SQL块中使用时运行速度非常慢,为17分钟;在PL/SQL块外部使用时,执行速度非常快(不到2秒) 我们发现PL/SQL中使用的执行计划是不同的
使用“alter system flush shared_pool”为我们解决了这个问题。这似乎迫使PL/SQL重新考虑要使用的执行计划。可能是隔离级别或自动提交的不同?尝试在每个环境中非常明确地控制它们,看看性能差异是否仍然存在。您需要一个新的DBA,对公司来说是新的,对Oracle来说不是新的。查询正在转换数据类型。执行计划不一致。(提示修正了这一点。)DBA查看的是光标打开的时间,而不是查询时间。嗯,再错不过了。Insert始终是SQL。按原样删除、更新并选择。Insert通过SQL引擎进行处理。如果pl/sql块中存在INSERT,pl/sql引擎将执行到sql引擎的上下文切换。您所需要做的就是阅读批量收集文档。“PL/SQL将DML和查询等SQL语句发送到SQL引擎执行,SQL将结果数据返回给PL/SQL。”感谢Stephanie的反馈,她更新了我的答案。我不知道它们是同一个引擎,但我想说的是同一点(即,在SQL引擎之上有一层PL/SQL处理可能会导致性能下降)