3

'TinyString'VARCHAR2と比較して、(単なる例の文字列)の文字列を行に格納されたCLOBに挿入すると、パフォーマンスが大幅に低下します。私の理解では、STORAGE IN ROWが有効になっているCLOBに4000バイト未満のデータを格納する場合、データはVARCHAR2と同じ方法で効果的に格納され(「オーバーフロー」4000バイトでない限り)、パフォーマンスが大幅に低下することはありません。ただし、私のベンチマーク手順*は、同じデータをCLOBに挿入するのがVARCHAR2に挿入するよりも15倍遅いことを示しています。

以下のコードをご覧ください。

いくつかのテーブルがあり、それぞれに以下のようなCOMPOUNDTRIGGERがアタッチされています。

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))

私のセットアップは次のとおりです。Windows7SP1、Oracle 11g

*私のbenchamrkingプロシージャは、各反復でtarget_tableの21k行を更新することを10回繰り返します。

4

2 に答える 2

3

あなたの場合、「tinystring」は常に<32767ですか?

あなたの時間は、あなたが作ったすべての一時的なロブを調べるFORALL部分で無駄になります。

各行部分のを挿入すると、パフォーマンスが向上します。

たとえば、lobトリガーを使用したテストシステムでは、次のようになります。

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

10000 rows created.

Elapsed: 00:00:10.49

対トリガーを次のように持つ:

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であることがわかっている場合は、次のようにトリガーを作成すると、フォラルを維持して速度を上げることができます。

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

つまり、挿入までlob操作を延期します。

于 2013-02-05T11:38:12.127 に答える
1

aCLOBがインラインで保存されている場合でも、LOBパフォーマンスガイドラインの付録CでVARCHAR2説明されているように、標準と比較してオーバーヘッドがあります。

aの長さがLOB3964バイト未満の場合、36バイトのヘッダーでインラインに格納されます。長さXのAVARCHAR2は、追加の1バイトまたは2バイトのオーバーヘッドを伴う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
于 2013-02-05T10:55:34.613 に答える