技术开发 频道

点评Oracle11g新特性:PLSQL新特性


    Oracle在调用函数的时候可以将调用过程直接替换为子查询的代码,这样可以避免在运行过程中调用开销,Oracle提供了一个新的PRAGMA INLINE来指示函数是否进行INLINE优化:
SQL> CREATE OR REPLACE FUNCTION F_TEST (P_IN IN NUMBER) RETURN NUMBER AS 2 BEGIN 3 IF P_IN > 1 THEN 4 RETURN P_IN + F_TEST(P_IN - 1); 5 ELSE 6 RETURN 1; 7 END IF; 8 END; 9 / 函数已创建。 SQL> CREATE OR REPLACE FUNCTION F_TEST_INLINE (P_IN IN NUMBER) RETURN NUMBER AS 2 BEGIN 3 IF P_IN > 1 THEN 4 PRAGMA INLINE (F_TEST_INLINE, 'YES'); 5 RETURN P_IN + F_TEST_INLINE(P_IN - 1); 6 ELSE 7 RETURN 1; 8 END IF; 9 END; 10 / 函数已创建。
    分别建立两个递归调用的函数,一个使用了11g提供的PRAGMA INLINE,强制Oracle对函数F_TEST_INLINE进行INLINE优化,看看调用两个函数所需的时间:
SQL> SET TIMING ON SQL> SELECT F_TEST_INLINE(1000000) FROM DUAL; F_TEST_INLINE(1000000) ---------------------- 5.0000E+11 已用时间: 00: 00: 01.32 SQL> SELECT F_TEST(1000000) FROM DUAL; F_TEST(1000000) --------------- 5.0000E+11 已用时间: 00: 00: 18.68 SQL> SELECT F_TEST_INLINE(1000000) FROM DUAL; F_TEST_INLINE(1000000) ---------------------- 5.0000E+11 已用时间: 00: 00: 00.42 SQL> SELECT F_TEST(1000000) FROM DUAL; F_TEST(1000000) --------------- 5.0000E+11 已用时间: 00: 00: 00.57

    无论是第一次调用,还是第二次调用,使用了INLINE的方法都比普通调用有一个明显的性能提升。
    除了强制使用PRAGMA INLINE方法外,Oracle还提供了自动INLINE的方式,只需要将初始化参数

PLSQL_OPTIMIZE_LEVLE设置为3,Oracle会自动选择合适的过程进行INLINE优化: SQL> SHOW PARAMETER PLSQL_OPTIMIZE_LEVEL NAME TYPE VALUE ------------------------------------ ----------- ------------------------------ plsql_optimize_level integer 2 SQL> ALTER SESSION SET PLSQL_OPTIMIZE_LEVEL = 3; 会话已更改。 SQL> CREATE OR REPLACE FUNCTION F_TEST (P_IN IN NUMBER) RETURN NUMBER AS 2 BEGIN 3 IF P_IN > 1 THEN 4 RETURN P_IN + F_TEST(P_IN - 1); 5 ELSE 6 RETURN 1; 7 END IF; 8 END; 9 / 函数已创建。 SQL> SELECT F_TEST(1000000) FROM DUAL; F_TEST(1000000) --------------- 5.0000E+11 已用时间: 00: 00: 00.40 SQL> SELECT F_TEST(1000000) FROM DUAL; F_TEST(1000000) --------------- 5.0000E+11 已用时间: 00: 00: 00.46

    可以看到设置了自动优化功能,F_TEST函数的调用也达到了PRAGMA INLINE函数的相同效果。需要说明的是,INLINE优化在大部分情况下会带来性能的提升,但是这个性能的提升是以占用更多的内存为代价的。下面看看两种方式的统计差别:

SQL> ALTER SESSION SET PLSQL_OPTIMIZE_LEVEL = 2; 会话已更改。 SQL> CREATE OR REPLACE FUNCTION F_TEST (P_IN IN NUMBER) RETURN NUMBER AS 2 BEGIN 3 IF P_IN > 1 THEN 4 RETURN P_IN + F_TEST(P_IN - 1); 5 ELSE 6 RETURN 1; 7 END IF; 8 END; 9 / 函数已创建。 SQL> CREATE OR REPLACE FUNCTION F_TEST_INLINE (P_IN IN NUMBER) RETURN NUMBER AS 2 BEGIN 3 IF P_IN > 1 THEN 4 PRAGMA INLINE (F_TEST_INLINE, 'YES'); 5 RETURN P_IN + F_TEST_INLINE(P_IN - 1); 6 ELSE 7 RETURN 1; 8 END IF; 9 END; 10 / 函数已创建。 SQL> SET SERVEROUT ON SQL> DECLARE 2 V_NUMBER1 NUMBER; 3 V_NUMBER2 NUMBER; 4 V_RES NUMBER; 5 BEGIN 6 7 INSERT INTO T_SESSION_STAT SELECT 1, 'STAT:' || NAME, VALUE 8 FROM V$SESSTAT A, V$STATNAME B 9 WHERE A.STATISTIC# = B.STATISTIC# 10 AND A.SID = (SELECT SID FROM V$MYSTAT WHERE ROWNUM = 1) 11 UNION ALL 12 SELECT 1, 'LATCH:' || NAME, GETS VALUE FROM V$LATCH; 13 14 V_NUMBER1 := DBMS_UTILITY.GET_TIME; 15 V_RES := F_TEST_INLINE(1000000); 16 V_NUMBER1 := DBMS_UTILITY.GET_TIME - V_NUMBER1; 17 18 INSERT INTO T_SESSION_STAT SELECT 2, 'STAT:' || NAME, VALUE 19 FROM V$SESSTAT A, V$STATNAME B 20 WHERE A.STATISTIC# = B.STATISTIC# 21 AND A.SID = (SELECT SID FROM V$MYSTAT WHERE ROWNUM = 1) 22 UNION ALL 23 SELECT 2, 'LATCH:' || NAME, GETS VALUE FROM V$LATCH; 24 25 V_NUMBER2 := DBMS_UTILITY.GET_TIME; 26 V_RES := F_TEST(1000000); 27 V_NUMBER2 := DBMS_UTILITY.GET_TIME - V_NUMBER2; 28 29 INSERT INTO T_SESSION_STAT SELECT 3, 'STAT:' || NAME, VALUE 30 FROM V$SESSTAT A, V$STATNAME B 31 WHERE A.STATISTIC# = B.STATISTIC# 32 AND A.SID = (SELECT SID FROM V$MYSTAT WHERE ROWNUM = 1) 33 UNION ALL 34 SELECT 3, 'LATCH:' || NAME, GETS VALUE FROM V$LATCH; 35 36 DBMS_OUTPUT.PUT_LINE('P1 EXECUTE ' || V_NUMBER1/100 || ' SECONDS'); 37 DBMS_OUTPUT.PUT_LINE('P2 EXECUTE ' || V_NUMBER2/100 || ' SECONDS'); 38 39 FOR C IN 40 ( 41 SELECT * 42 FROM 43 ( 44 SELECT A.NAME, C.VALUE + A.VALUE - 2 * B.VALUE VALUE 45 FROM 46 T_SESSION_STAT A, 47 T_SESSION_STAT B, 48 T_SESSION_STAT C 49 WHERE A.NAME = B.NAME 50 AND A.NAME = C.NAME 51 AND A.ID = 1 52 AND B.ID = 2 53 AND C.ID = 3 54 ) 55 WHERE ABS(VALUE) > 100 56 ) LOOP 57 DBMS_OUTPUT.PUT_LINE(RPAD(C.NAME, 50, ' ') || C.VALUE); 58 END LOOP; 59 60 END; 61 / P1 EXECUTE 1.4 SECONDS P2 EXECUTE 2.26 SECONDS STAT:session pga memory 89194496 PL/SQL 过程已成功完成。

    Oracle的inline操作将调用操作直接替换为程序的代码,避免了调用的开销,获得了明显的性能提升,但是这种方法消耗更多的PGA内容也是显而易见的。
    对于特别复杂的调用情况,或者递归、循环调用次数过多,可能会造成INLINE调用方式的内存占用过大,从而导致性能下降甚至出现内存耗尽的情况。INLINE带来的并非总是性能的提升,使用不当也可能造成严重的性能问题。
    Oracle提供的PRAGMA INLINE同样可以强制禁止过程INLINE优化的发生。

0
相关文章