簡體   English   中英

插入CLOB非常慢

[英]Inserting into CLOB very very slow

與VARCHAR2相比,在將'TinyString'字符串(只是一個示例字符串)插入到行內CLOB時,我遇到了顯着的性能下降。 我的理解是,當將<4000字節的數據存儲到啟用了STORAGE IN ROW的CLOB中時,數據有效地以與VARCHAR2相同的方式存儲(除非它'溢出'4000字節)並且應該沒有顯着的性能下降。 但是,我的基准測試程序*表明將相同數據插入CLOB比插入VARCHAR2 慢15倍

看看下面的代碼:

我有很多桌子,每個桌子都附有一個類似下面的COMPOUND TRIGGER:

CREATE OR REPLACE TRIGGER mdhl_basic_trigger_compound
  FOR INSERT OR UPDATE OR DELETE ON target_table

  COMPOUND TRIGGER TYPE EVENTS_HIST IS TABLE OF log_table%ROWTYPE INDEX BY PLS_INTEGER;
                                                coll_events_hist EVENTS_HIST;
                                                ctr PLS_INTEGER := 0;
                                                my_bgroup VARCHAR2(3);

  BEFORE EACH ROW IS    
    BEGIN

      IF INSERTING OR UPDATING THEN
        my_bgroup  := :NEW.BGROUP;
      ELSE
        my_bgroup  := :OLD.BGROUP;
      END IF;

      ctr := ctr + 1;
      coll_events_hist(ctr).BGROUP := my_bgroup;
      coll_events_hist(ctr).TABLE_NAME := 'BASIC_MDHL';
      coll_events_hist(ctr).EVENT_TS := current_timestamp;         
      coll_events_hist(ctr).EVENT_RAW := 'TinyString';

  END BEFORE EACH ROW;

  AFTER STATEMENT IS 
    BEGIN
      FORALL counter IN 1 .. coll_events_hist.count() 
           INSERT INTO log_table VALUES coll_events_hist(counter); 
  END AFTER STATEMENT; 
END mdhl_basic_trigger_compound;

當任何操作target_table ,在填充上述觸發存儲數據coll_events_hist類型成log_table ,其以下面的方式定義的:

CREATE TABLE "USERNAME"."LOG_TABLE" 
   (  "BGROUP" VARCHAR2(3) NOT NULL ENABLE, 
        "TABLE_NAME" VARCHAR2(255) NOT NULL ENABLE, 
      "EVENT_TS" TIMESTAMP (7) DEFAULT current_timestamp, 
      "EVENT_RAW" CLOB
   ) 
  SEGMENT CREATION IMMEDIATE 
  PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255 NOCOMPRESS LOGGING
  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT)
  TABLESPACE "USERS" 
 LOB ("EVENT_RAW") STORE AS BASICFILE "EV_RAW_SEG"(
  TABLESPACE "USERS" ENABLE STORAGE IN ROW CHUNK 16384 PCTVERSION 5
  CACHE 
  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT))

我的設置是:Windows 7 SP1,Oracle 11g

*我的benchamrking過程迭代10次,在每次迭代中更新target_table上的21k行。

在你的情況下,“tinystring”總是<32767?

你的時間將在FORALL部分浪費,查看你所做的所有臨時工作。

你會發現每個行部分的插入更好的性能:

例如在我的測試系統上使用你的高音扳機:

SQL> insert into target_Table select 'ABC' from dual connect by level <= 10000;

10000 rows created.

Elapsed: 00:00:10.49

vs觸發器:

SQL> CREATE OR REPLACE TRIGGER mdhl_basic_trigger
  2    before INSERT OR UPDATE OR DELETE ON target_table for each row
  3  declare
  4
  5  my_bgroup VARCHAR2(3);
  6
  7    v_timer2 number := 0;
  8    v_timer number;
  9  BEGIN
 10
 11        IF INSERTING OR UPDATING THEN
 12          my_bgroup  := :NEW.BGROUP;
 13        ELSE
 14          my_bgroup  := :OLD.BGROUP;
 15        END IF;
 16
 17        INSERT INTO log_table VALUES(my_bgroup, 'BASIC_MDHL', current_timestamp, 'TinyString');
 18
 19  END mdhl_basic_trigger;
 20  /

SQL> insert into target_Table select 'ABC' from dual connect by level <= 10000;

10000 rows created.

Elapsed: 00:00:01.18

如果你知道你的字符串總是<32k你可以保持forall以獲得提升速度,如果你創建你的觸發器:

