はじめに
IBM Db2のQレプリケーション(QREP)をについて、いろいろ触っているのですが、マニュアルのみでは理解が難しいと感じたので、手元の環境で簡単に動作確認をしてみました。
本記事では、フルリフレッシュについて試してみました。
QREPのセットアップはこちらで実施しています。
Qレプリケーションをセットアップしてみた
フルリフレッシュとは
Qレプリケーション(QREP)におけるフルリフレッシュとは、ソース表とターゲット表の同期をとる作業のことです。
レプリケーションはソース表とターゲット表の内容で整合性が取れている状態で開始しなければなりません。
フルリフレッシュは、MQを介さず、直接QアプライがソースDBに接続し、ソース表から全データを読み込みターゲット表にLOADします。(デフォルトはカーサーLOAD)
フルリフレッシュを実施する代表的なケースは以下が考えられます。
- ターゲット表作成時の初期ロード時
- 障害等でソース表とターゲット表でデータの整合性が取れなくなった場合の再ロード時
準備
テスト用の簡易的な表を用意しておきました。
$ db2 "select * from testschema.sports"
SPORT_ID SPORT_NAME ENGLISH_NAME
----------- ------------------------------ ------------------------------
1 サッカー Soccer
2 野球 Baseball
3 バスケットボール Basketball
4 テニス Tennis
5 卓球 Table Tennis
5 レコードが選択されました。
そして、Qサブスクリプション定義も作成しておきました。
$ db2 "select substr(subname,1,30)as subname, state, has_loadphase from asn.ibmqrep_subs where subname='SUB-SPORTS'"
SUBNAME STATE HAS_LOADPHASE
------------------------------ ----- -------------
SUB-SPORTS N I
1 レコードが選択されました。
$ db2 "select substr(subname,1,30)as subname, state, has_loadphase from asn.ibmqrep_targets where subname='SUB-SPORTS'"
SUBNAME STATE HAS_LOADPHASE
------------------------------ ----- -------------
SUB-SPORTS I I
1 レコードが選択されました。
Qサブスクリプション定義時に特にオプションで指定せずに作成すると、上記のような値が設定されていると思います。
Qサブスクリプションの状態について
フルリフレッシュを実施するときのQサブスクリプションの状態を確認してみます。
主に以下の項目が関係しています。
※今回はこの2つの項目のみについての確認とします。
| 列 | 説明 |
|---|---|
| STATE | 現在のQサブスクリプションの状態を示すフラグ |
| HAS_LOADPHASE | Qサブスクリプションのターゲット表が、ソース表からデータをロードするかどうかを示すフラグ |
IBMQREP_SUBS 表 (キャプチャーの制御表のひとつ)
"STATE" は通常、"I"(非活動化状態)か "A"(活動化状態)になっているはずです。
非活動化状態では、データ・メッセージを送信しませんが、活動化状態では送信します。
デフォルトで新規作成直後は"N"となっており、キャプチャー・プログラムの開始時に自動的に"A"(活動化状態)になります。
他にもいくつか状態がありますが、ここでは割愛させていただきます。
詳細はこちら:IBMQREP_SUBS 表
"HAS_LOADPHASE" はロードをするかどうかを決めるもので、フルリフレッシュ時のみ、この値を "I"(自動ロード)に変更します。それ以外の時は "N"(ロードしない)としておきます。
IBMQREP_TARGETS 表 (アプライの制御表のひとつ)
こちらも IBMQREP_SUBS 表と同様に、基本的には"STATE" と "HAS_LOADPHASE" を確認します。
同じように "STATE" は通常、"I"(非活動化状態)か "A"(活動化状態)の状態となっており、非活動化状態では、アプライはターゲットへの変更を適用せず、トランザクションを破棄します。活動化状態では、アプライが変更をターゲットに適用します。
"HAS_LOADPHASE" についてもSUBS表と意味は同じです。
詳細はこちら:IBMQREP_TARGETS 表
ちょっと試してみる
試しに "STATE" の値について、キャプチャー側を "A" 、アプライ側を "I"とした状態で、ソース表の変更を行ってみます。(キャプチャー/アプライ・プログラムは起動しておく)
$ db2 "select substr(subname,1,30)as subname, state, has_loadphase from asn.ibmqrep_subs where subname='SUB-SPORTS'"
SUBNAME STATE HAS_LOADPHASE
------------------------------ ----- -------------
SUB-SPORTS A N
1 レコードが選択されました。
$ db2 "select substr(subname,1,30)as subname, state, has_loadphase from asn.ibmqrep_targets where subname='SUB-SPORTS'"
SUBNAME STATE HAS_LOADPHASE
------------------------------ ----- -------------
SUB-SPORTS I N
1 レコードが選択されました。
ここでソース表を更新(バスケットボールをゴルフに変更)
$ db2 "update testschema.sports set sport_name='ゴルフ', english_name='Golf' where sport_name='バスケットボール' "
DB20000I SQL コマンドが正常に完了しました。
$ db2 "select * from testschema.sports"
SPORT_ID SPORT_NAME ENGLISH_NAME
----------- ------------------------------ ------------------------------
1 サッカー Soccer
2 野球 Baseball
3 ゴルフ Golf
4 テニス Tennis
5 卓球 Table Tennis
5 レコードが選択されました。
ログを確認します。
<QArow::QArow> ASN7513W "Q Apply" : "ASN" : "BR00000" : SUB_ID "4" が変更された行を受け取りましたが、 アクティブな Q サブスクリプション (受信キュー "QREP.RECVQ"、レプリケーション・キュー・マップ "SAMPLE_ASN_TO_TARGETDB_ASN") がありません。 Q アプライ・プログラムは変更を適用できません。 理由コード: "0"
<browser::verifyMsgVersion> ASN8999D Browser for queue 'QREP.RECVQ' received a 'ASNMQ_SUBDEACTIVATED' message.
<browser::processRecvQ> ASN7595W "Q Apply" : "ASN" : "BR00000" : Q アプライ・プログラムが Q サブスクリプションの非アクティブなメッセージを受け取りましたが、 SUB_ID "4" (受信キュー "QREP.RECVQ"、 レプリケーション・キュー・マップ "SAMPLE_ASN_TO_TARGETDB_ASN" ) を見つけることができないか、または または Q サブスクリプションが非アクティブ状態です。
キャプチャーから変更のメッセージを受け取ったが、アプライのQサブスクリプションが非アクティブのため、ターゲットへの変更を適用できない、というメッセージがログに出ています。(ID=4というのはこのQサブスクリプションの番号です。)
そのためターゲット表を確認しても、変更は適用されていませんでした。
当然ですが、キャプチャーが活動化状態でも、アプライが非活動状態であれば、適用されないということですね。逆もまた然りで、キャプチャーが非活動状態だと、そもそもメッセージを送らないので、アプライが活動化状態でもダメです。
フルリフレッシュの実行
以下の流れで、Qサブスクリプション定義のあるすべての表をフルリフレッシュしてみます。
1. Qキャプチャー/Qアプライ・プログラムの停止
stopを指定した asnqccmd および asnqccmd コマンドで停止します。
2. ソース/ターゲットの両方でローカルMQキューからすべてのメッセージを消去
ローカル・キューが残っている場合は、MQ コマンド clear qlocal(<キュー名>)を使用してキューをクリアします。
3. Qサブスクリプションの状態を変更(STATE、HAS_LOADPHASE)
フルリフレッシュ対象のQサブスクリプション状態を STATE=I、HAS_LOADPHASE=I に変更します。
$ db2 "update asn.ibmqrep_subs set state='I' where subname='SUB-SPORTS' "
DB20000I SQL コマンドが正常に完了しました。
$ db2 "update asn.ibmqrep_subs set has_loadphase='I' where subname='SUB-SPORTS' "
DB20000I SQL コマンドが正常に完了しました。
$ db2 "select substr(subname,1,30)as subname, state, has_loadphase from asn.ibmqrep_subs where subname='SUB-SPORTS'"
SUBNAME STATE HAS_LOADPHASE
------------------------------ ----- -------------
SUB-SPORTS I I
1 レコードが選択されました。
$ db2 "update asn.ibmqrep_targets set state='I' where subname='SUB-SPORTS' "
DB20000I SQL コマンドが正常に完了しました。
$ db2 "update asn.ibmqrep_targets set has_loadphase='I' where subname='SUB-SPORTS' "
DB20000I SQL コマンドが正常に完了しました。
$ db2 "select substr(subname,1,30)as subname, state, has_loadphase from asn.ibmqrep_targets where subname='SUB-SPORTS'"
SUBNAME STATE HAS_LOADPHASE
------------------------------ ----- -------------
SUB-SPORTS I I
1 レコードが選択されました。
4. Qキャプチャー/Qアプライ・プログラムの開始
asnqccmd および asnqccmd コマンドで起動します。
5. QサブスクリプションでCAPSTARTシグナルを送信
キャプチャーでCAPSTARTシグナルを送信します。
$ db2 "insert into asn.ibmqrep_signal (signal_time, signal_type, signal_subtype, signal_input_in, signal_state) values (current timestamp, 'CMD', 'CAPSTART', 'SUB-SPORTS', 'P')"
DB20000I SQL コマンドが正常に完了しました。
アプライ・ログに、ロードが完了したメッセージが出力されていました。
今回はとても簡易的なテーブルのため、一瞬でロードが完了しています。
ターゲット表にも反映されていることも確認できました。
<checkPlatformDB2Version> ASN7531I "Q Apply" : "ASN" : "BR00000SP004" : Q サブスクリプション "SUB-SPORTS" (受信キュー "QREP.RECVQ"、 レプリケーション・キュー・マップ "SAMPLE_ASN_TO_TARGETDB_ASN") に対して、ロード・ユーティリティー "LOAD from CURSOR" が Q アプライ・プログラムによって選択されました。 理由コード: "0"
<invokeLoad> ASN7528I "Q Apply" : "ASN" : "BR00000SP004" : Q サブスクリプション "SUB-SPORTS" (受信キュー "QREP.RECVQ"、 レプリケーション・キュー・マップ "SAMPLE_ASN_TO_TARGETDB_ASN") に対して、 Q アプライ・プログラムが "LOAD from CURSOR (REMOTE FETCH)" ユーティリティーを使用して "TESTSCHEMA.SPORTS" 表をロードします。
<invokeLoad> ASN7529I "Q Apply" : "ASN" : "BR00000SP004" : "TESTSCHEMA.SPORTS" 表の "LOAD from CURSOR (REMOTE FETCH)" ユーティリティーが、 Q サブスクリプション "SUB-SPORTS" (受信キュー "QREP.RECVQ"、レプリケーション・キュー・マップ "SAMPLE_ASN_TO_TARGETDB_ASN") に対して正常に完了しました。 このユーティリティーからのメッセージは次のとおりです。"Rows loaded: 5, Rows rejected: 0, Rows skipped: 0, Rows deleted: 0"。
<LoadParms::invokeLoad> ASN8999D Q Apply will ignore SQL3600N from the SET INTEGRITY utility for table "TESTSCHEMA.SPORTS" for the Q subscription "SUB-SPORTS".
<invokeLoad> ASN7529I "Q Apply" : "ASN" : "BR00000SP004" : "TESTSCHEMA.SPORTS" 表の "SET INTEGRITY" ユーティリティーが、 Q サブスクリプション "SUB-SPORTS" (受信キュー "QREP.RECVQ" 、レプリケーション・キュー・マップ "SAMPLE_ASN_TO_TARGETDB_ASN") に対して正常に完了しました。 このユーティリティーからのメッセージは次のとおりです。"SQLCODE: -3600"。
<QAsubMgr::subStateTransition> ASN7608I "Q Apply" : "ASN" : "BR00000" : プログラムが Q サブスクリプション "SUB-SPORTS" (受信キュー "QREP.RECVQ"、 レプリケーション・キュー・マップ "SAMPLE_ASN_TO_TARGETDB_ASN") のロードを終了し、 Q キャプチャー・プログラムに通知しました。
<QAsubMgr::subStateTransition> ASN7607I "Q Apply" : "ASN" : "BR00000" : Q サブスクリプション "SUB-SPORTS" (受信キュー "QREP.RECVQ"、 レプリケーション・キュー・マップ "SAMPLE_ASN_TO_TARGETDB_ASN") がターゲットのロードを終了しました。 変更は、スピル・キュー "IBMQREP.SPILL.MODELQ.0.4.1" から適用されます。
<browser::verifyMsgVersion> ASN8999D Browser for queue 'QREP.RECVQ' received a 'ASNMQ_LOADDONE_RCVD' message.
3行目の以下の部分を見ると、5レコードをロードできたことが分かると思います。
"Rows loaded: 5, Rows rejected: 0, Rows skipped: 0, Rows deleted: 0"
6. Qサブスクリプションでの状態を戻す
フルリフレッシュが完了したので、自動ロードの設定を戻しておきます。
$ db2 "update asn.ibmqrep_subs set has_loadphase='N' where subname='SUB-SPORTS' "
DB20000I SQL コマンドが正常に完了しました。
$ db2 "select substr(subname,1,30)as subname, state, has_loadphase from asn.ibmqrep_subs where subname='SUB-SPORTS'"
SUBNAME STATE HAS_LOADPHASE
------------------------------ ----- -------------
SUB-SPORTS A N
1 レコードが選択されました。
$ db2 "update asn.ibmqrep_targets set has_loadphase='N' where subname='SUB-SPORTS' "
DB20000I SQL コマンドが正常に完了しました。
$ db2 "select substr(subname,1,30)as subname, state, has_loadphase from asn.ibmqrep_targets where subname='SUB-SPORTS'"
SUBNAME STATE HAS_LOADPHASE
------------------------------ ----- -------------
SUB-SPORTS A N
1 レコードが選択されました。
7. Qキャプチャー/Qアプライ・プログラムを再起動
Qサブスクリプションの状態変更を反映させるために、一度再起動させます。
さいごに
これで、一旦フルリフレッシュできたかと思います。
状況によってはいくつかやり方もあるかと思いますが、こんな感じで試してみました。
ちなみに、フルリフレッシュ中もソース表の更新は可能であり、更新情報はMQを介して送信され、スピル・キューに蓄えられます。そしてアプライはロードが完了すると、スピル・キューにあるメッセージの処理を開始し、スピル・キューの適用が完了すると差分コピーを始める、というような動作となるようです。
今回は STATE と HAS_LOADPHASE の設定を変えてフルリフレッシュを実施してみましたが、実際は他にも関係する設定値があると思うので、引き続き調査が必要だと感じました。