補助インスタンスを用いた表単位のリカバリ [アーキテクチャ]

1.はじめに


 最近関わっている案件で、DBは遠隔の災対環境へ常時同期し、罹災時に災対環境に切り替えする、ただし特定の表(複数)だけ、前日の特定リストアポイントに復旧したい、という要件があった。いくつか検討した方式が以下であった。
 (a)災対同期をGoldenGate等論理レプリケーションとし表毎に同期を制御できるようにする
 (b)災対同期はDataGuardで物理レプリケーションとし、フェールオーバー後に
  (b-1)前日断面のエクスポートを取得しておき、それをインポートする
  (b-2)フラッシュバックテーブルで特定表を復旧
  (b-3)補助インスタンスを用いた表単位のリカバリ

 このうち、(a)は災対側の維持運用の負荷が高いので、(b)の方向性が望ましい。ActiveDataGuardなら(b-1)方式で、災対側でエクスポートしておくのがシンプルだ。しかし、表数やサイズが大きいと罹災時のRTOが許容できるか、ダンプファイルの置き場所やダンプ処理にかかる時間も実運用上許容されるかといった懸念がある。
 (b-2)はDBオープン後にフラッシュバックテーブルで特定断面に戻す方法である。フラッシュバックテーブルは内部的にはUNDOを使ってリストアポイントまでリカバリする。母体の表サイズが大きくても更新が少なければ高速に処理が完了する。リストアポイントまでのUNDOが上書きされないよう、十分にUNDOリテンションを確保する必要がある。リストアポイントまでの間にalter table等のDDLが実行された場合はフラッシュバックテーブルは使えない(エラーとなる)という制約があり、プライマリ側で定義変更後に罹災するなど刹那なタイミングで復旧できないリスクがあるため、これだけに頼るのもリスクがある。
 そこで(b-3)補助インスタンスを用いた表単位のリカバリである。これはRMANバックアップとアーカイブログを利用し、仮のインスタンスを立てて特定の(複数の)表をリカバリする機能である。災対側でもRMANのバックアップがとられていれば、そこから表の特定時点の断面を取り出すことができる。
 今回、この補助インスタンスを用いた表単位のリカバリを実際に行い、補助インスタンスを利用した表リカバリについて考察してみたい。

2.検証モデル


 ここでは、補助インスタンスを使った表単位のリカバリで、PDB上のSCOTT.EMPを特定のリストアポイント(=SCN)にリカバリしてみる(その他のDBは最新状態のまま)。テスト環境は手元のVirtual BoxのOracle19.11を使う。テスト準備として、アーカイブログモードとし、RMANフルバックアップを取得する。補助インスタンスリストア用に/mntをC:\Users\kazuhiro\Downloadsにマウントしておく(VirtualBoxの共有フォルダの設定)。
20240316_RMANrecoverTable.jpg

 テストシナリオは以下の通り:
  • PDB名ORCLのSCOTT.EMP/DEPTを作成、初期データを挿入
  • リストアポイント取得
  • EMPにINSERT 1件
  • EMPをリストアポイントにリカバリした内容をEMP_RECOVER表に復旧

 テストのコマンドは以下の通り。ポイントは★の部分のRMANコマンドで、recover tableで、EMP表をEMP_RECOVERED表に復旧するようにしている。なお、@empはemp表の作成スクリプトである。EMP表には初期データとして14件、リストアポイント作成後にempno=9999を1行を追加している。したがって、復旧した際には15件ではなく、14件となることが期待である。
rman target /
backup as compressed backupset database;

--EMP表作成
sqlplus scott/oracle@orcl
@emp
select count(*) from dept;
select count(*) from emp;

--リストアポイント(zenjitu)を作成
sqlplus sys/oracle@orcl as sysdba
create restore point zenjitu;

--リストアポイント後の更新を実行
sqlplus scott/oracle@orcl
insert into emp
values(  
 9999, 'KAZUHIRO', 'TAKAHASHI', 7000,  
 to_date('08-03-2024','dd-mm-yyyy'),  
 1000, null, 10  
);
commit;
select count(*) from emp;
select * from emp where empno=9999;
exit

--★表のリカバリ(EMP表をEMP_RECOVERED表に復旧する)
rman target /
recover table scott.emp of pluggable database orcl until restore point ZENJITU auxiliary destination '/mnt' REMAP TABLE 'SCOTT'.'EMP':'EMP_RECOVERED' ;

select count(*) from dept;
select count(*) from emp;
select * from emp where empno=9999;
select segment_name, bytes, blocks from user_segments;
desc emp
desc emp_recovered

3.検証結果


 補助インスタンスを用いた表のリカバリは、内部的に以下の動作を行っていることがわかった(検証結果のログは参考[1]を参照されたい)。ポイントとなる部分に★を付けたので、ここだけ追えば概ね流れは理解できるだろう。
