L
o
a
d
i
n
g
.
.
.

ホーム

お知らせ

製品・ソリューション

サービス

導入事例・パートナー

EXEM Academy・ブログ

会社情報

採用情報

2022.04.12

大量のDML作業のためのパフォーマンス改善方法

大量のDML作業のためのパフォーマンス改善方法

㈱エクセンコンサルティング本部/DBコンサルティングチーム パク・ジュンヨン

大量のデータを変更しなければならない作業は、それ自体だけでも大きな負担です。

 しかし、変更作業自体に限定されず、変更前のデータと変更後のデータをそれぞれ保存管理しなければならないメカニズムであれば、性能を改善しなければならないという立場ではさらに大きな負担となります。

文字通り大量のデータを変更しなければならない作業の特性上、SQLチューニングだけでパフォーマンスを改善する余地は多くないだけでなく、改善をしても劇的な効果を期待することは難しいのです。

 さらに、必然的に発生するredoとundoデータによる追加の負荷は、パフォーマンスを改善する必要があるという立場では、Oracleの合理的な同時性保証メカニズムがむしろ恥ずかしく感じられるかもしれません。

したがって、今回のトピックは、大量のDMLを処理する際に、パーティションテーブルの構成・変更やDirect Path Insertを誘導する方法などを通じてDMLワーカー自体の性能向上はもちろん、システムのDown Time最小化にも寄与できる方法 についてのお話します。

大量のDelete

月単位または特定の期間単位のデータを定期的に削除する作業は、どのシステムでも簡単であるとみなせる作業の1つです。 おなじみの作業ですが、その作業のパフォーマンスを向上させることは容易ではありません。

大量の Delete 操作は Table Full Scan や Index Scan などの該当テーブルを Access する方法による非効率というよりは必然的に発生するしかない redo、undo データ生成とともに、そのテーブルにインデックスが存在する場合、インデックスの数に応じて追加の Overhead が発生するのが性能低下の主な理由になるからです。

インデックスで発生する追加のオーバーヘッドとは、削除対象となるデータをインデックスからも削除しなければならないと同時に、この過程も redo と undo データが生成されることを意味します。

上記のような負荷を抑えながら大量のDelete操作の実行速度を改善するにはどのような方法があるでしょうか?
 答えはPartition Table構成にあります。

定期的に削除する必要がある期間が決まっていれば、その列を Key とする Range Partition を構成し、削除対象となる期間に対応する Partition Table を Alter Table [Partition_Name] Drop Partition [Partition_Name] コマンドを通じて DML ではない DDL で作業を置き換えることができます。

DDL への Delete 操作の置き換えは、 redo データが生成されず単に該当 Partition を Drop する作業だけ行うため、速度の差は比較できないほど大きく、大量の redo/undo データが発生しないので Down Time 最小化にも 大きな貢献ができます。

以下のテストでその違いについて詳しく調べてみましょう。

テーブル生成DDL(Partition_test)

CREATE TABLE partition_test (TIME_ID DATE , DAY_NAME VARCHAR2(9), DAY_NUMBER_IN_WEEK NUMBER(1,0) , DAY_NUMBER_IN_MONTH NUMBER(2,0) , CALENDAR_WEEK_NUMBER NUMBER(2,0) , FISCAL_WEEK_NUMBER NUMBER(2,0) , WEEK_ENDING_DAY DATE , WEEK_ENDING_DAY_ID NUMBER , CALENDAR_MONTH_NUMBER NUMBER(2,0) , FISCAL_MONTH_NUMBER NUMBER(2,0) , CALENDAR_MONTH_DESC VARCHAR2(8) , CALENDAR_MONTH_ID NUMBER , FISCAL_MONTH_DESC VARCHAR2(8) , FISCAL_MONTH_ID NUMBER, DAYS_IN_CAL_MONTH NUMBER , DAYS_IN_FIS_MONTH NUMBER , END_OF_CAL_MONTH DATE , END_OF_FIS_MONTH DATE , CALENDAR_MONTH_NAME VARCHAR2(9) , FISCAL_MONTH_NAME VARCHAR2(9) , CALENDAR_QUARTER_DESC CHAR(7) , CALENDAR_QUARTER_ID NUMBER , FISCAL_QUARTER_DESC CHAR(7) ,

FISCAL_QUARTER_ID NUMBER , DAYS_IN_CAL_QUARTER NUMBER , DAYS_IN_FIS_QUARTER NUMBER , END_OF_CAL_QUARTER DATE , END_OF_FIS_QUARTER DATE , CALENDAR_QUARTER_NUMBER NUMBER(1,0) , FISCAL_QUARTER_NUMBER NUMBER(1,0) , CALENDAR_YEAR NUMBER(4,0) , CALENDAR_YEAR_ID NUMBER ,  FISCAL_YEAR NUMBER(4,0) , FISCAL_YEAR_ID NUMBER , DAYS_IN_CAL_YEAR NUMBER , DAYS_IN_FIS_YEAR NUMBER , END_OF_CAL_YEAR DATE , END_OF_FIS_YEAR DATE )

PARTITION BY RANGE( time_id )(

PARTITION partition_test1 VALUES less than (to_date(‘1998-12-31′,’YYYY-MM-DD’)) , PARTITION partition_test2 VALUES less than (to_date(‘1999-12-31′,’YYYY-MM-DD’)) , PARTITION partition_test3 VALUES less than (to_date(‘2000-12-31′,’YYYY-MM-DD’)) , PARTITION partition_test4 VALUES less than (to_date(‘2001-12-31′,’YYYY-MM-DD’)) , PARTITION partition_test5 VALUES less than (to_date(‘2003-12-31′,’YYYY-MM-DD’)) );

テーブルの作成 DDL (Partition_test2)

CREATE TABLE partition_test2 (TIME_ID DATE , DAY_NAME VARCHAR2(9), DAY_NUMBER_IN_WEEK NUMBER(1,0) , DAY_NUMBER_IN_MONTH NUMBER(2,0) , CALENDAR_WEEK_NUMBER NUMBER(2,0) , FISCAL_WEEK_NUMBER NUMBER(2,0) , WEEK_ENDING_DAY DATE , WEEK_ENDING_DAY_ID NUMBER , CALENDAR_MONTH_NUMBER NUMBER(2,0) , FISCAL_MONTH_NUMBER NUMBER(2,0) , CALENDAR_MONTH_DESC VARCHAR2(8) , CALENDAR_MONTH_ID NUMBER , FISCAL_MONTH_DESC VARCHAR2(8) , FISCAL_MONTH_ID NUMBER, DAYS_IN_CAL_MONTH NUMBER , DAYS_IN_FIS_MONTH NUMBER , END_OF_CAL_MONTH DATE ,

END_OF_FIS_MONTH DATE , CALENDAR_MONTH_NAME VARCHAR2(9) , FISCAL_MONTH_NAME VARCHAR2(9) , CALENDAR_QUARTER_DESC CHAR(7) , CALENDAR_QUARTER_ID NUMBER , FISCAL_QUARTER_DESC CHAR(7) , FISCAL_QUARTER_ID NUMBER , DAYS_IN_CAL_QUARTER NUMBER , DAYS_IN_FIS_QUARTER NUMBER , END_OF_CAL_QUARTER DATE , END_OF_FIS_QUARTER DATE , CALENDAR_QUARTER_NUMBER NUMBER(1,0) , FISCAL_QUARTER_NUMBER NUMBER(1,0) , CALENDAR_YEAR NUMBER(4,0) , CALENDAR_YEAR_ID NUMBER ,  FISCAL_YEAR NUMBER(4,0) , FISCAL_YEAR_ID NUMBER , DAYS_IN_CAL_YEAR NUMBER , DAYS_IN_FIS_YEAR NUMBER , END_OF_CAL_YEAR DATE , END_OF_FIS_YEAR DATE );

データ入力(2つのテーブル同じ)

INSERT /*+ append */ INTO       partition_test SELECT a.*

FROM   sh.times a ,

(

SELECT LEVEL

FROM   dual

CONNECT BY LEVEL <= 10000

) b;

一般テーブル(Partition_test2)のデータ削除

SQL> delete partition_test2

2 where time_id<= (to_date(‘2000-12-31′,’YYYY-MM-DD’)); 10960000  行が削除されました。

Execution Plan

———————————————————-

Plan hash value: 3385385008

————————————————————————————–

| Id | Operation         | Name             | Rows | Bytes | Cost (%CPU)| Time     |

————————————————————————————–

|   0 | DELETE STATEMENT |                 | 5718 | 51462 |   141K (1)| 00:28:15 |

|   1 | DELETE          | PARTITION_TEST2   |      |      |          |         |

|* 2 |   TABLE ACCESS FULL| PARTITION_TEST2 | 5718 | 51462 |   141K (1)| 00:28:15 |

————————————————————————————–

Predicate Information (identified by operation id):

—————————————————

2 – filter(“TIME_ID”<=TO_DATE(‘ 2000-12-31 00:00:00’, ‘syyyy-mm-ddhh24:mi:ss’))

Note

—–

– dynamic sampling used for this statement (level=2)

Statistics

———————————————————-

5516 recursive calls 13289330 db block gets

521869 consistent gets

520081 physical reads

5831072716 redo size

565 bytes sent via SQL*Net to client

544 bytes received via SQL*Net from client

3 SQL*Net roundtrips to/from client

7 sorts (memory)

0 sorts (disk) 10960000 rows processed

パーティションテーブル(Partition_test)のデータ削除

SQL> alter table partition_test drop partition partition_test1;

テーブルが変更されました。

SQL> alter table partition_test drop partition partition_test2;

テーブルが変更されました。

SQL> alter table partition_test drop partition partition_test3;

テーブルが変更されました。

大量のINSERT

大量のデータをInsertする作業は、上で紹介したDelete作業に比べて性能改善の余地が多くあります。 これは、データ入力対象テーブルの属性変更(Nologging)とヒント(/*+ append */)だけでDirect Path Insertを誘導してREDOの発生を抑制する方法があります。

しかし、ここには別の障害があります。それはインデックスの存在です。

これは、大量のインサート操作時にデータがテーブルに入力されるタスクよりも、インデックスの数やインデックスの性格によって性能が左右されるとしても無理もないことです。

Direct Path Insert が可能な環境で、2 つのケースに対する大量の Insert タスクのパフォーマンスの違いと、これを改善する方法について学びます。

 インデックスのによるDirect Path Insertのパフォマンスの違い

Insert 対象となるテーブルにインデックスが多数生成されている場合、それだけの redo 発生量が付随します。

なので自然にInsert作業がうと思います。

以下のテストで、インデックスの数に基づいてDirect Path Insert操作の速度にどの程度影響するかを確認します。

テーブル生成 DDL 省略(上記大量の Delete のPartition_test テーブル DDL 参照)

3つのインデックス存在する場合の大量の Insert SQL> alter table partition_testnologging;

テーブルが変更されました。 SQL> INSERT /*+ append */

  • INTO   partition_test
  • SELECT a.*
  • FROM   sh.times a , 5 (
  • SELECT LEVEL
  • FROM   dual
  • CONNECT BY LEVEL <= 10000

9        ) b;

18260000 行が作成されました.

経過: 00:08:50.77

Statistics

———————————————————-

37399 recursive calls 4600992 db block gets 323059 consistent gets

493284 physical reads

5177833936 redo size

564 bytes sent via SQL*Net to client

636 bytes received via SQL*Net from client

3 SQL*Net roundtrips to/from client

3 sorts (memory)

3 sorts (disk) 18260000 rows processed

既存のインデックスを削除し、Insert 以降のインデックスを作成する

SQL> truncate table partition_test;

テーブルが切り取られました.

経過: 00:00:00.74

SQL> drop index idx_test1;

インデックスが削除されました。

 経過: 00:00:00.09

SQL> drop index idx_test2;

インデックスが削除されました。

 経過: 00:00:00.03

SQL> drop index idx_test3;

インデックスが削除されました.

 経過: 00:00:00.03

SQL> INSERT /*+ append */

  • INTO   partition_test
    • SELECT a.*
    • FROM   sh.times a , 5 (
  • SELECT LEVEL
  • FROM   dual
  • CONNECT BY LEVEL <= 10000

9        ) b;

18260000 行が作成されました。

経過: 00:00:24.49

Statistics

———————————————————-

13820 recursive calls 532991 db block gets

4210 consistent gets

7 physical reads

1975520 redo size

566 bytes sent via SQL*Net to client

636 bytes received via SQL*Net from client

3 SQL*Net roundtrips to/from client

20 sorts (memory)

0 sorts (disk) 18260000 rows processed

SQL> create index idx_test1 on partition_test(time_id, day_name) local;

インデックスが作成されました.

経過: 00:00:26.87

SQL> create index idx_test2 on partition_test(DAY_NUMBER_IN_WEEK,DAY_NUMBER_IN_MONTH) local;

インデックスが作成されました。

経過: 00:00:27.42

SQL> create index idx_test3 on partition_test(WEEK_ENDING_DAY,WEEK_ENDING_DAY_ID)

local;

インデックスが作成されました

経過: 00:00:26.72

上記のテスト結果のように大量のデータが入力されると、そのテーブルのインデックスがどれだけ存在するかによってパフォーマンスに大きな影響を与えることがわかります。

 これは単に大量のデータ入力時にインデックスの存在によるパフォーマンスの違いを示すテストなので、大量のデータが入力されるすべてのタスクにインデックスをドロップしたりUnusable状態に変更して作業するという意味ではないです。

ただし、オンラインユーザーがいない時間帯の配置作業であれば十分考慮できる方法だと考えます。

 実行速度はもちろんのことと、redo発生の最小化にも大きな貢献ができるメリットがあるからです。

 頻繁なIndex SplitによるDirect Path Insertのパフォマンスの違い

常に増加する値を挿入する必要がある性格の列が存在する可能性があります。

 代表的な例は、SYSDATEが入力される状況です。

この列にIndexが生成されていると仮定し、大量のInsert操作が行われると、9:1 Index Splitが頻繁に発生する現象が自然に現れるはずです。

 前述のように、Index Splitは決して軽いものではありません。 なぜ軽くないのか、その過程を見てみましょう。

まず、9:1 SplitとはMax値が入力される状況で右下のIndex Leaf Blockにもはや保存するスペースがなくなった場合、Free ListのFree Blockを取得し、既存の最下位Index Leaf Blockの最小限のデータを新しいFree Blockに 取り込み、新しく保存されるデータを該当ブロックに入力する過程をいいいます。

これはデータが増加する過程で必然的にIndex Sizeが増加し、Index Splitはこの過程で発生する自然な現象です。

しかし、Index Splitプロセスは無料ではないのです

Index Splitの全プロセスは、redoが発生し、Splitが開始して完了するまでLockingを通じてこの作業を保護します。 この Locking は、Enq : TX – Index Contention という Wait Event で Split が完了する時点まで Split プロセスを保護することになります。

したがって、常に増加する性格のカラムにインデックスが生成されており、当該テーブルに大量のデータが入力される場合、Index SplitによるWait Time増加とともに、redo発生などでInsert速度の低下は必然的に発生します。

では、これを改善できる方案があるのでしょうか?

 結論からお話しますと、問題となるIndexに他のカラムを追加して再生成する方法が存在します。

この方法の目的は、大量のデータが入力されると、既存の右下のIndex Leaf Blockに競合が集中していたことが結合インデックスによってその競合がある程度分散されるためです。

 新たに結合するIndex構成対象カラムのNDV(Number of Distinct Value)値が大きいほど、Index Split減少の効果はさらに大きくなります。

ただし、この部分で注意することは、Index Clustering Factor が大幅に減少する可能性があるため、NDV 値の大きい列を優先順位で構成する必要はないことです.

Array Processing

大量のDML操作は、データをLoadするかUpdate(Delete)するときに、一般的にLOOP文を使用して、ガンガンが処理することが多いです。

 この方法は、Loopごとに1回ずつDML操作が行われ、その分だけDBMS CALLが発生するため、性能上不利なのです。

