はじめに
データレプリケーション製品のOracle GoldenGateでは、アクティブ・アクティブ型の双方向レプリケーションがサポートされています。本記事にて機能概要を紹介します。
Oracle GoldenGate双方向構成について
双方向構成の環境では、同一データセットのデータベースが存在し、業務アプリケーションは、任意のデータベースを更新する事が可能です。ソースDBにて更新されたトランザクションデータは、Oracle GoldenGateによって、ターゲットDBにレプリケーションされます。結果として各システムのデータベースデータは同一の内容になります。
上記双方向構成においては、Primary SystemからSecondary Systemへのレプリケーション方向(上記図の青色)では、Primary SystemがソースDB、Secondary SystemがターゲットDBとなります。下記の流れでOracle GoldenGateによるレプリケーションが行われます。
- Primary ExtractプロセスがソースDBから更新データを抽出し、LocalTrailファイルへ出力する。
- DataPumpプロセスがLocalTrailファイルを読み取り、Secondary SystemのCollectorプロセスへ送信する。
- Collectorプロセスが、RemoteTrailファイルへ出力する。
- Replicatプロセスが、RemoteTrailファイルを読み取り、ターゲットDBを更新する。
逆にSecondary SystemからPrimary Systemへのレプリケーション方向(上記図の黄色)では、Secondary SystemがソースDB、Primary SystemがターゲットDBとなります。
業務アプリケーション等によるソースDBへの更新処理とOracle GoldenGateによるターゲットDBへの更新処理は非同期で行われるため、業務アプリケーションの処理時間影響は少ないですが、非同期で処理されるが故に、双方向構成で各データベースの同一データが、ほぼ同時に更新されてしまうと、データ競合の問題が発生します。競合の具体的な例を下記に示します。
時刻 | Primary System | Secondary System |
---|---|---|
10:00:00 | UPDATE fish SET name = 'サバ' WHERE id = 1; COMMIT; |
|
10:00:01 | UPDATE fish SET name = 'アジ' WHERE id = 1; COMMIT; |
|
10:00:05 | GoldenGateが、上記更新データ(サバ)をSecondary Systemへレプリケーションする。 | |
10:00:06 | GoldenGateが、上記更新データ(アジ)をPrimary Systemへレプリケーションする。 | |
10:00:07 | Primary Systemのデータが、サバからアジに更新される。 SELECT name FROM fish WHERE id = 1; NAME ------- アジ |
Secondary Systemのデータが、アジからサバに更新される。 SELECT name FROM fish WHERE id = 1; NAME ---------- サバ |
fishテーブルのid=1のデータが、ほぼ同時に更新された結果、競合が発生し、各データベースのデータは異なる状態となってしまいました。双方向構成の環境を運用する場合は、競合を解消するための仕組みを検討する必要があります。Oracle GoldenGateでは、競合を解消するための機能が、下記二つの方式にて提供されています。
- Conflict Detection and Resolution(以降CDRと表記) Oracle GoldenGate 11g 11.2以降から使用可能
- Automatic Conflict Detection and Resolution(以降Auto CDRと表記) Oracle GoldenGate 12c 12.3.0.1以降から使用可能
CDRは、競合の検出と解消するためのルールを柔軟な設計が可能です。Auto CDRは、競合の検出と解消ルールが自動化されており、比較的お手軽に導入する事が可能です。本記事では、Auto CDRを取り上げて紹介します。
Auto CDRの紹介
システム要件
Auto CDRを使用するためのシステム要件については、Oracle GoldenGate製品マニュアルに記載があります。Oracle GoldenGate 19.1の場合、マニュアル「Oracle DatabaseのためのOracle GoldenGateの使用」の「 自動競合検出および解決の要件 」をご参照願います。
Auto CDR有効化による定義変更
競合が発生する可能性のあるテーブル単位に、Auto CDRを有効化する必要があります。Auto CDRを有効化するとテーブル単位に下記が追加されます。
- 対象テーブルに非表示のタイムスタンプ列が追加される(列名は、CDRTS\$ROW)。
- ツームストンテーブルが追加される(テーブル名は、DT\$_<テーブル名>)。
実際にAuto CDRを有効化したfishテーブルの定義を、sqlplusを使用して確認した結果を下記に示します。sqlplusのシステム変数COLINVISIBLEをONに設定すると、非表示列の確認が可能です。
補足: fishテーブルのキー列はID列です。
SQL> SET COLINVISIBLE ON
SQL> DESC angler.fish
名前 NULL? 型
----------------------------------------- -------- ----------------------------
ID NOT NULL NUMBER
NAME VARCHAR2(100)
CDRTS$ROW (INVISIBLE) NOT NULL TIMESTAMP(6)
また、ツースムストンテーブルが作成されている事を確認します。
SQL> DESC angler.dt$_fish;
名前 NULL? 型
----------------------------------------- -------- ----------------------------
ID NUMBER
DELTIME$$ TIMESTAMP(6)
次にfishテーブルにデータを追加した際の動作を示します。
fishテーブルのCDRTS\$ROW列に、INSERT実行時のタイムスタンプが格納されます。(UPDATE時も同様)
補足: CDRTS\$ROWに格納される時刻データは、UTCで管理されます。
SQL> INSERT INTO angler.fish (id, name) VALUES (1,'セイゴ');
1行が作成されました。
SQL> COMMIT;
コミットが完了しました。
SQL> SELECT id, name, cdrts$row FROM angler.fish;
ID NAME CDRTS$ROW
---------- ---------- ------------------------
1 セイゴ 21-10-02 05:42:53.492774
DELETE処理を実行するとツームストンテーブルに、削除したキー列値とタイムスタンプが記録されます。
実際にfishテーブルのデータをDELETEした場合の動作を示します。
補足: ツームストンテーブルのDELTIME\$\$列もUTC管理です。
SQL> DELETE FROM angler.fish WHERE id = 1;
1行が削除されました。
SQL> COMMIT;
コミットが完了しました。
SQL> SELECT * FROM angler.dt$_fish;
ID DELTIME$$
---------- ------------------------
1 21-10-02 08:28:58.396096
Auto CDRでは、上記の非表示列(CDRTS\$ROW)およびツームストンテーブルのタイムスタンプ列(DELTIME\$\$)の情報を使用して、競合の検出と解消が行われます。ちなみにツームストン(tombstone)は、「墓石」という意味だそうです。削除された情報が墓石に刻まれているイメージでしょうか。私の場合、プロレス技のツームストン・パイルドライバが思い浮かびました。
競合検出と解消方法
Auto CDRにより、競合がどのように検出されて、またどのように解消されるのかを競合タイプごとに示します。
競合タイプ: INSERT ROW EXISTS
- 競合の検出
ターゲットDBへのINSERT実行時に、同一キーのデータが存在した場合 - 競合の解消
ソースDBとターゲットDBのCDRTS\$ROW列値を比較し、新しいほうのデータが採用される
下記にINSERT ROW EXISTS競合と解消の例を示します。
時刻 | Primary System | Secondary System |
---|---|---|
10:00:00 | INSERT INTO fish (id, name) VALUES (1, 'サバ'); COMMIT; [補足] CDRTS\$ROW列が、システム時刻の10:00:00で更新される。 |
|
10:00:01 | INSERT INTO fish (id, name) VALUES (1, 'アジ'); COMMIT; [補足] CDRTS\$ROW列が、システム時刻の10:00:01で更新される。 |
|
10:00:02 | Primary SystemからのレプリケーションデータをSecondary Systemへ適用する際に下記が発生する。 [競合の検出] 同一キーのデータがSecondary Systemに存在するため、INSERT ROW EXISTS競合が発生する。 [競合の解消] Secondary SystemのCDRTS\$ROWが新しいため、Primary SystemのINSERTデータは破棄される。 |
|
10:00:03 | Secondary SystemからのレプリケーションデータをPrimary Systemへ適用する際に下記が発生する。 [競合の検出] 同一キーのデータがPrimary Systemに存在するため、INSERT ROW EXISTS競合が発生する。 [競合の解消] Secondary SystemのCDRTS\$ROWが新しいため、Primary SystemのデータをUPDATEする。 UPDATE fish SET name='アジ' WHERE id=1; |
|
10:00:04 | SELECT name FROM fish WHERE id = 1; NAME ---------- アジ |
SELECT name FROM fish WHERE id = 1; NAME ---------- アジ |
上記の通り、データの内容は一致した状態となり、データ不整合は発生しませんが、結果として、Primary Systemに登録されたデータが破棄されてしまいます。実際のシステム運用において、上記の動作が許容できるか慎重に検討する必要があります。許容できない場合は、Primary SystemとSecondary Systemのキーの値が、重複しないような設計が必要になります。
競合タイプ: UPDATE ROW EXISTS
- 競合の検出
ターゲットDBへのUPDATE実行時に、ソースDBの更新前CDRTS\$ROW列値とターゲットDBのCDRTS\$ROW列値が異なる場合 - 競合の解消
ソースDBの更新後CDRTS\$ROW列値とターゲットDBのCDRTS\$ROW列値を比較し、新しいほうのデータが採用される
下記にUPDATE ROW EXISTS競合と解消の例を示します。
時刻 | Primary System | Secondary System |
---|---|---|
10:00:00 | UPDATE fish SET name = 'サバ' WHERE id = 1; COMMIT; [補足] CDRTS\$ROW列が、システム時刻の10:00:00で更新される。(更新前のCDRTS\$ROW列は、9:50:00とする) |
|
10:00:01 | UPDATE fish SET name = 'アジ' WHERE id = 1; COMMIT; [補足] CDRTS\$ROW列が、システム時刻の10:00:01で更新される。(更新前のCDRTS\$ROW列は、9:50:00とする) |
|
10:00:02 | Primary SystemからのレプリケーションデータをSecondary Systemへ適用する際に下記が発生する。 [競合の検出] Primary Systemの更新前CDRTS\$ROW(9:50:00)とSecondary SystemのCDRTS\$ROW(10:00:01)が一致しないため、UPDATE ROW EXISTS競合が発生する。 [競合の解消] Secondary SystemのCDRTS\$ROWが新しいため、Primary SystemのUPDATEデータは破棄される。 |
|
10:00:03 | Secondary SystemからのレプリケーションデータをPrimary Systemへ適用する際に下記が発生する。 [競合の検出] Secondary Systemの更新前CDRTS\$ROW(9:50:00)とPrimary SystemのCDRTS\$ROW(10:00:00)が一致しないため、UPDATE ROW EXISTS競合が発生する。 [競合の解消] Secondary SystemのCDRTS\$ROWが新しいため、Primary SystemのデータをUPDATEする。 UPDATE fish SET name = 'アジ' WHERE id = 1; |
|
10:00:04 | SELECT name FROM fish WHERE id = 1; NAME ---------- アジ |
SELECT name FROM fish WHERE id = 1; NAME ---------- アジ |
上記の通り、データの内容は一致した状態となり、データ不整合は発生しませんが、結果として、Primary Systemに登録されたデータが破棄されてしまいます。実際にシステム運用において、上記の動作が許容できるか慎重に検討する必要があります。許容できない場合は、アクティブ/アクティブ型の双方向レプリケーションの構成を見直し、片方向レプリケーション構成を検討するなどの対応をとる必要があります。
競合タイプ: UPDATE ROW MISSING
- 競合の検出
ターゲットDBへのUPDATE実行時に、更新対象行が存在しない場合 - 競合の解消
ソースDBの更新後CDRTS\$ROW列値とターゲットDBのDELTIME\$\$列値(ツームストンテーブル)を比較し、新しいほうのデータが採用される
競合タイプ: DELETE ROW EXISTS
- 競合の検出
ターゲットDBへのDELETE実行時に、ソースDBの更新前CDRTS\$ROW列値とターゲットDBのCDRTS\$ROW列値が異なる場合 - 競合の解消
ソースDBのDELTIME\$\$列値(ツームストンテーブル)とターゲットDBのCDRTS\$ROW列値を比較し、新しいほうのデータが採用される
下記にUPDATE ROW MISSING競合および、DELETE ROW EXISTS競合の例を示します。
Primary System のUPDATE処理がSecondary Systemにレプリケーションされる際に、UPDATE ROW MISSING競合が発生します。またSecondary SystemのDELETE処理がPrimary Systemにレプリケーションされる際に、DELETE ROW EXISTS競合が発生します。
時刻 | Primary System | Secondary System |
---|---|---|
10:00:00 | UPDATE fish SET name = 'サバ' WHERE id = 1; COMMIT; [補足] CDRTS\$ROW列が、システム時刻の10:00:00で更新される。(更新前のCDRTS\$ROW列は、9:50:00とする) |
|
10:00:01 | DELETE FROM fish WHERE id = 1; COMMIT; [補足] ツームストンテーブルにキー情報(id=1)とタイムスタンプ(10:00:01)が登録される。 |
|
10:00:02 | DELETE FROM fish WHERE id = 1; COMMIT; [補足] ツームストンテーブルにキー情報(id=1)とタイムスタンプ(10:00:01)が登録される。Primary SystemからのレプリケーションデータをSecondary Systemへ適用する際に下記が発生する。 [競合の検出] id=1のデータがSecondary Systemに存在しないため、UPDATE ROW MISSING競合が発生する。 [競合の解消] Secondary SystemのDELTIME\$\$が新しいため、Primary SystemのUPDATEデータは破棄される。 |
|
10:00:03 | Secondary SystemからのレプリケーションデータをPrimary Systemへ適用する際に下記が発生する。 [競合の検出] Secondary Systemの更新前CDRTS\$ROW(9:50:00)とPrimary SystemのCDRTS\$ROW(10:00:00)が一致しないため、DELETE ROW EXISTS競合が発生する。 [競合の解消] Secondary SystemのDELTIME\$\$が、Primary SystemのCDRTS\$ROWよりも新しいため、Primary SystemのデータをDELETEする。 DELETE FROM fish WHERE id = 1; |
|
10:00:04 | SELECT * FROM fish; レコードが選択されませんでした。 |
SELECT * FROM fish; レコードが選択されませんでした。 |
上記の通り、データの内容は一致した状態となり、データ不整合は発生しませんが、結果として、Primary Systemに登録されたデータが破棄されてしまいます。実際のシステム運用において、上記の動作が許容できるか慎重に検討する必要があります。許容できない場合は、片方向レプリケーション構成を検討など、アクティブ/アクティブ型の双方向レプリケーションの構成を見直す必要があります。
競合タイプ: DELETE ROW MISSING
- 競合の検出
ターゲットDBへのDELETE実行時に、削除対象行が存在しない場合 - 競合の解消
不整合は発生しないため、解消するための処理は行われない
下記にDELETE ROW MISSING競合の例を示します。
時刻 | Primary System | Secondary System |
---|---|---|
10:00:00 | DELETE FROM fish WHERE id = 1; COMMIT; 補足 ツームストンテーブルにキー情報(id=1)とタイムスタンプ(10:00:00)が登録される。 |
|
10:00:01 | DELETE FROM fish WHERE id = 1; COMMIT; 補足 ツームストンテーブルにキー情報(id=1)とタイムスタンプ(10:00:01)が登録される。 |
|
10:00:02 | Primary SystemからのレプリケーションデータをSecondary Systemへ適用する際に下記が発生する。 [競合の検出] id=1のデータがSecondary Systemに存在しないため、DELETE ROW MISSING競合が発生する。 |
|
10:00:03 | Secondary SystemからのレプリケーションデータをPrimary Systemへ適用する際に下記が発生する。 [競合の検出] id=1のデータがPrimary Systemに存在しないため、DELETE ROW MISSING競合が発生する。 |
|
10:00:04 | SELECT * FROM fish; レコードが選択されませんでした。 |
SELECT * FROM fish; レコードが選択されませんでした。 |
おわりに
Auto CDRの概要と、競合タイプごとにOracle GoldenGateがどのように競合を解消するのかを紹介しました。初めてご覧になった方には理解が難しかったかもしれませんが、Auto CDRにおける競合解決は、"タイムスタンプが古いデータは破棄されて、タイムスタンプの新しいデータが採用される"、とご理解いただくのが良いと思います。
次回は、実機を用いて、Auto CDRの有効化方法と競合解決履歴データの確認方法、運用時の考慮事項を紹介します。