Oracle GoldenGate自動競合検出および解消機能の紹介(前編)

オラクル散歩道

2022.01.26

  

1. はじめに

はじめまして、NTTデータ先端技術の千葉です。
直近数年は、Oracle Exadata Database Machineのインフラ設計構築プロジェクト、データベース移行プロジェクトに従事しています。今回は、Oracle GoldenGateの活用事例を紹介します。

2. Oracle GoldenGate双方向構成について

Oracle GoldenGateでは、アクティブ/アクティブ型の双方向構成がサポートされています。双方向構成の環境では、同一データセットのデータベースが存在し、業務アプリケーションは、任意のデータベースを更新する事が可能です。ソースDBにて更新されたトランザクションデータは、Oracle GoldenGateによって、ターゲットDBにレプリケーションされます。結果として各システムのデータベースデータは同一の内容になります。

Oracle GoldenGate双方向構成

上記双方向構成においては、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
SQL> UPDATE fish
        SET name = 'サバ'
     WHEERE id = 1;
SQL> COMMIT;            
10:00:01
SQL> UPDATE fish
        SET name = 'アジ'
     WHEERE id = 1;
SQL> COMMIT;            
10:00:05 Oracle GoldenGateが、上記更新データ(サバ)をSecondary Systemへレプリケーションする。
10:00:06 Oracle GoldenGateが、上記更新データ(アジ)をPrimary Systemへレプリケーションする。
10:00:07

Primary Systemのデータが、サバからアジに更新される。

SQL> SELECT * FROM fish;
        ID NAME 
---------- ----------
         1 アジ

Secondary Systemのデータが、アジからサバに更新される。

SQL> SELECT * FROM fish;
        ID NAME 
---------- ----------
         1 サバ

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以降から使用可能

上記1のCDRは、競合の検出と解消するためのルールを柔軟な設計が可能です。上記2のAuto CDRは、競合の検出と解消ルールが自動化されており、比較的お手軽に導入する事が可能です。本コラムでは、上記2のAuto CDRについて紹介します。本記事が、双方向構成を検討されている方々の手助けになれば幸いです。

3. Auto CDRの紹介

システム要件

Auto CDRを使用するためのシステム要件については、Oracle GoldenGate製品マニュアルに記載があります。Oracle GoldenGate 19.1の場合、マニュアル「Oracle DatabaseのためのOracle GoldenGateの使用」の「自動競合検出および解決の要件(https://docs.oracle.com/cd/F22974_01/oracle-db/automatic-conflict-detection-and-resolution2.html)」をご参照願います。

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
SQL> INSERT INTO fish (id, name) VALUES
(1, 'サバ');

補足: CDRTS$ROW列が、システム時刻の10:00:00で更新される。

SQL> COMMIT;
10:00:01
SQL> INSERT INTO fish (id, name) VALUES
(1, 'アジ');

補足: CDRTS$ROW列が、システム時刻の10:00:01で更新される。

SQL> COMMIT;
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
SQL> SELECT * FROM fish;
        ID NAME 
---------- ----------
         1 アジ
SQL> SELECT * FROM fish;
        ID NAME 
---------- ----------
         1 アジ

上記の通り、データの内容は一致した状態となり、データ不整合は発生しませんが、結果として、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
SQL> UPDATE fish
  SET name = 'サバ'
WHERE id = 1;            

補足: CDRTS$ROW列が、システム時刻の10:00:00で更新される。(更新前のCDRTS$ROW列は、9:50:00とする)

SQL> COMMIT;            
10:00:01
SQL> UPDATE fish
  SET name = 'アジ'
WHERE id = 1;

補足: CDRTS$ROW列が、システム時刻の10:00:01で更新される。(更新前のCDRTS$ROW列は、9:50:00とする)

SQL> COMMIT;         
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
SQL> SELECT * FROM fish;
        ID NAME 
---------- ----------
         1 アジ
SQL> SELECT * FROM fish;
        ID NAME 
---------- ----------
         1 アジ

上記の通り、データの内容は一致した状態となり、データ不整合は発生しませんが、結果として、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
SQL> UPDATE fish
  SET name = 'サバ'
WHERE id = 1;            

補足: CDRTS$ROW列が、システム時刻の10:00:00で更新される。(更新前のCDRTS$ROW列は、9:50:00とする)

SQL> COMMIT;            
10:00:01
SQL> DELETE FROM fish WHERE id = 1;       

補足: ツームストンテーブルにキー情報(id=1)とタイムスタンプ(10:00:01)が登録される。

SQL> COMMIT;    
10:00:02

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する。

10:00:04
SQL> SELECT * FROM fish;
レコードが選択されませんでした。
SQL> SELECT * FROM fish;
レコードが選択されませんでした。

上記の通り、データの内容は一致した状態となり、データ不整合は発生しませんが、結果として、Primary Systemに登録されたデータが破棄されてしまいます。実際のシステム運用において、上記の動作が許容できるか慎重に検討する必要があります。許容できない場合は、片方向レプリケーション構成を検討など、アクティブ/アクティブ型の双方向レプリケーションの構成を見直す必要があります。

競合タイプ: DELETE ROW MISSING
競合の検出 競合の解消
ターゲットDBへのDELETE実行時に、削除対象行が存在しない場合 不整合は発生しないため、解消するための処理は行われない

下記にDELETE ROW MISSING競合の例を示します。

時刻 Primary System Secondary System
10:00:00
SQL> DELETE FROM fish WHERE id = 1;        

補足: ツームストンテーブルにキー情報(id=1)とタイムスタンプ(10:00:00)が登録される。

SQL> COMMIT;           
10:00:01
SQL> DELETE FROM fish WHERE id = 1;   

補足: ツームストンテーブルにキー情報(id=1)とタイムスタンプ(10:00:01)が登録される。

SQL> COMMIT; 
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
SQL> SELECT * FROM fish;
レコードが選択されませんでした。
SQL> SELECT * FROM fish;
レコードが選択されませんでした。

4. おわりに

前編では、Auto CDRの概要と、競合タイプごとにOracle GoldenGateがどのように競合を解消するのかを紹介しました。初めてご覧になった方には理解が難しかったかもしれませんが、Auto CDRにおける競合解決は、"タイムスタンプが古いデータは破棄されて、タイムスタンプの新しいデータが採用される"、とご理解いただくのが良いと思います。
後編では、実機を用いて、Auto CDRの有効化方法と競合解決履歴データの確認方法、運用時の考慮事項を紹介します。

5. 番外編~私の散歩道~

デカ唐揚げ弁当

自転車圏内にある地元の弁当屋"ダイナマイトキッチン"を紹介します。ダイナマイトキッチンは、デカ盛りのメニューをお手頃価格で販売しているテイクアウト専門の弁当屋で、画像は、私のお気に入りであるデカ唐揚げ弁当(おかずのみ)です。画像の通り、454g(容器の重さ含む)の大ボリュームでなんと540円 (価格は2021年10月時点)です。数は10個で、重量はそれぞれ43g, 44g, 50g, 43g, 47g, 42g, 44g, 42g, 43g, 45gでした。重量の平均は、44.3gで、標本不偏分散は6.233です。唐揚げの重量は正規分布に従うと仮定して、母平均μに対する90%の信頼区間を求めると、42.85g < μ < 45.75g となります。ばらつきが小さいため、二名でシェアしてもケンカになりませんね!ご興味のある方は、ぜひ検索してみてください。

※文章中の商品名、会社名、団体名は、各社の商標または登録商標です。