web-dev-qa-db-ja.com

データベースの複製と整合性チェック

私が持っている2つのデータベース(Oracle 10g)に関する質問がありました。それらをAとBと呼びましょう。Aは(さまざまなテーブルに)いくつかの情報を持っています。 Aの変更とBの「同期」。

Aに変更を加えることができない(選択するだけで、トリガーがない)ことを知って、いくつかの方法、手法、または多分考えについてお聞きしたいと思います。

あらかじめご理解とご協力をお願い申し上げます。

追加情報

回答のおかげで、それが関連するかどうかはわかりませんが、「サブテーブル」(選択)で機能するかどうかはわかりませんが、MINUS演算子を見つけました。

5
Marco Aviles

"constantly check ... sync"および "Aに変更を加えることができない"という要件のため、オプションはかなり制限されています。マテリアライズドビューログ、dbms_alert、ストリーム、スタンバイデータベースなどはすべてテーブルから外れています。

Aのテーブルが常にすべての行を更新している場合(ジャックダグラスが言ったように)、マテリアライズドビューはセットアップが最も簡単です。ほとんどのレコードがAで時々変化しないというより可能性の高いイベントでは、Aから選択してBで必要に応じてマージおよび削除するパッケージをBでセットアップする必要があります。これにより、それが実行される頻度と同じくらい最新のものであるが、あなたの要件を考えると、それはあなたができる最善であるかもしれません。

具体的には、パッケージは次のことを行う必要があります。

  • Aに存在しないB行から削除します。
  • Merge AからBに一致すると更新され、一致しない場合は挿入されます。

Aのテーブルを複数回ヒットしないようにする場合は、テーブル全体をBのグローバル一時テーブルに挿入し、そこから削除/マージを実行できます。

マイナスについて:マイナス は、BにないAのクエリからのすべての行を示すことができます。 AのBマイナスクエリを使用すると、異なる行をすべて取得できますが、挿入/更新部分を追加する前であっても、処理に時間がかかる可能性があります。 Aが更新または削除を取得しない場合、最初のマイナスの結果を挿入できますが、insert into B...where not exists A...の方が高速でシンプルです。

5
Leigh Riffel

Aのテーブルが変更されたことを知るには、ポーリング以外の方法はありません。マテリアライズドビューを定期的に更新してdblinkで機能するようにすることもできますが、完全な更新しかできないため、テーブルが小さい場合にのみ実用的です。

難しいのは、db_AでSELECT以外のアクセス権がないことです。だからここに考えがありますが、それは満たすことができる(または満たされない)いくつかのかなり厳密な仮定が必要です:

要件:

  • 同期されるすべてのテーブルには、次のいずれかが含まれます。
    • タイムスタンプ(解像度が高いほど良い)
    • 一意の順次ID
  • すべてのテーブル行は、一度同期されると変更されません。
    • または、変更が発生し、レコードのタイムスタンプが更新された場合、その方法で修正できる可能性があります。

ここで、db_Bで:

CREATE TABLE table1...
CREATE TABLE table2...,
etc.

PROCEDURE SYNC_TABLE1 IS
    MAX_ACTIVITY_DATE DATE;
    MAX_SEQUENCE_NO NUMBER;

    BEGIN
        SELECT MAX(SEQUENCE_NO), MAX(ACTIVITY_DATE) 
          INTO MAX_SEQUENCE_NO, MAX_ACTIVITY_DATE
          FROM table1;
    EXCEPTION WHEN NO_DATA_FOUND THEN
        MAX_SEQUENCE_NO := 0;
        MAX_ACTIVITY_DATE = TO_DATE('01/01/1980', 'MM/DD/YYYY');
    END;

    -- Bring over recent entries from db_A.table1 to db_B.table1

    INSERT INTO table1
    SELECT *
      FROM table1@db_A
     WHERE
           -- if using timestamps as your criteria:
           activity_date > MAX_ACTIVITY_DATE
           -- if using sequence nos as your criteria:
           sequence_no > MAX_SEQUENCE_NO
    ;

    -- consider adding limiters to decrease the bandwidth necessary
    -- for large transactions. For example activity_date < MAX_ACTIVITY_DATE + 30
    -- would load a month's worth of transactions at a time. sequence_no <
    -- MAX_SEQUENCE_NO + 500 would load 500 transactions at a time.

    COMMIT;
EXCEPTION WHEN OTHERS THEN
    ROLLBACK;
    -- Consider logging the error!
    RAISE;
END;
(lather, rinse, repeat.)

繰り返しますが、これは、連続する一意のID ORが常にdb_Aで更新されるアクティビティ日付である場合にのみ機能します(その日付は、以前のものなので、タイムスタンプが最適です。)

Oracleインスタンス間(およびOracle以外のインスタンス(OracleからmySqlなど))間でデータを同期する方法は、同期可能なすべてのテーブルにsync_date列があることを確認することです。データの同期が要求されると、sync_date列に同期の日付が入力されます。したがって、実際の同期プロセスは単純です。

FOR r in ( SELECT * FROM table1
            WHERE sync_date IS NULL ) LOOP
    send_sync_data_somewhere;
    UPDATE table1
       SET sync_date = current_timestamp
     WHERE rowid=r.rowid;
END LOOP;

通常、リミッターが有効になりますが、あなたはそれを理解します。さらに、レコードのデータが変更された場合、sync_date列はNULLになり、その時点で同期プロセスは再びそれを取得します。

注:行が同期された後でデータの変更をサポートできる場合は、状況に関係なく、何らかの重複除外処理が必要になります。 UPDATE ... WHERE EXISTSと組み合わせたSELECT句でMERGEまたはWHERE NOT EXISTSを指定したINSERTを試すことができます。

うまくいけば、それが役立ちます。

3
Kerri Shotts