5

LUW に DB2 9.7 FP5 を使用しています。250 万行のテーブルがあり、約 100 万行を削除したいのですが、この削除操作はテーブル全体に分散されています。5 つの削除ステートメントでデータを削除しています。

delete from tablename where tableky between range1 and range2
delete from tablename where tableky between range3 and range4
delete from tablename where tableky between range5 and range5
delete from tablename where tableky between range7 and range8
delete from tablename where tableky between range9 and range10

これを行っている間、最初の 3 回の削除は正常に機能しますが、4 回目は失敗し、DB2 がハングして何もしません。以下は私が従ったプロセスです、これについて私を助けてください:

1. Set following profile registry parameters: DB2_SKIPINSERTED,DB2_USE_ALTERNATE_PAGE_CLEANING,DB2_EVALUNCOMMITTED,DB2_SKIPDELETED,DB2_PARALLEL_IO

2.Alter bufferpools for automatic storage.

3. Turn off logging for tables (alter table tabname activate not logged initially) and delete records

4. Execute the script with +c to make sure logging is off

このような大量のデータを削除するためのベスト プラクティスは何ですか? 同じテーブルから同じ性質のデータを削除しているときに失敗するのはなぜですか?

4

6 に答える 6

8

これは常にトリッキーな作業です。トランザクションのサイズ (安全なロールバックなど) は、トランザクション ログのサイズによって制限されます。トランザクション ログは、あなたの sql コマンドだけでなく、同じ瞬間に db を使用している他のユーザーのコマンドによっても埋められます。

次の方法のいずれかまたは組み合わせを使用することをお勧めします

1.コミット

頻繁にコミットします - あなたの場合、各削除コマンドの後に1つのコミットを置きます

2. トランザクション ログのサイズを増やす

私が思い出したように、デフォルトの db2 トランザクション ログはそれほど大きくありません。トランザクション ログのサイズは、データベースごとに個別に計算/調整する必要があります。参照はこちら、詳細はこちら

3. ストアド プロシージャ

ブロック単位で削除を行うストアド プロシージャを記述して呼び出します。たとえば、次のようになります。

-- USAGE - create: db2 -td@ -vf del_blocks.sql
-- USAGE - call: db2 "call DEL_BLOCKS(4, ?)"

drop PROCEDURE DEL_BLOCKS@

CREATE PROCEDURE DEL_BLOCKS(IN PK_FROM INTEGER, IN PK_TO INTEGER)
LANGUAGE SQL
BEGIN
    declare v_CNT_BLOCK     bigint;

    set v_CNT_BLOCK   = 0;

    FOR r_cur as c_cur cursor with hold for
        select tableky from tablename 
        where tableky between pk_from and pk_to
        for read only
    DO
            delete from tablename where tableky=r_cur.tableky;

            set v_CNT_BLOCK=v_CNT_BLOCK+1;

            if v_CNT_BLOCK >= 5000 then
                set v_CNT_BLOCK = 0;
                commit;
            end if;
    END FOR;

    commit;
END@

4. 置換オプションを使用したエクスポート + インポート

非常に大きなテーブルを削除する必要がある場合や、少量のレコードのみを残す必要がある場合 (および FK 制約がない場合) は、エクスポート + インポート (置換) を使用しました。インポートの置換オプションは非常に破壊的です。新しいレコードのインポートが開始される前にテーブル全体が削除されます( db2 import コマンドの参照)。このような機密性の高い操作のために、バックアップ、エクスポート、インポートの 3 つのスクリプトを作成し、それぞれを個別に実行します。エクスポート用のスクリプトは次のとおりです。

echo '===================== export started '; 
values current time;

export to tablename.del of del  
select *  from tablename where (tableky between 1 and 1000 
    or tableky between 2000 and 3000 
    or tableky between 5000 and 7000 
    ) ; 
echo '===================== export finished ';  
values current time;

インポートスクリプトは次のとおりです。

echo '===================== import started ';  
values current time;

import from tablename.del of del  allow write access commitcount 2000
-- !!!! this is IMPORTANT and VERY VERY destructive option  
replace  
into tablename ;

echo '===================== import finished ';

5.切り捨てコマンド

バージョン 9.7 の Db2 では、TRUNCATE ステートメントが導入されました。

テーブルからすべての行を削除します。

基本的:

TRUNCATE TABLE <tablename> IMMEDIATE

私は db2 で TRUNCATE を使用した経験がありませんでしたが、他のいくつかのエンジンでは、コマンドは非常に高速であり、トランザクション ログを使用しません (少なくとも通常の方法では使用されません)。詳細はこちらまたは公式ドキュメントでご確認ください。解決策 4 として、この方法も非常に破壊的です。テーブル全体を消去するため、コマンドを発行する前に十分に注意してください。テーブル/データベースのバックアップを最初に実行して、以前の状態を確認します。

これをいつ行うかについての注意

データベースに他のユーザーがいない場合、またはテーブルをロックしてこれを確認します。

ロールバックに関する注意

トランザクション db (db2 など) では、ロールバックにより、トランザクションが開始されたときの状態に db の状態を復元できます。方法 1、3、および 4 ではこれを達成できないため、「元の状態に復元する」機能が必要な場合、これを保証する唯一のオプションは方法番号です。2 - トランザクション ログを増やします

于 2013-05-10T09:24:19.923 に答える
1
delete from ordpos where orderid in ((select orderid from ordpos where orderid not in (select id from ordhdr) fetch first 40000 rows only));

これでクエリが解決されることを願っています:)

于 2017-01-06T14:04:19.590 に答える
0

何百万行もあるデータを削除するベスト プラクティスは、削除の間に commit を使用することです。あなたの場合、すべての削除ステートメントの後に commit を使用できます。

コミットが行うことは、トランザクション ログをクリアし、他の削除操作を実行するためのスペースを確保することです。

または、5 つの削除ステートメントの代わりにループを使用し、delete ステートメントを delete に渡します。ループを 1 回繰り返した後、1 つのコミットを実行すると、データベースがハングすることはなく、同時にデータが削除されます。

このようなものを使用してください。

while(count<no of records)
delete from (select * from table fetch fist 50000 records only)
commit;
count= total records- no of records.
于 2013-05-09T05:41:51.000 に答える