Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/performance/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Performance 为什么PLSQL比SQL*Plus慢_Performance_Oracle_Plsql - Fatal编程技术网

Performance 为什么PLSQL比SQL*Plus慢

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正在查看光标移动的时间 打

我有几个Oracle查询在通过SQL*PLUS运行时表现良好。但是,当它们作为PL/SQL包的一部分执行时,它们需要更长的时间

我们的DBA通过PLSQL观察到这些查询需要10分钟,通过SQL*Plus需要10秒

有没有人对在哪里查找错误配置有任何指示

客户端-Windows 2000 服务器-Linux(Oracle企业版)

谢谢

--

决议:

我希望我能接受每个人的答案。他们中有几个人很有帮助


  • 查询正在转换数据类型
  • 执行计划不一致。 (提示修复了该问题。)
  • DBA正在查看光标移动的时间 打开而不是查询时间。

使用SQL跟踪查看每种情况下的执行计划。我突然想到一种可能性(来自经验):包是否将错误类型的值绑定到查询?在SQL Plus中,您可能正在运行:

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相同的计划之前一直使用提示

其他可能的问题:

  • SQL*Plus只返回前100或 所以,行,然后等待您要求更多,但PL/SQL必须处理 他们都不请自来。琐碎的问题,但有时被忽视
  • SQL*Plus使用常量,PL/SQL使用绑定变量。有时,使用常量可以让优化器检查扭曲的数据,也可以使用其他索引
  • 通过SQLPlus发布的DML(例如选择、更新、删除)直接发布到Oracle的SQL引擎,而PLSQL过程中的DML首先由PL/SQL处理(例如进行变量绑定),然后发送到SQL引擎

    在大多数情况下,PL/SQL中的同一语句将执行与SQL相同的操作,并且这两种方式通常会产生相同的执行计划。根据我的经验(通常在需要绑定变量时),它会导致非常不同的性能。我曾经看到过这样的情况:在sqlplus中发出SELECT只需几秒钟,而通过PL/SQL发出SELECT则需要1-2分钟


    我建议您调整您的语句,使其在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处理可能会导致性能下降)