SID='gicq'でインスタンスを起動

SCNをリストアポイントに設定
制御ファイルをリストア ★①補助インスタンス用制御ファイル
クローンデータベースをマウント
ログスイッチ

SCNをリストアポイントに設定
クローンデータファイルの1,9,4,11,3,10とtempファイルの1,3に新しい名前を付与
全てのtempfileをスイッチ
クローンデータファイルをリストア(1,9,4,11,3,10) ★②CDBリストア
全てのクローンデータファイルをスイッチ

SCNをリストアポイントに設定
クローンデータファイルをオンライン(1,9,4,11,3,10)
クローンデータベースの表領域をリカバリ ★③CDBメディアリカバリ
"SYSTEM", "ORCL":"SYSTEM", "UNDOTBS1", "ORCL":"UNDOTBS1", "SYSAUX", "ORCL":"SYSAUX"
クローンデータベースをリードオンリーでオープン

クローンのPDBをオープン(ORCL)

クローンでspfileをメモリから生成
クローンをシャットダウン
クローンをnomountで起動
制御ファイルを設定(①でリストアしたファイル)
クローンをシャットダウン
クローンをnomountで起動
クローンをマウント

SCNをリストアポイントに設定
クローンデータファイルの12に新しい名前を付与
クローンデータファイルをリストア(12) ★④PDBリストア
全てのクローンデータファイルをスイッチ

SCNをリストアポイントに設定
クローンORCLのデータファイルをオンライン(12)
クローンデータベースの表領域をリカバリ ★⑤PDBメディアリカバリ
"ORCL":"USERS", "SYSTEM", "ORCL":"SYSTEM", "UNDOTBS1", "ORCL":"UNDOTBS1", "SYSAUX", "ORCL":"SYSAUX" delete archivelog
クローンデータベースをリセットログでオープン

クローンのPDBをオープン(ORCL)

ORCLにディレクトリオブジェクトを作成する(TSPITR_DIROBJ_DPDIR as /mnt)

クローンのORCLにディレクトリオブジェクトを作成する(TSPITR_DIROBJ_DPDIR as /mnt)

表のエクスポート(/mnt/tspitr_gicq_28603.dmp) ★⑥表エクスポート

クローンデータベースをシャットダウン(abort)

表のインポート ★⑦表インポート
自動インスタンスの削除

 復旧後の結果確認をしたところ、想定通りEMP_RECOVEREDが作成されている。件数は14件で、INSERTの更新前のリストアポイントに復旧していることがわかる。
[oracle@localhost ~]$ sqlplus scott/oracle@orcl
...
SQL> select table_name from user_tables;

TABLE_NAME
--------------------------------------------------------------------------------
DEPT
EMP
EMP_RECOVERED ★

SQL>
SQL> select count(*) from EMP_RECOVERED;

  COUNT(*)
----------
        14

SQL>
SQL> select * from emp where empno=9999;

     EMPNO ENAME      JOB              MGR HIREDATE         SAL       COMM
---------- ---------- --------- ---------- --------- ---------- ----------
    DEPTNO
----------
      9999 KAZUHIRO   TAKAHASHI       7000 08-MAR-24       1000
        10


SQL> select * from EMP_RECOVERED where empno=9999;

no rows selected

 セグメントの状態を確認すると、EMP_RECOVEREDにはPKの索引がない。表の定義を確認すると、EMPNOのNOT NULL制約がなくなっていた。FK制約もない。データは復旧できるが、表の制約は外れてしまうようだ。
SQL> select segment_name, bytes, blocks from user_segments;

SEGMENT_NAME        BYTES    BLOCKS
________________ ________ _________
DEPT                65536         8
EMP                 65536         8
EMP_RECOVERED       65536         8 ★対応するPK索引がない
PK_DEPT             65536         8
PK_EMP              65536         8

SQL>
SQL> desc emp

Name        Null?       Type
___________ ___________ _______________
EMPNO       NOT NULL    NUMBER(4)
ENAME                   VARCHAR2(10)
JOB                     VARCHAR2(9)
MGR                     NUMBER(4)
HIREDATE                DATE
SAL                     NUMBER(7,2)
COMM                    NUMBER(7,2)
DEPTNO                  NUMBER(2)
SQL> desc emp_recovered

Name        Null?    Type
___________ ________ _______________
EMPNO                NUMBER(4) ★NOT NULLがない
ENAME                VARCHAR2(10)
JOB                  VARCHAR2(9)
MGR                  NUMBER(4)
HIREDATE             DATE
SAL                  NUMBER(7,2)
COMM                 NUMBER(7,2)
DEPTNO               NUMBER(2)
SQL>
SQL> select index_name from user_indexes;

INDEX_NAME
_____________
PK_DEPT
PK_EMP

