このブログを検索

2011年2月9日水曜日

マテリアライズドビューの同期エラーを解決する

0.はじめに
マテリアライズドビューは、実体を持つビューのこと。
サーバ間レプリケーションとしても使えるが、設定によってはフルスキャンするので安易に使うべきではありません。
履歴系テーブルの集計など、ビューの内容を保存しておきたい場合に有効。
(用語)
マスタ表・・・同期元のテーブル
レプリケーション表・・・同期先のテーブル。マテリアライズドビュー
リフレッシュ・・・マスタ表からレプリケーション表への同期
完全リフレッシュ・・・マスタ表からレプリケーション表へテーブル全件コピー
マテリアライズドビューログ・・・マスタ表の更新履歴。マテビューログ
高速リフレッシュ・・・マテビューログのレプリケーション表への反映
リフレッシュグループ・・・自動リフレッシュでの実行ジョブの単位
rowidマテリアライズドビュー・・・rowidをキーにしたマテビュー。インデックス参照がないので高速

1.アラートログの確認
エラーは、レプリケーション側のDBのアラートログに出力されます。
$ORACLE_BASE/admin/sss01rep/bdump/alert_sss01rep.log

ここでは、rowidマテビューかつ高速リフレッシュ設定をした場合において、マスタ表をtruncateしてしまったケースを取り上げます。
高速リフレッシュは、更新履歴つまりマスタ表の差分情報だけで同期をするため、マスタ表そのものがなくなるとエラーとなります。
また、今回のケースはリフレッシュグループにより自動リフレッシュを行っています。
ORA-12034: "SSS01DBIUSER"."TBL_SITEITEM1"の
マテリアライズド・ビュー・ログは最終リフレッシュよりも新しいものです。

2.対応手順
完全リフレッシュをすることで解決します。
マテビューログは、完全リフレッシュすることにより自動的にtruncateされます。
手動でマテビューログをtruncateしても解決しません。
システムテーブルにあるマテビュー管理テーブルにスナップショット情報が残っているため不整合が発生します。

2-1.自動リフレッシュの失敗によりジョブが停止していないか確認します
BROKEN=Yの場合、停止しています。
失敗してもリトライしてくれますが、FAILURES≧16となるとジョブが停止します。
set time on
set pages 10000
set lines 100
COLUMN BROKEN FORMAT a6
COLUMN SCHEMA_USER FORMAT a20
COLUMN JOB FORMAT 99999
COLUMN WHAT FORMAT a55
alter session set nls_date_format='yyyy/mm/dd hh24:mi:ss';
select JOB,SCHEMA_USER,LAST_DATE,NEXT_DATE,FAILURES,BROKEN,WHAT from dba_jobs;

   JOB SCHEMA_USER          LAST_DATE           NEXT_DATE             FAILURES BROKEN
------ -------------------- ------------------- ------------------- ---------- ------
WHAT
-------------------------------------------------------
    50 SSS01DBIUSER         2011/02/09 22:46:47 2011/02/09 22:47:47          9 N
dbms_refresh.refresh('"SSS01DBIUSER"."REFRESH_I"');

2-2.アラートログでエラーとなっているテーブルをリフレッシュグループから外します
完全リフレッシュ中に、自動同期と処理が重ならないにするためです。
$ sqlplus sss01dbiuser@SSS01REP
SQL> execute dbms_refresh.subtract(name=>'REFRESH_I',list=>'TBL_SITEITEM1')

2-3.必要であればリフレッシュグループを再開します。
高速リフレッシュの場合、リフレッシュグループが停止している間、マテビューログに更新履歴が溜まり続けます。
マテビューログは必ずフルスキャンなので、溜め過ぎてから再開するとリフレッシュが終わらなくなります。
サーバ間マテビューの場合、UNDO関連など別のエラーが発生して2次災害となります。
$ sqlplus sss01dbiuser@SSS01REP
SQL> exec dbms_job.broken(job=>50,broken=>false);
SQL> commit;

2-4.インデックス情報を調べておき、レプリケーション表のインデックスを削除します
インデックスを貼ったままだと、完全リフレッシュが数倍遅くなります。
SQL> drop index IXT1_SITEITEM1;

2-5.完全リフレッシュを行います
sqlplus sss01dbiuser@SSS01REP
SQL> exec dbms_mview.refresh('SSS01DBIUSER.TBL_SITEITEM1', 'c')

2-6.インデックスを再作成します
sqlplus sss01dbiuser@SSS01REP
SQL> create index IXT1_SITEITEM1 on TBL_SITEITEM1 (USER_ID,ITEM_ID) tablespace SSS01_I_IDX;

2-7.リフレッシュグループに再追加
sqlplus sss01dbiuser@SSS01REP
SQL> exec DBMS_REFRESH.ADD ('REFRESH_I','TBL_SITEITEM1');

2-8.リフレッシュグループが停止していたら再開

2-9.自動リフレッシュのインターバルを待ち、FAILURES=0かつBROKEN=Nであることを確認します

0 件のコメント:

コメントを投稿