为啥 PLSQL 比 SQL*Plus 慢
Posted
技术标签:
【中文标题】为啥 PLSQL 比 SQL*Plus 慢【英文标题】:Why is PLSQL slower than SQL*Plus为什么 PLSQL 比 SQL*Plus 慢 【发布时间】:2009-11-05 17:47:38 【问题描述】:我有几个通过 SQL*PLUS 运行时执行良好的 Oracle 查询。但是,当它们作为 PL/SQL 包的一部分执行时,它们需要更长的时间。
我们的 DBA 发现这些查询通过 PLSQL 需要 10 分钟,通过 SQL*Plus 需要 10 秒。
有没有人指出在哪里寻找错误配置?
客户端 - Windows 2000 服务器 - Linux (Oracle Enterprise)
谢谢
--
分辨率:
我希望我能接受每个人的答案。其中有几个很有帮助。
查询正在转换数据类型。 执行计划不匹配。 (提示解决了这个问题。) DBA 正在查看光标所在的时间 打开而不是查询时间。【问题讨论】:
可能是隔离级别或自动提交的差异?尝试在每个环境中非常明确地控制这些,看看性能差异是否仍然存在。 你需要一个新的 DBA,对公司来说是新人,而不是 Oracle 的新人 【参考方案1】:使用 SQL 跟踪查看每种情况下的执行计划。想到的一种可能性(根据经验):包是否将错误类型的值绑定到查询?可能是您在 SQL Plus 中运行:
select * from mytable where id = '1234';
但是在 PL/SQL 中你正在运行:
select * from mytable where id = p_id;
其中 p_id 被定义为一个数字。这将在 ID 列上强制使用 TO_NUMBER 并阻止 Oracle 使用索引。
【讨论】:
查询正在转换数据类型。执行计划不匹配。 (提示解决了这个问题。)DBA 正在查看光标打开的时间而不是查询时间。【参考方案2】:很可能不是查询运行时间更长,而是在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;
在我的机器上运行超过3
秒,而这个:
SELECT SUM(1)
FROM dual
CONNECT BY
level <= 1000000
仅在 0.5
秒内完成。
当您从SQL
调用PL/SQL
时,也会发生上下文切换,如下所示:
SELECT plsql_function(column)
FROM mytable
或者当触发器触发时。
【讨论】:
【参考方案3】:我们的 DBA 发现这些查询通过 PLSQL 需要 10 分钟,通过 PL/PSQL 需要 10 秒。
如果 DBA 不想为您解决这个问题,我可以理解,但如果您的 DBA 确实已经看到了这两种情况,并且还没有为您提供两种情况的解释计划,那么他确实不是一个很好的 DBA。
可能没有错误配置,我自己也遇到过 - 所有绑定变量,没有常量,没有提示。直接运行——性能好。把它放在 BEGIN..END 里面 - bam,慢得要命。事实证明,有时查询只是使用 PL/SQL(即 Oracle 9.2)中的不同执行计划。
我的解决方案 - 在 PL/SQL 版本使用与 SQL 相同的计划之前使用提示。
其他可能的问题:
-
SQL*Plus 只返回前 100 个或
所以行,然后等你要求更多,但PL/SQL必须处理
他们都没有问。微不足道的问题,但有时会被忽视。
您为 SQL*Plus 使用常量,为 PL/SQL 绑定变量。有时使用常量可以让优化器检查倾斜数据,并且可以使用其他索引。
【讨论】:
【参考方案4】:您真的在这里比较同类吗?您是在 PL/SQL 中执行原始 SQL 语句(最佳情况)还是使用显式或隐式游标返回值然后处理它们?有很大的不同。
【讨论】:
【参考方案5】:我们遇到了类似的问题。更新查询在 PL/SQL 块中使用时运行速度非常慢 17 分钟,而在 PL/SQL 之外使用时执行速度非常快(不到 2 秒)。
我们发现 PL/SQL 中使用的执行计划不同。
使用“alter system flush shared_pool”为我们解决了这个问题。这似乎迫使 PL/SQL 重新考虑要使用的执行计划。
【讨论】:
【参考方案6】:引用和扩展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;
在我的机器上运行超过 3 秒,而这个:
SELECT SUM(1)
FROM dual
CONNECT BY
level <= 1000000
只需 0.5 秒即可完成。
当您从 SQL 调用 PL/SQL 时也会发生上下文切换,如下所示:
SELECT plsql_function(column)
FROM mytable
or when a trigger fires.
解决上下文切换问题的一种方法是使用 BULK COLLECT。如果要收集大量行,使用 BULK COLLECT INTO 某种类型的集合可以显着加快 PL/SQL 语句中的 SQL。
【讨论】:
【参考方案7】:通过 SQLPlus 发出的 DML(例如 SELECT、UPDATE、DELETE)直接发送到 Oracle 的 SQL 引擎,而 PLSQL 过程中的 DML 首先由 PL/SQL 处理(例如进行变量绑定),然后发送到 SQL 引擎。
在大多数情况下,PL/SQL 中的相同语句将执行与 SQL 相同的操作,并且两种方式通常会产生相同的执行计划。根据我的经验(通常在需要绑定变量时),它可能会导致非常不同的性能。我曾经见过在 SQL Plus 中发出的 SELECT 只需要几分之一秒,而通过 PL/SQL 发出的 SELECT 需要 1-2 分钟。
我建议您调整您的语句,使其在 PL/SQL 中的工作效果与在 SQL 中一样好。专注于正确绑定变量(使用 FORALL 和 BULK COLLECT),但也要检查执行计划并进行单元测试。
【讨论】:
嗯,大错特错了。插入始终是 SQL。就像删除、更新和选择一样。通过 SQL 引擎处理插入。如果 pl/sql 块中有 INSERT,则 PL/SQL 引擎执行到 SQL 引擎的上下文切换。您需要做的就是阅读 BULK COLLECT 文档。 download.oracle.com/docs/cd/B19306_01/appdev.102/b14261/… "PL/SQL将DML和查询等SQL语句发送给SQL引擎执行,SQL将结果数据返回给PL/SQL。" 感谢斯蒂芬妮的反馈,更新了我的答案。我不知道它们是同一个引擎,但我试图提出的观点是相同的(即在 SQL 引擎之上有一层 PL/SQL 处理可能会导致性能下降)以上是关于为啥 PLSQL 比 SQL*Plus 慢的主要内容,如果未能解决你的问题,请参考以下文章
windows下, sql plus 远程 连接oracle 不行,但是用客户端, plsql developer远程就可以, 这是为啥啊?