SQL> CREATE OR REPLACE TRIGGER mdhl_basic_trigger_compound
  2    FOR INSERT OR UPDATE OR DELETE ON target_table
  3
  4     COMPOUND TRIGGER
  5
  6     type events_rec is record (BGROUP VARCHAR2(3),
  7          TABLE_NAME VARCHAR2(255) ,
  8        EVENT_TS TIMESTAMP (7),
  9        EVENT_RAW varchar2(32767));
 10     TYPE EVENTS_HIST IS TABLE OF events_rec INDEX BY PLS_INTEGER;
 11     coll_events_hist EVENTS_HIST;
 12     ctr PLS_INTEGER := 0;
 13     my_bgroup VARCHAR2(3);
 14
 15  v_timer2 number := 0;
 16  v_timer number;
 17    BEFORE EACH ROW IS
 18      BEGIN
 19
 20        IF INSERTING OR UPDATING THEN
 21          my_bgroup  := :NEW.BGROUP;
 22        ELSE
 23          my_bgroup  := :OLD.BGROUP;
 24        END IF;
 25
 26        ctr := ctr + 1;
 27        coll_events_hist(ctr).BGROUP := my_bgroup;
 28        coll_events_hist(ctr).TABLE_NAME := 'BASIC_MDHL';
 29        coll_events_hist(ctr).EVENT_TS := current_timestamp;
 30        coll_events_hist(ctr).EVENT_RAW := 'TinyString';
 31
 32    END BEFORE EACH ROW;
 33
 34    AFTER STATEMENT IS
 35      BEGIN
 36  v_timer := dbms_utility.get_time;
 37        FORALL counter IN 1 .. coll_events_hist.count()
 38             INSERT INTO log_table VALUES coll_events_hist(counter);
 39  v_timer2 := v_timer2 + (dbms_utility.get_time - v_timer);
 40             dbms_output.put_line(v_timer2/100);
 41    END AFTER STATEMENT;
 42  END mdhl_basic_trigger_compound;
 43  /
SQL> insert into target_Table select 'ABC' from dual connect by level <= 10000;

10000 rows created.

Elapsed: 00:00:00.39

即推遲吊桿操作直到插入。

即使CLOB以串聯方式存儲,與標准VARCHAR2相比也存在一些開銷,如LOB性能指南的附錄C中所述

LOB的長度小於3964字節時,它以36字節的頭部內聯存儲。 長度為X的VARCHAR2將被存儲為X字節的數據,並帶有額外的一個或兩個字節的開銷。

我認為這種開銷將帶入內存,這意味着PLSQL CLOB對象的效率將低於同等大小的VARCHAR2

將使用以下腳本演示34-35個額外字節:

SQL> create table test_var(a varchar2(4000));

Table created

SQL> create table test_clob(a clob);

Table created

SQL> SET SERVEROUTPUT ON
SQL> DECLARE
  2    l_time TIMESTAMP := systimestamp;
  3  BEGIN
  4    FOR i IN 1..100000 LOOP
  5      INSERT INTO test_var VALUES (rpad('x', 1000, 'x'));
  6    END LOOP;
  7    dbms_output.put_line(systimestamp - l_time);
  8  END;
  9  /
+000000000 00:00:16.180299000

SQL> DECLARE
  2    l_time TIMESTAMP := systimestamp;
  3  BEGIN
  4    FOR i IN 1..100000 LOOP
  5      INSERT INTO test_clob VALUES (rpad('x', 1000, 'x'));
  6    END LOOP;
  7    dbms_output.put_line(systimestamp - l_time);
  8  END;
  9  /
+000000000 00:00:27.180716000

插入CLOB需要更多時間,這可以通過消耗的額外空間來解釋:

SQL> EXEC dbms_stats.gather_table_stats(USER, 'TEST_VAR');

PL/SQL procedure successfully completed.

SQL> EXEC dbms_stats.gather_table_stats(USER, 'TEST_CLOB');

PL/SQL procedure successfully completed.

SQL> select blocks, table_name from user_tables where table_name like 'TEST_%';

    BLOCKS TABLE_NAME
---------- ------------------------------
     33335 TEST_CLOB
     28572 TEST_VAR

當我們插入較小的字符串時,問題更加嚴重:

-- after TRUNCATE tables
SQL> DECLARE
  2    l_time TIMESTAMP := systimestamp;
  3  BEGIN
  4    FOR i IN 1..1000000 LOOP
  5      INSERT INTO test_var VALUES (rpad('x', 10, 'x'));
  6    END LOOP;
  7    dbms_output.put_line(systimestamp - l_time);
  8  END;
  9  /

+000000000 00:00:51.916675000

SQL> DECLARE
  2    l_time TIMESTAMP := systimestamp;
  3  BEGIN
  4    FOR i IN 1..1000000 LOOP
  5      INSERT INTO test_clob VALUES (rpad('x', 10, 'x'));
  6    END LOOP;
  7    dbms_output.put_line(systimestamp - l_time);
  8  END;
  9  /

+000000000 00:01:57.377676000

-- Gather statistics

SQL> select blocks, table_name from user_tables where table_name like 'TEST_%';

    BLOCKS TABLE_NAME
---------- ------------------------------
      7198 TEST_CLOB
      2206 TEST_VAR

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM