今回からはOracle Golden Gateを使ってみた 応用編です。
インストールや基本設定はOracle Golden Gateを使ってみた 基本編(1)から(3)をご参照ください。
応用編では双方向レプリケーションの設定と検証を行います。双方向レプリケーションで重要なのは競合の解消です。
以下、競合についてマニュアルから抜粋です。
Oracle Fusion Middleware Oracle GoldenGateの管理, 19c (19.1.0)
Oracle GoldenGateは非同期ソリューションであるため、個別のシステム上にある同一のデータセットに対して(ほぼ)同時に変更が行われた場合、競合が発生する可能性があります。競合は、同時変更のタイミングが次の非同期状態のいずれかにつながる場合に発生します。
- Replicatが、一意性整合性制約(PRIMARY KEY制約やUNIQUE制約など)に違反する挿入操作または更新操作を適用すると、一意性競合が発生します。この競合タイプの例としては、2つの異なるデータベースから2つのトランザクションが発生し、各トランザクションが同じ主キー値で表に行を挿入する場合があげられます。
- Replicatが、同じ行への別の更新と競合する更新を適用すると、更新競合が発生します。更新競合が発生するのは、異なるデータベースから生じた2つのトランザクションがほぼ同時に同じ行を更新した場合です。証跡レコードに格納されている古い値(変更前の値)とターゲット・データベース内の同じ行の現行値との間に差異があると、Replicatは更新競合を検出します。
- 2つのトランザクションが異なるデータベースから生じ、一方が行を削除するのと同時にもう一方が同じ行を更新または削除すると、削除競合が発生します。この場合、その行は存在せず、更新も削除もできません。主キーが存在しないため、Replicatは行を見つけられません。
ここで「(ほぼ)同時」と言っているのは一方のDBでDMLを発行し、それがもう一方のDBに反映される前にもう一方でDMLを発行したケースになります。
FLUSHSECSで指定した時間(デフォルト1秒)が経過するか、Extractバッファがいっぱいになればフラッシュされる仕組みですし、基本編(3)で検証した感じでは秒単位のタイムラグは普通にあるようなので、個人的な感覚では「(ほぼ)同時」と言うより、「だいたい同時」ですが…。
とは言え、検証では更新量が少ないので実運用では違うかも知れません。
では、競合解消の設定を行っていきます。
Oracle GoldenGateには競合検出および解決を自動で行う機能(Conflict Detection and Resolution:CDR)が備わっているので、これを使用します。
CDRには以下の基本ルーチンが備わっています。
- INSERTの一意性競合を解決します。
- 行は存在するが、1つ以上の列の変更前イメージがデータベースの現行値と異なる場合に発生する、UPDATEの「データが見つからない」競合を解決します。
- 行が存在しない場合に発生する、UPDATEの「データが見つからない」競合を解決します。
- 行は存在するが、1つ以上の列の変更前イメージがデータベースの現行値と異なる場合に発生する、DELETEの「データが見つからない」競合を解決します。
- 行が存在しない場合に発生する、DELETEの「データが見つからない」競合を解決します。
以上が「どのような」競合を解消するかですが、「どのように」解消するかにはUSEMAX解決、USEMIN解決、USEMAXSEQ解決、USEMINSEQ解決、OVERWRITE解決、DISCARD解決、USEDELTA解決およびIGNORE解決があります。
この8つの解決方法を上記5つのパターンそれぞれに組み合わせて設定してゆく事になりますが、ここでは実運用で使用する可能性が高そうなパターンのみ検証します。
検証で使用する解決方法を簡単に説明すると以下の通りです。
- USEMAX解決:トレイル・レコードの解決列と、ターゲットDBデータの解決列の値を比較し、大きい方を優先する。
- OVERWRITE解決:トレイル・レコードのデータでターゲットDBデータを上書きする。
- DISCARD解決:ターゲットDBの現在の値を保持し、トレイル・レコード内のデータを破棄ファイルに書き込む。
実運用で使いそうな具体例で説明すると、後から発行されたDMLを優先する場合、DMLが発行された時刻を記録するTIMESTAMP列を解決列として使用し、USEMAX解決を行います。
競合発生時に一方のDBの更新を優先し、他方のDBの更新を無効にする場合は優先DB側でDISCARDを設定し、もう一方のDBでOVERWRITEを設定します。
尚、ReplicatがBATCHSQLモードで動作する場合、競合解決は実行されません。
では、前準備としてDBMS_GOLDENGATE_ADMパッケージのADD_AUTO_CDRプロシージャを使用して、最新タイムスタンプの競合検出および解決を双方のDBで構成します。ADD_AUTO_CDR_COLUMN_GROUPプロシージャでDMLが発行された時刻を記録するTIMESTAMP列を追加します。
SQL> conn / as sysdba
Connected.
SQL> BEGIN
2 DBMS_GOLDENGATE_ADM.ADD_AUTO_CDR(
3 schema_name => 'ggtest',
4 table_name => 'tab2');
5 END;
6 /
PL/SQL procedure successfully completed.
SQL> SELECT TABLE_OWNER,TABLE_NAME,TOMBSTONE_TABLE,ROW_RESOLUTION_COLUMN FROM ALL_GG_AUTO_CDR_TABLES ORDER BY TABLE_OWNER, TABLE_NAME;
TABLE_OWNER TABLE_NAME TOMBSTONE_TABLE ROW_RESOLUTION_COLUMN
--------------- --------------- --------------- -------------------------
GGTEST TAB2 DT$_TAB2 CDRTS$ROW
SQL> desc tab2
Name Null? Type
----------------------------------------- -------- ----------------------------
COL1 NOT NULL NUMBER
COL2 VARCHAR2(100)
COL3 TIMESTAMP(6)
SQL> desc DT$_TAB2
Name Null? Type
----------------------------------------- -------- ----------------------------
COL1 NUMBER
DELTIME$$ TIMESTAMP(6)
トゥームストーン・テーブルとしてDT$_TAB2が作成されました。
元々のTAB2には上記の通りCOL1、COL2、COL3の3つのカラムがあり、COL1は主キーです。DT$_TAB2には主キーのCOL1と、DML発行時刻を記録するTIMESTAMPが作成されています。
#TOMBSTONEは普通名詞で墓石の事なんですが、どうしてこんな名前なんでしょう…
テーブルだけでなく、同名のインデックスも作成されていますね。
SQL> select segment_name,segment_type,TABLESPACE_NAME from user_segments;
SEGMENT_NAME SEGMENT_TYPE TABLESPACE_NAME
-------------------- ------------------ ------------------------------
DT$_TAB2 TABLE USERS
DT$_TAB2 INDEX USERS
SYS_C007538 INDEX USERS
SYS_C007613 INDEX USERS
SYS_C007837 INDEX USERS
TAB1 TABLE USERS
TAB2 TABLE USERS
TAB3 TABLE USERS
8 rows selected.
SQL> select table_name,index_name,INDEX_TYPE from ind order by 1;
TABLE_NAME INDEX_NAME INDEX_TYPE
-------------------- -------------------- ---------------------------
DT$_TAB2 DT$_TAB2 NORMAL
TAB1 SYS_C007837 NORMAL
TAB2 SYS_C007538 NORMAL
TAB3 SYS_C007613 NORMAL
場合によってはテーブルにinsertトリガー、updateトリガーを使用してinsert、update時刻を記録するカラムを持つ設計になっているので、その場合はそのカラムがそのまま利用可能です。
但し、主キーはUPDATEROWEXISTSのUSEMAXには使えません。
次に、ggsciからDBに接続して表のすべての列の無条件のサプリメンタル・ロギングを有効にします。これも双方のDBで実行します。
GGSCI (mitsdb01) 1> DBLOGIN USERID ggadmin,password welcome1
Successfully logged into database.
GGSCI (mitsdb01 as GGADMIN@ggs) 2> add TRANDATA ggtest.tab2 allcols
2022-03-25 10:07:56 INFO OGG-15132 Logging of supplemental redo data enabled for table GGTEST.TAB2.
2022-03-25 10:07:56 INFO OGG-15133 TRANDATA for scheduling columns has been added on table GGTEST.TAB2.
2022-03-25 10:07:56 INFO OGG-15134 TRANDATA for all columns has been added on table GGTEST.TAB2.
2022-03-25 10:07:56 INFO OGG-15135 TRANDATA for instantiation CSN has been added on table GGTEST.TAB2.
2022-03-25 10:07:56 INFO OGG-10471 ***** Oracle Goldengate support information on table GGTEST.TAB2 *****
Oracle Goldengate support native capture on table GGTEST.TAB2.
Oracle Goldengate marked following column as key columns on table GGTEST.TAB2: COL1.
ここまでで事前準備が済んだので、次回はパラメータ設定と競合解消の検証を行います。