sql - 为什么 PLSQL 优化级别没有提供预期结果?

标签 sql oracle plsql oracle-sqldeveloper plsql-package

我试图深入了解 plsql 中的编译器优化。理论上,默认优化级别 PLSQL_OPTIMIZE_LEVEL 设置为 2。为了获得更好的性能,我们可以将其设置为 3。为了向自己解释这一点,我使用一个过程调用另一个过程的示例,因此可以使用级别3(内联过程)的优化功能。

这是第一个过程:

create or replace procedure p1
is
n number:=0;
begin
for i in 1..500000000
loop
n:=n+1;
end loop;
end;

这是第二个:

create or replace procedure CALL_PROC_ARITH
IS
BEGIN
for i in 1..10
loop
P1;
end loop;
END;

这是两个过程的plsql_code_type,即INTERPRETED

plsql_code_type

所以最初两个过程的优化级别都是2。当我执行过程CALL_PROC_ARITH时,大约需要05:00.866分钟。
后来我在 session 级别将优化级别修改为3。当我执行过程CALL_PROC_ARITH时,大约需要00:05:05.011分钟,增加了5秒。

有人可以告诉我为什么我会看到这种与预期行为的偏差吗?
如果我使用 NATIVE 编译,我会看到不同的结果吗?

注意:我是从 IDE 运行它,而不是直接从 SQLPlus CLI 运行。

数据库:Oracle 18c XE

最佳答案

您应该使用更好的资源来理解 PLSQL_OPTIMIZE_LEVEL,并且您应该确保以正确的方式测试正确的内容。

1。 PLSQL_OPTIMIZE_LEVEL 如何工作?

了解任何参数的最佳方法是使用官方文档中的数据库引用。参数 PLSQL_OPTIMIZE_LEVEL 经常更改,因此请确保引用准确的版本。网络上有很多非官方的、过时的信息,但是here's the relevant text for 18c :

0

Maintains the evaluation order and hence the pattern of side effects, exceptions, and package initializations of Oracle9i and earlier releases. Also removes the new semantic identity of BINARY_INTEGER and PLS_INTEGER and restores the earlier rules for the evaluation of integer expressions. Although code will run somewhat faster than it did in Oracle9i, use of level 0 will forfeit most of the performance gains of PL/SQL in Oracle Database 10g.

1

Applies a wide range of optimizations to PL/SQL programs including the elimination of unnecessary computations and exceptions, but generally does not move source code out of its original source order.

2

Applies a wide range of modern optimization techniques beyond those of level 1 including changes which may move source code relatively far from its original location.

3

Applies a wide range of optimization techniques beyond those of level 2, automatically including techniques not specifically requested.

该描述使得很难判断内联何时发生。听起来内联可能发生在级别1,可能发生在级别2。我下面的测试显示从0到1的内联性能差异很大,与1 到 2,2 到 3 没有区别。

但是很多行为都没有记录,因此很难判断何时会发生哪种优化。

2。设置级别后是否重新编译代码?

仅设置 session 值是不够的,还必须重新编译程序,如下所示:

alter session set plsql_optimize_level=3;
alter procedure call_proc_arith compile;
alter procedure p1 compile;

3。您真的在测试内联吗?

您的程序包含大量循环和过程调用,但我认为您的数字是倒退的。要测试内联,必须让大循环调用该过程,并让小循环进行计数。您永远不会注意到仅 10 次过程调用的编译器差异。

我使用这些程序进行测试:

create or replace procedure p2 is
    n number:=0;
begin
    for i in 1..5 loop
        n:=n+1;
    end loop;
end;
/

create or replace procedure CALL_PROC_ARITH2 is
begin
    for i in 1..10000000 loop
        p2;
    end loop;
end;
/

--Check the PL/SQL optimize level for the objects.
select name, plsql_optimize_level, plsql_code_type
from all_plsql_object_settings
where owner = user
    and name like 'CALL_PROC%' or name like 'P_';

4。您的测试方法足够稳健吗?

您的测试应该尝试补偿消耗 CPU 的其他事件。以交替顺序运行多个小测试,抛出高值和低值,并比较平均值。两次运行五分钟测试的五秒差异并不显着。

我使用以下 PL/SQL block 来测试运行时间。 (您可以构建一个 PL/SQL 程序来以随机顺序运行 block 并记录时间。我手动完成了这部分。)级别 3 和级别 2 运行速度相同,级别 1 稍微慢一点,级别 0 明显慢一些。速度较慢。

--Level 3: 3.331, 3.403, 3.419
alter session set plsql_optimize_level = 3;
alter procedure call_proc_arith2 compile;
alter procedure p2 compile;

begin
    call_proc_arith2;
end;
/

--Level 2: 3.383, 3.470, 3.444
alter session set plsql_optimize_level = 2;
alter procedure call_proc_arith2 compile;
alter procedure p2 compile;

begin
    call_proc_arith2;
end;
/

--Level 1: 3.867, 3.859, 3.873
alter session set plsql_optimize_level = 1;
alter procedure call_proc_arith2 compile;
alter procedure p2 compile;

begin
    call_proc_arith2;
end;
/

--Level 0: 6.286, 6.296, 6.315
alter session set plsql_optimize_level = 0;
alter procedure call_proc_arith2 compile;
alter procedure p2 compile;

begin
    call_proc_arith2;
end;
/

5。您关心 PL/SQL 优化吗?

在大多数现实世界的 PL/SQL 程序中,内联过程不会产生有意义的差异。最佳实践是使用 SQL 完成尽可能多的繁重工作。但无论您的逻辑在哪里,请确保您使用的是分析器,并且仅调整需要大量时间的程序部分。在调整 PL/SQL 程序的一部分之前,您应该有一些硬性数字,例如“如果我优化第 X 行,程序的运行速度可以提高 Y%。”

关于sql - 为什么 PLSQL 优化级别没有提供预期结果?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63879436/

相关文章:

oracle - 如何在Oracle中定义具有动态返回类型的pl sql函数?

python - mysqldb cursor.close() throws ProgrammingError : (2014, "commands out of sync...") 执行从文件读取的sql时

sql - 在 Oracle 中创建直方图/频率分布的最佳方法?

sql - 在另一个 plsql 脚本中调用 plsql 脚本

oracle - 从云 Azure 应用程序连接到 Oracle 服务器

sql - 未选择 Oracle sql 空值

sql选择到

php - 使用php在sql数据库中插入多行

php - mysql 日期间隔、php 日期间隔

java - 在JAVA中使用Solr和MySQL进行查询