しかし、Array Processingを利用したBulk SQLを使用すれば、Loopなしでたった1回のSQLを実行するだけで処理が可能です。 つまり、大量のDMLを1回(Limit予約語を使用して一度に処理できる件数を制限しない場合)に処理できるため、DBMS CALLを減らしてLoopで処理された方法に比べて大きなパフォーマンス改善効果を見ることができます。

以下のテストでその性能差を比較してみましょう。

Array Processing を活用して   Insert する Procedure Source – P_BULK_INSERT_TEST1

CREATE OR REPLACE PROCEDURE P_BULK_INSERT_TEST1 IS

CURSOR sales_cur IS

SELECT PROD_ID,

CUST_ID, TIME_ID, CHANNEL_ID, PROMO_ID, QUANTITY_SOLD, AMOUNT_SOLD

FROM    SH.SALES;

TYPE SALES_TBL_TYPE IS TABLE OF sales_cur%ROWTYPE INDEX BY BINARY_INTEGER; SALES_TBL SALES_TBL_TYPE;

BEGIN

OPEN sales_cur; LOOP

FETCH sales_cur BULK COLLECT INTO sales_tbl LIMIT 1000; — PGA 消費を小さく保つ

1000件ずつ処理

FOR i IN sales_tbl.FIRST..sales_tbl.LAST LOOP –各種計算はここから

sales_tbl(i).AMOUNT_SOLD := sales_tbl(i).AMOUNT_SOLD * 1.5; END LOOP;

— FETCHされた1000件をSQL 1回実行として扱う

FORALL i IN sales_tbl.FIRST..sales_tbl.LAST

INSERT INTO SALES2 VALUES (sales_tbl(i).PROD_ID,

sales_tbl(i).CUST_ID, sales_tbl(i).TIME_ID, sales_tbl(i).CHANNEL_ID, sales_tbl(i).PROMO_ID, sales_tbl(i).QUANTITY_SOLD, sales_tbl(i).AMOUNT_SOLD

);

EXIT WHEN sales_cur%NOTFOUND; END LOOP;

CLOSE sales_cur; COMMIT;

EXCEPTION

WHEN OTHERS THEN ROLLBACK;

RAISE_APPLICATION_ERROR(-20001,’RAISE_APPLICATION_ERROR : ‘

||SQLCODE||’:’|| SUBSTR(SQLERRM,1,200)); END;

/

ガンガン Loop を通じて Insert する Procedure Source – P_BULK_INSERT_TEST2

 CREATE OR REPLACE PROCEDURE P_BULK_INSERT_TEST2 IS

CURSOR sales_cur IS

SELECT PROD_ID,

CUST_ID, TIME_ID, CHANNEL_ID, PROMO_ID, QUANTITY_SOLD, AMOUNT_SOLD

FROM    SH.SALES;

TYPE SALES_TBL_TYPE IS TABLE OF sales_cur%ROWTYPE INDEX BY BINARY_INTEGER; SALES_TBL SALES_TBL_TYPE;

BEGIN

OPEN sales_cur; LOOP

FETCH sales_cur BULK COLLECT INTO sales_tbl LIMIT 1000; – PGA消費を小さく保つ

1000件ずつ処理

FOR i IN sales_tbl.FIRST..sales_tbl.LAST LOOP –各種計算はここから

sales_tbl(i).AMOUNT_SOLD := sales_tbl(i).AMOUNT_SOLD * 1.5; END LOOP;

— LOOPの使い方 INSERT SQL 1000回実行

FOR i IN sales_tbl.FIRST..sales_tbl.LAST LOOP – FORALL予約語の除外

INSERT INTO SALES2 VALUES (sales_tbl(i).PROD_ID,

sales_tbl(i).CUST_ID, sales_tbl(i).TIME_ID, sales_tbl(i).CHANNEL_ID, sales_tbl(i).PROMO_ID, sales_tbl(i).QUANTITY_SOLD, sales_tbl(i).AMOUNT_SOLD

);

END LOOP;

EXIT WHEN sales_cur%NOTFOUND; END LOOP;

CLOSE sales_cur; COMMIT;

