为什么oracle存储过程的执行时间会大大增加取决于它是如何执行的?
Why oracle stored procedure execution time is greatly increased depending on how it is executed?
这是我的问题:我们有一个名为 HEAVY_SP
的存储过程,根据它的执行方式,执行时间会大大增加:
(1)调用执行
在 Oracle 中直接执行 SQL 开发人员 IDE
CALL HEAVY_SP(0, 'F', 5, ...)
需要 15 秒(我们当前的解决方案)
(2) 使用 播放 按钮
正在使用 Oracle SQL Developer 打开程序并执行 "play" 按钮:
需要 15 秒
(3) dbms_job : 计划模式
需要 15 秒
(4) dbms_job : 即时执行模式
需要 1 个多小时
查看数据的处理方式,发现每次迭代都非常慢。
(5) 来自 SQL_PLUS (linux)
需要 1 个多小时,迭代非常慢
(6) 来自 JAVA
需要 1 个多小时,迭代非常慢
(7) 来自 TOAD
需要 1 个多小时,迭代非常慢
研究
我们吃了很多google页如下:
why-does-a-query-run-slower-in-a-stored-procedure-than-in-the-query-window
oracle-pl-sql-procedure-runs-slower-than-sql
oracle-insert-in-stored-procedure-very-slow-compared-to-insert-run-manually
stored-proc-running-30-slower-through-java-versus-running-directly-on-database
所以我的问题是:
- 为什么 Oracle 会这样做?
- 它不应该在所有场景(相同参数)下都表现得很快吗?
- 必须修改存储过程?
- 如果查询计划、跟踪文件或统计信息显示不同的行为,必须修复存储过程?
- 为什么查询 window 中的执行速度很快?
提前致谢。
来自评论的提示
TIP #1
遵循 @BobJarvis 关于统计的建议
结果:我们的统计数据是最新的。甚至,我们在所有有问题的表中重新执行EXEC DBMS_STATS.GATHER_TABLE_STATS(ownname=>'SOME_USER', tabname=>'SOME_TABLE', cascade => TRUE);
,结果是一样的。
TIP #2
遵循 @KonstantinSorokin
的建议
我怀疑执行计划可能因会话设置的不同而不同。考虑比较 v$ses_optimizer_env
Result :我们已经比较了结果 v$ses_optimizer_env 对于 (1 ) 和 (4) 场景。
TIP #3
使用此查询:
select s.sid,s.serial#,s.username, s.machine,replace(q.SQL_FULLTEXT,chr(0)) sql_text, s.program, s.logon_time, s.status, s.OSUSER
from v$session s, v$sql q
where
s.status='ACTIVE'
and s.username is not null
and s.sql_hash_value = q.hash_value
order by s.LOGON_TIME, s.username;
我注意到机器、程序和用户会根据测试发生变化:
快速模式(查询window)
machine | program | ouser
--------------------|------------------ | -------
my laptop username | SQL DEVELOPER | User
滞后模式(后台执行)
machine | program | ouser
--------------------|------------------ | -------
ip-10-6-7-1 | oracle@ip-10-6-7-1| rdsdb
TIP #4
遵循 @KonstantinSorokin 与痕迹相关的建议。
结果 : 一位时态 DBA 进行了调查,他告诉我们一些 sql_id 有不同的执行计划。他的建议是:使用提示。
这可能是解决方案,但是,为什么有些 SQL ID 有不同的执行计划?
[已解决]
感谢@IsaacMejia,NLS_COMP=LINGUISTIC 是执行缓慢的原因。所以 java 不是问题的原因。 Oracle 错误配置是导致我们出现问题的原因。
解决方案必须在实例级别为 NLS_COMP=BINARY 设置正确的值。
但就我而言,我有几个应用程序可以很好地使用该值。因此,为了避免在我们的应用程序中出现排序和比较问题,我无法覆盖实例 NLS 设置。
临时解决方案是在存储过程的开头执行:
execute immediate 'alter session set NLS_COMP=''BINARY''';
和 return 到完成时的先前值:
execute immediate 'alter session set NLS_COMP=''LINGUISTIC''';
现在存储过程 运行 与直接在查询中执行一样快 window (ORACLE SQL DEVELOPER)
尝试从不同的情况(ide 或 java 程序)获取 nls 参数,它们必须不同
select * from NLS_SESSION_PARAMETERS
然后存储过程的 inside 设置变量,使它们在最快情况下相等。
execute immediate 'alter session set NLS_SORT=''SPANISH''';
一旦您的 SP 拥有所有 nls 参数。它会 运行 快。
我最近在 Alter session slows down the query through Hibernate 中发现了一个类似的案例。但在他们的情况下,他们更改了 de 参数,然后变慢了。
我调查发现参数 NLS_COMP y NLS_SORT 可能会影响 oracle 如何使用字符串的执行计划(在比较或排序时)
当 NLS_COMP 定义为 LINGUISTIC 时,它将使用 NLS_SORT 中定义的语言。
例如,如果 NLS_COMP = LINGUISTIC 并且 NLS_SORT=BINARI_AI 您的查询
是
select * from table where string_column like 'HI%'
在内部它会做
select * from table where
NLSSORT(string_column,'BINARI_AI') >= HEXTORAW('324242432')
NLSSORT(string_column,'BINARI_AI') >= HEXTORAW('675757576')
因此,如果您没有 NLSSORT(column,'BINARI_AI') 的索引,它将非常慢。
知道 NLS_SORT=BINARY_AI 将使您的排序和比较不区分重音和区分大小写。
这是我的问题:我们有一个名为 HEAVY_SP
的存储过程,根据它的执行方式,执行时间会大大增加:
(1)调用执行
在 Oracle 中直接执行 SQL 开发人员 IDE
CALL HEAVY_SP(0, 'F', 5, ...)
需要 15 秒(我们当前的解决方案)
(2) 使用 播放 按钮
正在使用 Oracle SQL Developer 打开程序并执行 "play" 按钮:
需要 15 秒
(3) dbms_job : 计划模式
需要 15 秒
(4) dbms_job : 即时执行模式
需要 1 个多小时
查看数据的处理方式,发现每次迭代都非常慢。
(5) 来自 SQL_PLUS (linux)
需要 1 个多小时,迭代非常慢
(6) 来自 JAVA
需要 1 个多小时,迭代非常慢
(7) 来自 TOAD
需要 1 个多小时,迭代非常慢
研究
我们吃了很多google页如下:
why-does-a-query-run-slower-in-a-stored-procedure-than-in-the-query-window
oracle-pl-sql-procedure-runs-slower-than-sql
oracle-insert-in-stored-procedure-very-slow-compared-to-insert-run-manually
stored-proc-running-30-slower-through-java-versus-running-directly-on-database
所以我的问题是:
- 为什么 Oracle 会这样做?
- 它不应该在所有场景(相同参数)下都表现得很快吗?
- 必须修改存储过程?
- 如果查询计划、跟踪文件或统计信息显示不同的行为,必须修复存储过程?
- 为什么查询 window 中的执行速度很快?
提前致谢。
来自评论的提示
TIP #1
遵循 @BobJarvis 关于统计的建议
结果:我们的统计数据是最新的。甚至,我们在所有有问题的表中重新执行EXEC DBMS_STATS.GATHER_TABLE_STATS(ownname=>'SOME_USER', tabname=>'SOME_TABLE', cascade => TRUE);
,结果是一样的。
TIP #2
遵循 @KonstantinSorokin
的建议我怀疑执行计划可能因会话设置的不同而不同。考虑比较 v$ses_optimizer_env
Result :我们已经比较了结果 v$ses_optimizer_env 对于 (1 ) 和 (4) 场景。
TIP #3
使用此查询:
select s.sid,s.serial#,s.username, s.machine,replace(q.SQL_FULLTEXT,chr(0)) sql_text, s.program, s.logon_time, s.status, s.OSUSER
from v$session s, v$sql q
where
s.status='ACTIVE'
and s.username is not null
and s.sql_hash_value = q.hash_value
order by s.LOGON_TIME, s.username;
我注意到机器、程序和用户会根据测试发生变化:
快速模式(查询window)
machine | program | ouser
--------------------|------------------ | -------
my laptop username | SQL DEVELOPER | User
滞后模式(后台执行)
machine | program | ouser
--------------------|------------------ | -------
ip-10-6-7-1 | oracle@ip-10-6-7-1| rdsdb
TIP #4
遵循 @KonstantinSorokin 与痕迹相关的建议。
结果 : 一位时态 DBA 进行了调查,他告诉我们一些 sql_id 有不同的执行计划。他的建议是:使用提示。
这可能是解决方案,但是,为什么有些 SQL ID 有不同的执行计划?
[已解决]
感谢@IsaacMejia,NLS_COMP=LINGUISTIC 是执行缓慢的原因。所以 java 不是问题的原因。 Oracle 错误配置是导致我们出现问题的原因。
解决方案必须在实例级别为 NLS_COMP=BINARY 设置正确的值。
但就我而言,我有几个应用程序可以很好地使用该值。因此,为了避免在我们的应用程序中出现排序和比较问题,我无法覆盖实例 NLS 设置。
临时解决方案是在存储过程的开头执行:
execute immediate 'alter session set NLS_COMP=''BINARY''';
和 return 到完成时的先前值:
execute immediate 'alter session set NLS_COMP=''LINGUISTIC''';
现在存储过程 运行 与直接在查询中执行一样快 window (ORACLE SQL DEVELOPER)
尝试从不同的情况(ide 或 java 程序)获取 nls 参数,它们必须不同
select * from NLS_SESSION_PARAMETERS
然后存储过程的 inside 设置变量,使它们在最快情况下相等。
execute immediate 'alter session set NLS_SORT=''SPANISH''';
一旦您的 SP 拥有所有 nls 参数。它会 运行 快。
我最近在 Alter session slows down the query through Hibernate 中发现了一个类似的案例。但在他们的情况下,他们更改了 de 参数,然后变慢了。
我调查发现参数 NLS_COMP y NLS_SORT 可能会影响 oracle 如何使用字符串的执行计划(在比较或排序时)
当 NLS_COMP 定义为 LINGUISTIC 时,它将使用 NLS_SORT 中定义的语言。
例如,如果 NLS_COMP = LINGUISTIC 并且 NLS_SORT=BINARI_AI 您的查询 是
select * from table where string_column like 'HI%'
在内部它会做
select * from table where
NLSSORT(string_column,'BINARI_AI') >= HEXTORAW('324242432')
NLSSORT(string_column,'BINARI_AI') >= HEXTORAW('675757576')
因此,如果您没有 NLSSORT(column,'BINARI_AI') 的索引,它将非常慢。
知道 NLS_SORT=BINARY_AI 将使您的排序和比较不区分重音和区分大小写。