SQL> select constraint_name,constraint_type,table_name,status from user_constraints;

CONSTRAINT_NAME    CONSTRAINT_TYPE    TABLE_NAME    STATUS
__________________ __________________ _____________ __________
FK_DEPTNO          R                  EMP           ENABLED
PK_DEPT            P                  DEPT          ENABLED
PK_EMP             P                  EMP           ENABLED ★EMP_RECOVEREDに対応するPK制約がない
SQL>

4.考察


 今回の検証を通して分かったことは以下の通り
  • recover table scott.emp of pluggable database orcl until restore pointで特定の表の断面を復旧させることが可能
  • 補助インスタンスに必要なメモリ(SGAのサイズ)は元のDBと同じ
  • 補助インスタンスに必要な領域はSYSTEM、SYSAUX、UNDO(CDBとPDB)+復旧したい表の格納されている表領域を構成するデータファイルの合計サイズ+TEMP(CDBとPDB)+REDO+リストアが必要なアーカイブログ
  • 復旧時間は上記ファイルのリストアおよびSCNまでのリカバリ時間+エクスポート+インポートの時間
  • 復旧した表は制約が外れる

 今回の検証結果を踏まえると、データの内容は復旧できるが、表の制約や関連する索引は復旧できない。元表をドロップして、recover tableで戻したとしても、その状態から制約や索引を付与する対処をすることは、運用上結構厳しいかもしれない。それならば、今回のように別名で一度復旧させて、元表をトランケートし、insert into selectでコピー、リストアした表をドロップが良いだろう。一時的にこの表の領域が2倍必要となるが、表毎の個別対応をするよりはシンプルに復旧できる。
SQL> truncate table emp;

Table EMP truncated.

SQL> insert /*+ append */ into emp select * from emp_recovered;

14 rows inserted.

SQL> commit;

Commit complete.

SQL> drop table emp_recovered;

Table EMP_RECOVERED dropped.

SQL>

 もう一つ考えられるのは、RESTOREコマンドにNOTABLEIMPORT句をつけて、ダンプの出力で留めておく方法である。インポートする際にトランケートモード、DATA_ONLYでインポートすれば、索引を維持したままデータを復旧できるし、DB領域は余計にかからないだろう。

22.1.3.4 リカバリされた表および表パーティションのターゲット・データベースへのインポートについて デフォルトでは、RMANは、リカバリされた表または表パーティション(エクスポート・ダンプ・ファイルに格納される)をターゲット・データベースにインポートします。ただし、RESTOREコマンドのNOTABLEIMPORT句を使用すると、リカバリされた表または表パーティションをインポートしないように選択できます。


 もしリストアポイント後に定義変更がされていた場合、元表と復旧した表とで定義が異なることになる。この場合はやはり表は再作成(必要な索引も含め)した上で、上記いずれかの対応を取ることになるのだろう。データやオブジェクトの依存関係を考えなければならないという点で、オブジェクトレベルのリカバリは本質的に難易度が高くなるのは仕方がない。

5.まとめ


 本稿では補助インスタンスを用いた表のリカバリについて、実機検証した結果を踏まえ活用方法について考察した。総じて言えるのは、バックアップから特定の表の特定の状態のダンプファイルを生成することができる、という点は極めて有用である。本質的には複雑な処理をRMANの1つのコマンドで実現できてしまうのは非常に有難い。
 ただ、使いどころという観点では、あまり大量の表に対して実行するケースには向いておらず、特定、あるいはいくつかの表について復旧したい場合に限られるだろう。補助インスタンスは復旧に関連する表領域(を構成するデータファイル)をリストアするため、復旧範囲が広がれば広がるほど、元のデータベースと同じ領域が補助インスタンスに必要となってしまうし、ダンプファイルも大きくなってしまう。
 今回の結果を踏まえると、フラッシュバックテーブルが使えるなら、多くの場合でその方が復旧はシンプルで速いだろう。UNDOを大きくとる必要はあるが、バックアップ運用が回れば副作用はないように思う。それでもUNDOが不足した場合(ギャランティでない場合は上書きされてしまう)は復旧できないというリスクがあるし、定義変更の影響もあるので、そういった表についてはこの方法で個別にリカバリすればよいだろう。
 今回調べている中で、TSPITRを使った表領域のリカバリもできることが気が付いた。リカバリしたい表(+関連する索引)を表領域にまとめられれば、有効な復旧方法になるかもしれない。この点については、別途検証してみたい。Oracleは様々なリカバリ方法が用意されているので、ユーザ側が要件に対してどこまで使い方をイマジネーションできるかが重要である。

参考


[1]補助インスタンスを用いた表のリカバリ検証のログ20240311_recovertable.txt
[2]バックアップおよびリカバリ・ユーザーズ・ガイド 19c, 22 表および表パーティションのリカバリ