EXCEPTION

WHENOTHERS THEN ROLLBACK;

RAISE_APPLICATION_ERROR(-20001,’RAISE_APPLICATION_ERROR : ‘

||SQLCODE||’:’|| SUBSTR(SQLERRM,1,200)); END;

/

P_BULK_INSERT_TEST2 Vs. P_BULK_INSERT_TEST2

SQL> exec P_BULK_INSERT_TEST1;

PL/SQL処理が正常に完了しました。

経過: 00:00:02.20

SQL> SQL>

SQL> exec P_BULK_INSERT_TEST2;

PL/SQL 処理が正常に完了しました.

経過: 00:00:35.08

注 : Array Processing を利用した Update Procedure Source CREATE OR REPLACE PROCEDURE P_BULK_INSERT_TEST2 IS

CURSOR sales_cur IS

SELECT PROD_ID,

CUST_ID, TIME_ID, CHANNEL_ID, PROMO_ID, QUANTITY_SOLD, AMOUNT_SOLD

FROM    SH.SALES;

TYPE SALES_TBL_TYPE IS TABLE OF sales_cur%ROWTYPE INDEX BY BINARY_INTEGER; SALES_TBL SALES_TBL_TYPE;

BEGIN

OPEN sales_cur; LOOP

FETCH sales_cur BULK COLLECT INTO sales_tbl LIMIT 1000; – PGA消費を小さく保つ

1000件ずつ処理

FOR i IN sales_tbl.FIRST..sales_tbl.LAST LOOP –各種計算はここから

sales_tbl(i).AMOUNT_SOLD := sales_tbl(i).AMOUNT_SOLD * 1.5; END LOOP;

— UPDATE/DELETEも可能

FORALL i IN sales_tbl.FIRST..sales_tbl.LAST

UPDATE SALES SET AMOUNT_SOLD = sales_tbl(i).AMOUNT_SOLD

WHERE PROD_ID = sales_tbl(i).PROD_ID AND CUST_ID = sales_tbl(i).CUST_ID AND TIME_ID = sales_tbl(i).TIME_ID

AND CHANNEL_ID = sales_tbl(i).CHANNEL_ID AND PROMO_ID = sales_tbl(i).PROMO_ID;

EXIT WHEN sales_cur%NOTFOUND; END LOOP;

CLOSE sales_cur; COMMIT;

EXCEPTION

WHENOTHERS THEN ROLLBACK;

RAISE_APPLICATION_ERROR(-20001,’RAISE_APPLICATION_ERROR : ‘

||SQLCODE||’:’|| SUBSTR(SQLERRM,1,200)); END;

/

別の難

先に紹介した方法の適用が可能であれば、大量のDML作業に対する性能改善 Process を行うことができます。

 しかし、もう一つの克服すべき難関がまだ待っています。

これは、Partition Tableへの変更が必要な部分に相当するもので、膨大な量のデータを格納している既存テーブルをPartition Tableに変更する作業が不十分であるということです。

一定時間オフラインが可能だとすれば変更作業を試してみることもできるのですが、常にサービスを続けなければならない環境でPartition Tableに変更しなければならないと勧告すれば、原論的には正しいといえるのですが、実務を担当しているDBAには大きな当惑感を抱かせる結果を生み出すことになります。

しかし、この難しく見える問題も克服できる方法があります。 これはPartition Exchange(以下、PE)機能を利用するものです。 PE機能は、文字通り通常のテーブルをPartition Tableに変更できることを意味します。 さらに、追加のスペースも必要なく、かなり効率的な方法でPartition Tableへの移行を達成することができます。

以下のテストは、PE機能を活用して一般テーブルをPartition Tableに簡単に切り替えることができることを証明しています。

従来使用していた一般表 : Partition_test2

: Delete テストのテーブル生成スクリプトとデータ入力スクリプトのリファレンスPartition Tableに切り替えるPartition Tableを作成する

