Oracle PL SQL 在处理过程中的最后一条语句后,过程需要很长时间才能完成

Oracle PL SQL Procedure takes long time to complete, after the very last statement in the procedure is processed

我们正在使用 Oracle 11.2.0.4.0。 我们创建了一个过程来为所有可用行更新 table 中的 CLOB 列。

我们有一个 DBMS_OUTPUT.PUT_LINE 语句在过程的最后一行打印 SYSDATE。

当我们执行这个程序时,在最后一行打印 SYSDATE 之后需要很长时间才能完成。

不确定我是否遗漏了什么。任何帮助将不胜感激

我们尝试通过在过程中使用 DBMS_OUPUT.PUT_LINE(SYSDATE) 语句来执行该过程,以 debug/find 痛点。

这就是我们执行过程并将输出粘贴到最后的方式:

SET TIMING ON
SET SERVEROUTPUT ON

SELECT 'Outside block Start time: ' || to_char(SYSDATE, 'DD-MON-YYYY HH24:MI:SS') OUTSIDEBLOCKSTARTTIME from dual;
DECLARE
  I_LIMIT BINARY_INTEGER;
  I_STR_TO_REPLACE VARCHAR2(200);
  I_REPLACEMENT_STR VARCHAR2(200);
BEGIN
  I_LIMIT := 10000;
  I_STR_TO_REPLACE := 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890';
  I_REPLACEMENT_STR := 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890';

  P_TEST_CLOB_MASK(
    I_LIMIT => I_LIMIT,
    I_STR_TO_REPLACE => I_STR_TO_REPLACE,
    I_REPLACEMENT_STR => I_REPLACEMENT_STR
  );

END;
/
SELECT 'Outside block End time: ' || to_char(SYSDATE, 'DD-MON-YYYY HH24:MI:SS') OUTSIDEBLOCKENDTIME from dual;

以下是程序的代码:

CREATE OR REPLACE PROCEDURE p_test_clob_mask (
   i_limit             IN   PLS_INTEGER DEFAULT 1000,
   i_str_to_replace    IN   VARCHAR2
         DEFAULT 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890',
   i_replacement_str   IN   VARCHAR2
         DEFAULT 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890'
)
IS
   CURSOR cur_fetch_cddaddcomphistory
   IS
      SELECT ROWID row_id, ua4cf_cddaddcomphistory ua4cf_cddaddcomphistory
        FROM generic_set11
       WHERE ua4cf_cddaddcomphistory IS NOT NULL;
   l_limit                NUMBER         := 0;
   l_counter              NUMBER         := 0;
   l_rowid                ROWID;
   l_hold_clob            CLOB;
   l_masked_clob          CLOB;
   l_length_clob          NUMBER         := 0;
   l_str_to_replace       VARCHAR2 (500);
   l_replacement_str      VARCHAR2 (500);
   l_overall_start_dt     DATE;
   l_batch_start_dt       DATE           := NULL;
   l_batch_time_taken     NUMBER         := 0;
   l_overall_time_taken   NUMBER         := 0;
BEGIN
   l_overall_start_dt := SYSDATE;
   DBMS_OUTPUT.put_line (   'Overall Start: '
                         || TO_CHAR (l_overall_start_dt,
                                     'DD-MON-RR HH24:MI:SS'
                                    )
                        );
   l_str_to_replace := i_str_to_replace;
   l_replacement_str := i_replacement_str;
   l_limit           := i_limit;

   OPEN cur_fetch_cddaddcomphistory;

   LOOP
      FETCH cur_fetch_cddaddcomphistory
       INTO l_rowid, l_hold_clob;

      EXIT WHEN cur_fetch_cddaddcomphistory%NOTFOUND;
      l_counter := l_counter + 1;
      l_length_clob := DBMS_LOB.getlength (l_hold_clob);
      DBMS_LOB.createtemporary (l_masked_clob, TRUE);
      DBMS_LOB.writeappend (l_masked_clob,
                            l_length_clob,
                            TRANSLATE (l_hold_clob,
                                       i_str_to_replace,
                                       i_replacement_str
                                      )
                           );

      UPDATE generic_set11
         SET ua4cf_cddaddcomphistory = l_masked_clob
       WHERE ROWID = l_rowid;

      IF MOD (l_counter, l_limit) = 0
      THEN
         COMMIT;
      END IF;
   END LOOP;

   CLOSE cur_fetch_cddaddcomphistory;

   COMMIT;
   l_overall_time_taken :=
                        TRUNC (((SYSDATE - l_overall_start_dt) * 24 * 60), 2);
   DBMS_OUTPUT.put_line (   'Overall End: '
                         || TO_CHAR (SYSDATE, 'DD-MON-RR HH24:MI:SS')
                         || ' - Time taken in Mins: '
                         || l_overall_time_taken
                         || ' - Counters: '
                         || l_counter
                        );
END p_test_clob_mask;
/

我们的期望是在最后一个 DBMS_OUPUT.PUT_LINE 完成后,程序应该完成。

但是经过的时间显示 20 分钟以上,而根据最后的 DBMS_OUPUT.PUT_LINE 声明,所用时间仅为 3 分钟

下面是输出:

OUTSIDEBLOCKSTARTTIME
-------------------------------------------------------
Outside block Start time: 27-DEC-2018 15:33:45

Elapsed: 00:00:00.118
Overall Start: 27-DEC-18 15:33:45
Overall End: 27-DEC-18 15:37:37 - Time taken in Mins: 3.86 - Counters: 138913


PL/SQL procedure successfully completed.

Elapsed: 00:20:19.313

OUTSIDEBLOCKENDTIME
-----------------------------------------------------
Outside block End time: 27-DEC-2018 15:54:04

Elapsed: 00:00:00.095

根据评论,修复是添加行

    DBMS_LOB.freetemporary(l_masked_clob);

在循环结束时。这确保使用的临时 CLOBs 在使用时被清理。编写的代码导致 Oracle 为循环的每次迭代创建一个新的临时 CLOB,并且很可能清除所有这些临时 CLOB(可能有数百甚至数千个)在块的末尾导致延迟。

另一种可能的性能提升可以通过在循环外创建一个临时 LOB 并在每次迭代结束时将其清除(例如使用 DBMS_LOB.eraseDBMS_LOB.trim)来获得循环,并在最后释放它。