oracle SQL优化之高效的函数调用

原文地址:https://oracle-base.com/articles/misc/efficient-function-calls-from-sql

     http://www.oracle.com/technetwork/issue-archive/2011/11-sep/o51asktom-453438.html

1. 问题描述

  我们说一个function是确定性的(deterministic),当对于相同的输入总是返回相同的输出,oracle中的内置函数如abs,不管多少次调用,abs(-1)总是返回1。假设在sql中调用这种function,如果存在相同的输入数据,每次调用都要重新执行function的话就会产生性能浪费。 

create table func_test(id number);

--插入数据
INSERT INTO func_test
SELECT CASE
         WHEN level = 10 THEN 3
         WHEN MOD(level, 2) = 0 THEN 2
         ELSE 1
       END
FROM   dual
CONNECT BY level <= 10;
COMMIT;
create or replace function slow_function(
       p_in  number
) return number
  deterministic 
is
begin
  sys.dbms_lock.sleep(1);
  return p_in;
end;
SQL> set timing on;
SQL> select slow_function(id) from func_test;
SLOW_FUNCTION(ID)
-----------------
                1
                2
                1
                2
                1
                2
                1
                2
                1
                3
10 rows selected
Executed in 10.046 seconds

上述SQL执行时间为10.046秒,说明对于每一行数据,都执行了slow_function方法。由于slow_function是确定性的:对于slow_function(1)用于返回1,所以上述SQL对于造成了性能浪费。

 

2. 标量子查询缓存(scalar subquery caching)

 标量子查询缓存会通过缓存结果减少sql对function的调用次数,改用标量子查询的sql仅仅用时3.057秒,即SQL对于slow_function的调用只发生了三次。

SQL> select (select slow_function(id) from dual) from func_test;
(SELECTSLOW_FUNCTION(ID)FROMDU
------------------------------
                             1
                             2
                             1
                             2
                             1
                             2
                             1
                             2
                             1
                             3
10 rows selected
Executed in 3.057 seconds

当使用标量子查询的时候,oracle会在内存中建立一个很小的hash table用于缓存子查询结果,对多可以缓存255个子查询结果:

select (select slow_function(id) from dual) from func_test;
 
  id slow_function(id)

当第一次查询slow_function(1)的时候,由于hash table没有缓存,所以需要执行slow_function;当第二次查询slow_function(1)的时候可以直接从hash table拿出结果,而不用再次调用slow_function。即便有可能发生hash冲突,而且hash table只支持255个bucket,标量子查询对于性能提高总是好的。

 

3. DETERMINISTIC

  oracle通过关键字DETERMINISTIC来表名一个function是确定性的,确定性函数可以用于创建基于函数的索引。

create or replace function slow_function(
       p_in  number
) return number 
deterministic
is
begin
  sys.dbms_lock.sleep(1);
  return p_in;
end;

deterministic缓存受限于每次从服务器fetch多少数据,缓存仅在当前fetch的生命周期内有效,而标量子查询是当前查询内有效。

假设set arraysize 1 DETERMINISTIC关键字不会对性能起到任何帮助,set arraysize 15,也仅仅是每15条数据的缓存结果可以重用。

 

4. RESULT_CACHE

  oracle通过关键字RESULT_CACHE对函数返回的结果进行缓存,缓存结果可以被session共享。

create or replace function slow_function(p_in number) 
return number 
result_cache 
is
begin
  sys.dbms_lock.sleep(1);
  return p_in;
end;

通过关键字result_cache的函数在执行过以后,速度会大幅提升。

 

5. 即便通过deterministic和result_cache可以提高,但我们总是应该使用标量子查询,因为deterinisitic和result_cache仅仅是缓存结果,并不能减少SQL和PLSQL上下文质检的切换,即总是会发生SQL引擎和PLSQL引擎的交互,并不会减少对CPU的消耗。

 标量子查询也可以作用于where子句以提高查询性能。

 

6. 函数调用中的读一致性问题

  在sql中调用function,假设function中也有sql的话,在隔离级别为read committed的情况下,有可能会发生不可重复读和幻想读。可以通过dbms_flashback保证读一致性。 

EXEC DBMS_FLASHBACK.enable_at_time(SYSTIMESTAMP);
SELECT slow_function(id)
FROM   func_test;
EXEC DBMS_FLASHBACK.disable;

 

 

  

 

posted on 2016-02-21 13:54  收苞米的拖拉机  阅读(3355)  评论(0编辑  收藏  举报