CREATE TABLE partition_test (TIME_ID DATE , DAY_NAME VARCHAR2(9), DAY_NUMBER_IN_WEEK NUMBER(1,0) , DAY_NUMBER_IN_MONTH NUMBER(2,0) , CALENDAR_WEEK_NUMBER NUMBER(2,0) , FISCAL_WEEK_NUMBER NUMBER(2,0) , WEEK_ENDING_DAY DATE , WEEK_ENDING_DAY_ID NUMBER , CALENDAR_MONTH_NUMBER NUMBER(2,0) , FISCAL_MONTH_NUMBER NUMBER(2,0) , CALENDAR_MONTH_DESC VARCHAR2(8) ,

CALENDAR_MONTH_ID NUMBER , FISCAL_MONTH_DESC VARCHAR2(8) , FISCAL_MONTH_ID NUMBER, DAYS_IN_CAL_MONTH NUMBER , DAYS_IN_FIS_MONTH NUMBER , END_OF_CAL_MONTH DATE , END_OF_FIS_MONTH DATE , CALENDAR_MONTH_NAME VARCHAR2(9) , FISCAL_MONTH_NAME VARCHAR2(9) , CALENDAR_QUARTER_DESC CHAR(7) , CALENDAR_QUARTER_ID NUMBER , FISCAL_QUARTER_DESC CHAR(7) , FISCAL_QUARTER_ID NUMBER , DAYS_IN_CAL_QUARTER NUMBER , DAYS_IN_FIS_QUARTER NUMBER , END_OF_CAL_QUARTER DATE , END_OF_FIS_QUARTER DATE , CALENDAR_QUARTER_NUMBER NUMBER(1,0) , FISCAL_QUARTER_NUMBER NUMBER(1,0) , CALENDAR_YEAR NUMBER(4,0) , CALENDAR_YEAR_ID NUMBER ,  FISCAL_YEAR NUMBER(4,0) , FISCAL_YEAR_ID NUMBER , DAYS_IN_CAL_YEAR NUMBER , DAYS_IN_FIS_YEAR NUMBER , END_OF_CAL_YEAR DATE , END_OF_FIS_YEAR DATE )

PARTITION BY RANGE( time_id )(

PARTITION part1 VALUES less than (to_date(‘2002-12-31′,’YYYY-MM-DD’)) ); –

Partiton Table に切り替えるテーブルの Max 値を入力

Partition Exchangeの実行

SQL> ALTER TABLE partition_test exchange partition part1 WITH TABLE partition_test2 without validation ;

テーブルが変更されました。経過:00:00:00.03

既存テーブルの削除後にRename

110│2013 기술백서 White Paper

SQL> DROP TABLE partition_test2;

テーブルが削除されました。

経過:00:00:00.06

SQL> RENAME partition_test to partition_test2;

テーブル名が変更されました。

経過:00:00:00.03

上記のようにすると、既存のテーブルは単にPartition Tableに変更できることがわかります。 ただし、注意すべき点は、既存の一般テーブルで持っていた制約条件(Primary Key)やIndex構成はすべて同一に生成することで正常なPEの実行が可能となることです。

結論

先に紹介した大量のDML作業の性能を改善する方法は、テーブルの構造自体を変更や、発生可能なredoの量を最大限減少させる方法、そしてIndexの構造変更やDropなどでまとめることができます。 しかし、このような方法は、ややもすると実務を担当するDBAにとってはただの雲をつかむだけの話に過ぎないかも知れません。

 頭では十分理解して共感できる良い方法を提示したとしても、現実に適用できないのであれば、その空虚さはさらに大きな重さになることでしょう。

しかし、上で紹介した内容は実質的に適用でき、また適用した事例があります。 もちろん、業務の性格によっては、数学の公式のように常に同じ方法もより適用できるわけではないですが、その環境に合ったソリューションは常に存在すると信じています。

非現実的だと思ってしまう瞬間、性能改善の余地は遠のいていきます。  

上記の内容の核心をきちんと整理していれば、該当する状況について様々な性能改善アイデアを提示でき、その中で最も簡単に適用できる方法を協議を通じて探すことができることでしょう。

大量のDML作業性能改善とシステムのDownTime最小化という二兎を得ることは難しくないのです。


PHP Code Snippets Powered By : XYZScripts.com