【AWS EC2】OracleアーカイブREDOログを使用したホットバックアップ戦略・AMI+アーカイブログによる不完全リカバリ検証

お久しぶりです。
クラウドアーキテクチャチームの齋藤です。

今回は、AWS EC2インスタンスにOracleをインストールして運用している環境でのホットバックアップ戦略についてお話しします。
当方、特にOracleについて詳しいわけでもないので、間違っていることやよりよいアプローチがあればぜひご教示いただけますと幸いです。

内容としては、ある程度AWSはわかるが、Oracleわからんって人がメインとなると思います。

# Oracleバックアップについてのキャッチアップ

本内容を読む前に、まずOracleのバックアップ知識をキャッチアップしておきましょう。

[Oracleバックアップの基礎的知識][2]
- リカバリとリストアの違い
- 物理バックアップ
- データ・制御・REDOログファイル
- ARCHIVE LOG モード
- ホットバックアップ
- 完全リカバリと不完全リカバリ

ここまでで基本的な用語と流れを把握できます。

[アーカイブログとは][3]
- アーカイブログ

[REDOログとは][7]
- REDOログ

Oracleバックアップにおいて、RPOを決める重要な要素がアーカイブログです。
アーカイブログが無ければフルバックアップの間隔でRPOが決まってしまいますが、アーカイブログが適用できればその分だけRPOを短縮することができます。

[RMANによるリカバリ][4]
- RMAN (Recovery Manager)

バックアップにはユーザー管理、つまりOSコマンドなどでとるバックアップと、RMANという専用ツールを利用してとるバックアップが存在します。
今回はこのRMANを使っての手順を検証します。

[不完全リカバリ][8]
- 不完全リカバリ
- SCNの重要性

完全リカバリは壊れている部分だけを修復するリカバリです。
それに対して、不完全リカバリは過去のあるタイミングに切り戻すリカバリです。
今回はRPOの短縮ということで、完全にDBが破壊されバックアップから切り戻すことを想定しているため、不完全リカバリの実現を目指します。

そして、不完全リカバリを行う場合はコントロール・ファイル、SYSTEM 表領域、ユーザー表領域においてSCNをそろえなければDBをオープンすることができないということを理解してください。


# AWS EC2 + Oracle環境のバックアップの基本

AWS EC2にOracleをインストールしている環境でバックアップをとる場合、いくつかのアプローチが考えられます。

## AWS機能によるバックアップ
まずはAWSの機能の範囲での物理バックアップです。
インスタンスが動かないというだけならEBSをデタッチ・アタッチしてしまえば完全リカバリとなるわけですが、今回はEBS障害を前提とします。

OracleをEC2にインストールしている場合、当然そのすべてのファイルはEBS上に存在することになります。
そのため、EBSのスナップショットをとっておけば、それがそのままフルバックアップとして機能します。
また、EBSは増分バックアップになるので高頻度でバックアップをとることが可能です。
RPOは[DLMを使うことで1時間程度まで短縮][5]できると思われます。
※[cron式のスケジューリングにも対応しているようです][6]
ただし、AWSの機能で単純にバックアップをとるということは、ディスクI/O単位での管理しかできず、データベースのI/Oを無視したバックアップをとることになります。
この場合、[分離ブロック問題][1]が発生することになるため、スナップショットをとる瞬間はバックアップモードをONにすることを検討してください。

バックアップ戦略
- DBをアーカイブログモードで運用する。
- EBSのスナップショットを取得するタイミングではバックアップモードをONにする
- 定期的にEBSスナップショットを取得する。

リカバリ手順
- [EBSスナップショットを利用したリカバリ][0]

## Oracle機能によるバックアップ

DBのオープンにはSCNが制御ファイル・システム表領域・ユーザー表領域で一致しなければならないという制約があります。
そこで、不完全リカバリを行うためにはまず制御ファイルを除いたすべての領域を過去に戻すための論理バックアップ（エクスポート・ユーティリティ、Oracle Data Pump）を取得しておく必要があります。（フルバックアップでもいい）
制御ファイルにはRMANのバックアップ操作用データや切り戻し目標となるSCNも含まれるため、制御ファイルは論理バックアップよりも高頻度でバックアップを取っておく必要があるでしょう。

手順としてはOracleの機能で完結しているので単純にRMANを使ったホットバックアップと不完全リカバリを実行すればよいです。

バックアップ戦略
- DBをアーカイブログモードで運用する
- バックアップを取得するタイミングではバックアップモードをONにする
- 定期的に論理バックアップや制御ファイルのバックアップを取っておく
- 各バックアップを退避しておく

リカバリ手順
- [不完全リカバリ][9]
- ※アーカイブログなどを退避している場合、この手順に加えてバックアップファイルをRMANに登録する手順が必要になる（後述）

# 基本戦略への懸念点

ここまで挙げた方法にはそれぞれ懸念点が存在します。
もしこれらを問題と感じないなら、その手法を選んでもよいでしょう。

## EBSスナップショットの課題

EBSスナップショットをとる場合の費用は増分バックアップとなるため、データ更新量に依存することになります。
またデータベースは1つのDBFが複数回更新されることもあるため、RPOの短縮のために高頻度でスナップショットをとるならばその分だけ費用も上がりやすくなるでしょう。
また、たくさんの環境を同時にバックアップ取得し続けることになれば、[Rate exceeded][10]を高頻度で起こす危険性もあります。

## Oracleフルバックアップ+アーカイブログの課題

まずフルバックアップを残すということは、それだけEBSの容量を大きくとらなければならないということが問題となります。
これは費用面に大きな負担をかけることになります。
また、DBのバックアップを取得するのにかかる時間もEBSより多くかかります。
フルバックアップとアーカイブログをS3などに退避するということであれば、巨大な容量を誇るフルバックアップが生み出す負担はより大きなものとなると想像できます。
しかし、退避しないのであればEBS障害に復旧できなくなるため、バックアップの価値は薄くなってしまいます。

そもそも普通の運用を想像すれば定期的にAMIなどでバックアップをとっているわけで、そこに加えてOracleフルバックアップを定期的に取るというのは管理上無駄かつ複雑に感じます。

# 検証する仮説・要件

そこで、なるべくコストや時間面での負担を上げずに、RPOを短縮する方法はないのでしょうか、と考えたのが今回の本題になります。

ここまでの情報から、Oracleのアーカイブログ適用を行えればRPOを改善できるということはご理解いただけるかと思います。
しかし、EBSスナップショットを高頻度で取りたくはありません。
そして単純な不完全リカバリのためにフルバックアップを追加で取りたくもありません。

そもそもAMI(EBSスナップショット)自体にDBのすべてのファイルが含まれているんだから、これをフルバックアップとしてリカバリを行えないでしょうか。
できれば定期的に取得しているAMI(EBSスナップショット)にアーカイブログを適用する形でRPOを短縮できないでしょうか。

というわけで、以下を前提としてバックアップ戦略検討と不完全リカバリ検証をしてみました。
- Oracleのアーカイブログを使用したリカバリを検討
- DBは24時間稼働の環境も多いので、ホットバックアップが前提
- 1時間以内のRPO実現
- Oracleフルバックアップはとらない
- AMI（EBS）バックアップは低頻度

# バックアップ戦略

RPO1時間以内を目指すため、アーカイブログと制御ファイルのバックアップは1時間単位で取得し退避しておく必要があります。
ただし、アーカイブログがあれば変更のすべてを適用できるため、AMIは1日1回で十分になります。

- アーカイブログモードをONにして運用
- AMIバックアップは1日1回（バックアップモードをONにして取得）
- ※AMIを取得する時、systemctlなどでDBの自動起動設定をしていた場合は一時的に無効にする
- アーカイブログと制御ファイルバックアップをセットで1時間ごとにRMANで取得
- RMANでバックアップされたファイルを1時間ごとにS3にアップロード
- リカバリ時はAMI取得日付以降のアーカイブログをすべて適用する

※リカバリ時にAMIを切り戻したときにDBが起動してしまうと、時間経過でSCNが更新されてしまいアーカイブログの適用がうまくいかなくなる。
リカバリする場合は、AMIで切り戻した直後にDBが起動しないようにしておこう。

# DBの現在の状態を確認

```
#アーカイブログモード確認
SQL> archive log list
データベース・ログ・モード     アーカイブ・モード
自動アーカイブ                 有効
アーカイブ先                    /mnt/productdb/oradata/product/archive
最も古いオンライン・ログ順序   692
アーカイブする次のログ順序    694
現行のログ順序               694
```

```
#REDOログが多重化されている場合、どこに出力されているか
SQL> select group#,status,type,member from v$logfile;	
GROUP# STATUS  TYPE   MEMBER	
------ ------- ------ --------------------------------------------------	
1         ONLINE /mnt/productdb/oradata/product/log1_1product.log	
1         ONLINE /mnt/productdb/oradata/product/log1_2product.log	
2         ONLINE /mnt/productdb/oradata/product/log2_1product.log	
2         ONLINE /mnt/productdb/oradata/product/log2_2product.log	
3         ONLINE /mnt/productdb/oradata/product/log3_1product.log	
3         ONLINE /mnt/productdb/oradata/product/log3_2product.log	
```

```
#制御ファイルの場所
SQL> SHOW PARAMETER CONTROL FILES		
		
NAME                                 TYPE		
------------------------------------ ---------------------------------		
VALUE		
------------------------------		
control_file_record_keep_time        integer		
7		
control_files                        string		
"/mnt/productdb/oradata/product/ctrl01product.ctl, 
/mnt/productdb/oradata/product/ctrl02product.ctl,
 /mnt/productdb/oradata/product/ctrl03product.ctl
control_management_pack_access       string"		
none		
```

```
#RMANの設定
RMAN> show all;								
リカバリ・カタログのかわりにターゲット・データベース制御ファイルを使用しています								
db_unique_name productのデータベースにおけるRMAN構成パラメータ:								
CONFIGURE RETENTION POLICY TO REDUNDANCY 1; # default #不要な全体バックわっぷをいくつ保存するか		
CONFIGURE BACKUP OPTIMIZATION OFF; # default #バックアップ最適化　同じデバイスタイプにバックアップされない		
CONFIGURE DEFAULT DEVICE TYPE TO DISK; # default #自動チャネルのデフォルトデバイス		
CONFIGURE CONTROLFILE AUTOBACKUP ON; # default #制御ファイルの自動バックアップ	
CONFIGURE CONTROLFILE AUTOBACKUP FORMAT FOR DEVICE TYPE DISK TO '%F'; # default #バックアップ場所とファイル名の形式		
CONFIGURE DEVICE TYPE DISK PARALLELISM 1 BACKUP TYPE TO BACKUPSET; # default #バックアップセットのデフォルトデバイスタイプ		
CONFIGURE DATAFILE BACKUP COPIES FOR DEVICE TYPE DISK TO 1; # default #データファイルのデフォルトデバイスタイプ		
CONFIGURE ARCHIVELOG BACKUP COPIES FOR DEVICE TYPE DISK TO 1; # default #アーカイブログの同上		
CONFIGURE MAXSETSIZE TO UNLIMITED; # default #バックアップの最大サイズ		
CONFIGURE ENCRYPTION FOR DATABASE OFF; # default #データベース全体に対しての暗号化		
CONFIGURE ENCRYPTION ALGORITHM 'AES128'; # default #書き込み時の暗号化アルゴリズム		
CONFIGURE COMPRESSION ALGORITHM 'BASIC' AS OF RELEASE 'DEFAULT' OPTIMIZE FOR LOAD TRUE ; # default #圧縮の暗号化アルゴリズム
CONFIGURE RMAN OUTPUT TO KEEP FOR 7 DAYS; # default								
CONFIGURE ARCHIVELOG DELETION POLICY TO NONE; # default	#アーカイブログが削除可能になる条件		
CONFIGURE SNAPSHOT CONTROLFILE NAME TO '/u01/app/oracle/product/19.3.0/dbhome_1/dbs/snapcf_product.f'; # default #スナップショット制御ファイルの名前と場所
```

# DBリカバリ検証

## 事前準備

EC2インスタンスを起動し、Oracleをインストールしてデータベースをオープンしておいてください。

いつでも切り戻せるように、最初にAMIを取っておきましょう。

また、バックアップの退避先としてs3バケットを作成しておきましょう。

## バックアップモードを有効化
```
sudo su oracle
export ORACLE_SID=product
sqlplus / as sysdba
SQL> alter database begin backup;
```


<details><summary>分離ブロック問題を解決するための手順です。</summary>
分離ブロック問題は、バックアップをファイルのI/O単位で取得するのに対し、DBはDBのI/O単位を別で持っていることによって起きる問題です。
バックアップモードを有効化している間、DBへの書き込みはDBのI/O単位でREDOログにも記録されるようになります。
これにより、バックアップ取得時には分離ブロックが発生したとしても、アーカイブログを適用することでリカバリできるという仕組みです。
</details>

<details><summary>よくある勘違いとして、バックアップモード中はデータベースの静止点となるというものがありますが、これは間違いです。</summary>

バックアップモードはホットバックアップをとるための仕組みであるため、当然DBは静止状態になりません。
以下のようなSQLを業務スキーマで何度か叩いてみてください。
SCNが更新されていることが確認できるはずです。
```
sudo su oracle
export ORACLE_SID=product
sqlplus id/pw@product
SQL> select current_scn, scn_to_timestamp(current_scn) from v$database;
Oracle Database 19c Standard Edition 2 Release 19.0.0.0.0 - Production
Version 19.14.0.0.0
に接続されました。
SQL> select current_scn, scn_to_timestamp(current_scn) from v$database;

CURRENT_SCN
-----------
SCN_TO_TIMESTAMP(CURRENT_SCN)
---------------------------------------------------------------------------
42750855
22-06-14 3:27:14
```
</details>
ホットバックアップでリカバリを行う場合は、必ずアーカイブログを適用しましょう。

## DBの起動設定を無効化する。

AMiを切り戻したとき、DBがオープンしてしまうとSCNが更新されてしまい、アーカイブログの適用がうまくいかなくなる危険性があります。
リカバリが終わるまではDBが勝手にオープンしないよう、起動設定を行っている場合は無効化しましょう。

なお、user-dataで行っている場合もあるので、そちらも確認しておいてください。
```
systemctl disable dbora
```

## AMIの取得

この後DBを更新してアーカイブログを出力し、リカバリを検証します。
これはそのリカバリを行うためのフルバックアップになります。
必ずAMIを取得しておいてください。

## 起動設定を有効化し、DBを起動する

```
sudo su -
systemctl enable dbora.service
systemctl start dbora.service
```

## バックアップモードを無効化・DBオープン

私の手元の環境ではdbora.serviceをたたいてもオープンではなくマウントモードでの起動だったので、一応止めて起動する手順も入れています。

[Oracleの起動モードについて][11]

```
sudo su oracle
export ORACLE_SID=product
sqlplus / as sysdba
SQL> alter database end backup;
SHUTDOWN
STARTUP
```

## 【補足】REDOログを確認する準備

さて、リカバリ手順とは関係ありませんが、REDOログに書き込まれた内容からリカバリをおこなうということで、ちゃんとREDOログに今回の処理が記載されているのか確認したいことだと思います。
そこで、その手順も含めて紹介します。
実際のリカバリ手順では不要です。

まず今回の処理がどのREDOログにかきこまれるのか確認しましょう。
以下でCURRENTとなっているロググループに書き込まれるはずです。
今回はグループ3ですね。
```
#どのREDOログに現在書き込んでいるか確認
sqlplus / as sysdba
SQL> select * from v$log;
GROUP# THREAD# SEQUENCE#      BYTES  BLOCKSIZE MEMBERS ARCHIVED STATUS     FIRST_CHANGE# FIRST_TIME NEXT_CHANGE#
------ ------- --------- ---------- ---------- ------- -------- ---------- ------------- ---------- ------------
NEXT_TIM     CON_ID
-------- ----------
1       1         1  209715200        512       2 YES      INACTIVE        42647794 22-05-26       42749671
22-06-14          0

2       1         2  209715200        512       2 YES      INACTIVE        42749671 22-06-14       42852730
22-06-15          0

3       1         3  209715200        512       2 NO       CURRENT         42852730 22-06-15     9.2954E+18
```

ちなみにどのファイルに書き込んでるかは以下を参照します。
とりあえずどこにあるかわからなくなったら、これら全部見てみればOKです。
ちょうどログスイッチが入ってアーカイブログ送りになったとかない限りは見つけられるはずです。
```
#REDOログが多重化されている場合、どこに出力されているか
sqlplus / as sysdba
SQL> select group#,status,type,member from v$logfile;	
GROUP# STATUS  TYPE   MEMBER	
------ ------- ------ --------------------------------------------------	
1         ONLINE /mnt/productdb/oradata/product/log1_1product.log	
1         ONLINE /mnt/productdb/oradata/product/log1_2product.log	
2         ONLINE /mnt/productdb/oradata/product/log2_1product.log	
2         ONLINE /mnt/productdb/oradata/product/log2_2product.log	
3         ONLINE /mnt/productdb/oradata/product/log3_1product.log	
3         ONLINE /mnt/productdb/oradata/product/log3_2product.log	
```

REDOログの中身を見るには[ログマイナー][12]という機能を使用します。
ログマイナーを使用するには最小サプリメンタル・ロギングが有効である必要があります。
```
sqlplus / as sysdba
SQL> select SUPPLEMENTAL_LOG_DATA_MIN from v$database;

SUPPLEMENTAL_LOG_DATA_MI
------------------------
NO
```

最小サプリメンタル・ロギングを有効化します。
ちなみに、この最小サプリメンタル・ロギングを有効化している間の更新しか参照できません。
```
sqlplus / as sysdba
SQL> alter database add supplemental log data;
```

分析対象とするREDOログファイルを指定します。		
※今回はオンラインREDOログを指定。		
※exitしたら再設定が必要		
```
sqlplus id/pw@product
SQL> EXECUTE DBMS_LOGMNR.ADD_LOGFILE( LOGFILENAME => '/mnt/productdb/oradata/product/log3_1product.log', OPTIONS => DBMS_LOGMNR.NEW);	
PL/SQLプロシージャが正常に完了しました。
```

ログマイナー起動
※exitしたら再設定が必要
```
sqlplus id/pw@product
SQL> execute dbms_logmnr.start_logmnr(options =>　dbms_logmnr.dict_from_online_catalog);
PL/SQLプロシージャが正常に完了しました
```

## DBに適当な更新を行う

さて、DBへの更新は個人でテーブル構造とか違うと思うので、何かしら適当なINSERT文とCOMMITを投げてもらえればよいと思います。
恥ずかしいミスとして、COMMIT投げ忘れて、エラー無いのに全然リカバリがうまくいかないというあほなことをしました。
```
sqlplus id/pw@product
SQL> INSERT INTO NANKA_DETAIL(CD,KB,DATE,DATE2,ID,ID2,ID3,CHECKED,BIKOU,FLG,DATE3) VALUES ('3', 3,'1000-01-01','1000-01-01',3,3,3,3,'3','3','1000-01-01');
SQL> select * from NANKA_DETAIL;
SQL> COMMIT;
```

## 【補足】REDOログの書き込みを確認する。
以下のように書き込み先のテーブルやオペレーション等で検索することができます。
ちなみに、以下のSCNに戻すとCOMMITのSCNより前になるはずなので、リカバリしてもデータは戻らないことに注意してください。
ちゃんとCOMMIT後のSCNまでリカバリしましょう。
```
sqlplus id/pw@product
SQL> select SCN,TIMESTAMP,TABLE_NAME,OPERATION,SQL_REDO from v$logmnr_contents where operation='INSERT' and SQL_REDO like '%NANKA_DETAIL%';
SCN TIMESTAM	
---------- --------	
TABLE_NAME	
--------------------------------------------------------------------------------	
OPERATION	
--------------------------------------------------------------------------------	
SQL_REDO	
--------------------------------------------------------------------------------	
42855011 22-06-16	
SR_A_KB_TENKO_DETAIL	
INSERT	
insert into "id"."NANKA_DETAIL"(CD,KB,DATE,DATE2,ID,ID2,ID3,CHECKED,BIKOU,FLG,DATE3) VALUES ('3', 3,'1000-01-01','1000-01-01',3,3,3,3,'3','3','1000-01-01');
```

## 【補足】SCNを確認する
一応、SCNを調べる方法についても説明しておきます。
```
#タイムスタンプを指定して、scnを検索可能
sqlplus id/pw@product
SQL> SELECT TIMESTAMP_TO_SCN('2022061403:45:05') FROM DUAL ;	
	
TIMESTAMP_TO_SCN('2022061403:45:05')	
------------------------------------	
42752611	

#scnを指定してタイムスタンプを検索
sqlplus id/pw@product
SQL> select current_scn, scn_to_timestamp(current_scn) from v$database;

CURRENT_SCN
-----------
SCN_TO_TIMESTAMP(CURRENT_SCN)
---------------------------------------------------------------------------
42853543
22-06-14 4:09:35
```

## RMANでアーカイブ(REDO)ログ＋制御ファイルバックアップを取得

やっとリカバリ手順に戻ってきました。
アーカイブログと制御ファイルについてバックアップを取得します。
ピースハンドルと書かれているものがバックアップピース（バックアップセットの最小単位）の出力先になっています。
要はこれがバックアップです。

今回のコマンドを実行するだけで、RMANが勝手に現在のREDOログをアーカイブしてくれて、03105umc_3_1_1のような形でバックアップしてくれます。
それと同時に現時点の制御ファイルもc-676445785-20220616-00のような形でバックアップしてくれます。
ちなみに形式はRMANで読み込む専用の形式なので、このバックアップを使用したリカバリは必ずRMANを使用してください。
```
rman target /
RMAN> BACKUP ARCHIVELOG ALL;
backupを22-06-16で開始しています
現在のログがアーカイブされました。
リカバリ・カタログのかわりにターゲット・データベース制御ファイルを使用しています
チャネル: ORA_DISK_1が割り当てられました
チャネルORA_DISK_1: SID=283 デバイス・タイプ=DISK
チャネルORA_DISK_1: アーカイブ・ログ・バックアップ・セットを開始しています
チャネルORA_DISK_1: バックアップ・セットにアーカイブ・ログを指定しています
入力アーカイブ・ログ・スレッド=1 順序=698 レコードID=580 スタンプ=1103013633
入力アーカイブ・ログ・スレッド=1 順序=699 レコードID=581 スタンプ=1105694425
チャネルORA_DISK_1: ピース1 (22-06-16)を起動します
チャネルORA_DISK_1: ピース1 (22-06-16)が完了しました
ピース・ハンドル=/u01/app/oracle/product/19.3.0/dbhome_1/dbs/02105uku_2_1_1 タグ=TAG20220616T043126 コメント=NONE
チャネルORA_DISK_1: バックアップ・セットが完了しました。経過時間: 00:00:46
チャネルORA_DISK_1: アーカイブ・ログ・バックアップ・セットを開始しています
チャネルORA_DISK_1: バックアップ・セットにアーカイブ・ログを指定しています
入力アーカイブ・ログ・スレッド=1 順序=1 レコードID=582 スタンプ=1107314726
入力アーカイブ・ログ・スレッド=1 順序=2 レコードID=583 スタンプ=1107491263
入力アーカイブ・ログ・スレッド=1 順序=3 レコードID=584 スタンプ=1107491484
チャネルORA_DISK_1: ピース1 (22-06-16)を起動します
チャネルORA_DISK_1: ピース1 (22-06-16)が完了しました
ピース・ハンドル=/u01/app/oracle/product/19.3.0/dbhome_1/dbs/03105umc_3_1_1 タグ=TAG20220616T043126 コメント=NONE
チャネルORA_DISK_1: バックアップ・セットが完了しました。経過時間: 00:00:15
backupを22-06-16で終了しました

Control File Autobackupを22-06-16で開始しています
ピース・ハンドル=/u01/app/oracle/product/19.3.0/dbhome_1/dbs/c-676445785-20220616-00 コメント=NONE
Control File Autobackupを22-06-16で終了しました
```

ちなみに、[パラメータファイル][13]というものもあります。
pfileを使っていたりspfileを使っていたりと様々だと思いますが、必要であればバックアップをとっておいても良いと思います。
弊社ではgit管理してuser-dataでインスタンス起動時に毎回ダウンロードしてくるようになっているので無視できました。

## バックアップファイルのS3への退避
EBS障害発生時を考慮して、各バックアップはS3に退避しておきましょう。
これでAMIとアーカイブログ＋制御ファイルのバックアップを取得することができました。
```
cd /u01/app/oracle/product/19.3.0/dbhome_1/dbs/
aws s3 sync .  s3://archive-logs-test/environment/2022/06/16/01backup/ --exclude "*" --include "02105uku_2_1_1"
aws s3 sync .  s3://archive-logs-test/environment/2022/06/16/01backup/ --exclude "*" --include "03105umc_3_1_1"
aws s3 sync .  s3://archive-logs-test/environment/2022/06/16/01backup/ --exclude "*" --include "c-676445785-20220616-00"
```

## AMIの切り戻し

事前に取得しておいた更新前のAMIに切り戻しましょう。
先ほど実行したDB更新の内容分がまき戻った状態になりますが、アーカイブログにはバックアップ取得時以降のすべての更新がログに含まれているため、リカバリできるはずです。

弊社ではuser-dataでもDBの起動を行っていたので、systemctlに加えてuser-dataでの起動処理も無効化しました。
これについてはお手元の環境で問題なければ無視してかまいません。

## 起動設定をONにし、DBを起動する(非オープン)

弊社の環境ではMOUNTモードで起動しますのでこれでいいですが、そうでない場合はMOUNTモードで起動するように手順を読み替えてください。
```
sudo su -
systemctl enable dbora.service
systemctl start dbora.service
```

ちなみに、オープンしていない場合は業務スキーマにsqlplusでログインしてみた時にORA-01033: ORACLE initialization or shutdown in progressが出ると思いますので、簡単に試すことができます。
一応正規の確認SQLもあります。
```
export ORACLE_SID=product
sqlplus / as sysdba
SQL> select STATUS from v$instance;

STATUS
------------------------------------
MOUNTED
```

## バックアップモードを無効化

```
export ORACLE_SID=product
sqlplus / as sysdba
alter database end backup;
```

## S3からバックアップをコピー

```
cd /u01/app/oracle/product/19.3.0/dbhome_1/dbs/
aws s3 cp  s3://archive-logs-test/environment/2022/06/16/01backup/ ./ --recursive
```

## RMANにバックアップ登録

RMANはバックアップの管理や切り戻し可能な位置などを把握していますが、OS側でファイルを消したり戻したりした動作を解釈することができません。
そこで、先ほどコピーした未来のアーカイブログをRMANに登録する必要があります。
登録後、LIST BACKUP;コマンドによりRMANが認識している使用可能なバックアップ一覧を確認することができます。
```
rman target /
RMAN> CATALOG START WITH '02105uku_2_1_1';
RMAN> CATALOG START WITH '03105umc_3_1_1';
RMAN> CATALOG START WITH 'c-676445785-20220616-00';
RMAN> LIST BACKUP;
```

## DBをアンマウント
以前お話ししたように、バックアップ先のSCNを明示するために制御ファイルのSCNを未来のものに置き換える必要があります。
DBの起動モードでは、MOUNT状態だと制御ファイルを使用して動作する状態になっているため、一度シャットダウンしてNOMOUNTモードで起動しなおす必要があります。
```
RMAN> SHUTDOWN
RMAN> STARTUP NOMOUNT
```

## バックアップを使用した制御ファイルのリストア
RMANで取得しておいた未来の制御ファイルにリストアします。
これで制御ファイルについてはOKです。
```
RMAN> restore controlfile from '/u01/app/oracle/product/19.3.0/dbhome_1/dbs/c-676445785-20220616-00';
restoreを22-06-15で開始しています
チャネル: ORA_DISK_1が割り当てられました
チャネルORA_DISK_1: SID=20 デバイス・タイプ=DISK

チャネルORA_DISK_1: 制御ファイルをリストア中です
チャネルORA_DISK_1: リストアが完了しました。経過時間: 00:00:01
出力ファイル名=/mnt/productdb/oradata/product/ctrl01product.ctl
出力ファイル名=/mnt/productdb/oradata/product/ctrl02product.ctl
出力ファイル名=/mnt/productdb/oradata/product/ctrl03product.ctl
restoreを22-06-15で終了しました
```

## データベースをマウント	
制御ファイルのリストアが終わったので、あらためでDBをマウントモードに、制御ファイルを読み込みます。
```
RMAN> sql 'alter database mount';
```
	
## アーカイブログのリストア
[アーカイブログもリストア][15]します。
```
RMAN> RESTORE archivelog FROM TIME '22-06-16';
restoreを22-06-16で開始しています
チャネルORA_DISK_1の使用

チャネルORA_DISK_1: デフォルトの宛先へのアーカイブ・ログのリストアを開始しています
チャネルORA_DISK_1: アーカイブ・ログをリストアしています
アーカイブ・ログ・スレッド=1 順序=2
チャネルORA_DISK_1: アーカイブ・ログをリストアしています
アーカイブ・ログ・スレッド=1 順序=3
チャネルORA_DISK_1: バックアップ・ピース/u01/app/oracle/product/19.3.0/dbhome_1/dbs/03105umc_3_1_1から読取り中です
チャネルORA_DISK_1: ピース・ハンドル=/u01/app/oracle/product/19.3.0/dbhome_1/dbs/03105umc_3_1_1 タグ=TAG20220616T043126
チャネルORA_DISK_1: バックアップ・ピース1がリストアされました
チャネルORA_DISK_1: リストアが完了しました。経過時間: 00:00:01
restoreを22-06-16で終了しました
```

ここで一つ罠がありました
この後に実行する[RECOVERコマンド][14]の説明では以下のように書かれています。
```
RECOVERコマンドでは必要に応じてアーカイブREDOログ・ファイルが自動的にリストアされるため、手動によるリストアが必要になることはほとんどありません。
アーカイブREDOログ・ファイルを手動でリストアする可能性が発生するのは、リカバリ時間を短縮する場合、ログを複数の宛先に書き込む場合、Point-in-Timeリカバリ後にログの内容を分析する場合などです。
データベースを停止せずに以前のインカネーションからログをリストアする場合、次の句とともにRESTORE ARCHIVELOGを使用できます。
```
これを信用するなら、このステップでの処理は必要ありません。
しかし、実際にはこの処理をスキップするとリカバリーはうまくいきません。

理由としては、処理順序になります。
確かにRECOVERコマンドではアーカイブREDOログ・ファイルが自動的にリストアされるのですが、処理順番としてはリカバリ→リストアの順で実行されていました。
リカバリはアーカイブログを適用する行為、リストアはアーカイブログを配置しなおす行為です。
当たり前ですが、リカバリした後にリストアしても適用されるアーカイブログはリストア前のアーカイブログです。
なぜこうなっているのかはOracleへの知見が乏しく理解できておりませんが、とにかくログの出力を見ている感じそのように見受けられました。
DBのバージョンは11gではなかった気がするのでそのせいかな......まぁともかくアーカイブログのリストアは先にやっておきましょう。

## アーカイブログリカバリ
いよいよアーカイブログを使用したデータファイルのリカバリ、ロールフォワードの適用です。
今回はSCNを指定して、現在値からここまでを適用していますが、いろんな指定の仕方があるようなので需要に応じて手段を選んでください。
実際DB非オープンだとSCNとかよくわからんってなるので、タイムスタンプでリカバリするとかもあります。
```
RMAN> recover database until scn 42854000;	
	
recoverを22-06-16で開始しています	
チャネル: ORA_DISK_1が割り当てられました	
チャネルORA_DISK_1: SID=22 デバイス・タイプ=DISK	
	
メディア・リカバリを開始しています	
	
スレッド1 (順序2)のアーカイブ・ログは、ファイル/mnt/productdb/oradata/product/log2_1product.logとしてディスクにすでに存在します	
アーカイブ・ログ・ファイル名=/mnt/productdb/oradata/product/log2_1product.log スレッド=1 順序=2	
メディア・リカバリが完了しました。経過時間: 00:00:04	
チャネルORA_DISK_1: デフォルトの宛先へのアーカイブ・ログのリストアを開始しています	
チャネルORA_DISK_1: アーカイブ・ログをリストアしています	
アーカイブ・ログ・スレッド=1 順序=3	
チャネルORA_DISK_1: バックアップ・ピース/u01/app/oracle/product/19.3.0/dbhome_1/dbs/03105umc_3_1_1から読取り中です	
チャネルORA_DISK_1: ピース・ハンドル=/u01/app/oracle/product/19.3.0/dbhome_1/dbs/03105umc_3_1_1 タグ=TAG20220616T043126	
チャネルORA_DISK_1: バックアップ・ピース1がリストアされました	
チャネルORA_DISK_1: リストアが完了しました。経過時間: 00:00:01	
recoverを22-06-16で終了しました	
```
ログを見てわかるように、「メディア・リカバリが完了しました」という表示の後に「アーカイブ・ログのリストアを開始しています」という表示がされます。
そしてその後にリカバリを適用するログは出力されません。
そのため、アーカイブログのリストアは事前にやっておくことが必要です。

## データベースオープン
いよいよデータベースオープンです。
今回行ったのは不完全リカバリになります。
そのため、現在のREDOログは完全に不正な物になっています。
そこで、resetlogsオプションをつけてREDOログを再作成した状態でDBを起動する必要があることに注意してください。
```
RMAN> sql 'alter database open resetlogs';
```

これでリカバリは完了です。
DBが正しく更新後の内容になっているかを確認してください。
お疲れ様でした！！！
Oracle1から調べて全部手探りでここまで来るのめっちゃ大変でした！！！！！！
ほめて！！！！！！


## 【多分いらない】高速リカバリ領域のアーカイブログを削除
ここは全く検証してないしもしかしたら何ですけど、高速リカバリ領域にアーカイブログを出力している場合はリカバリ後に削除しておいたほうがいいかもしれません。

[不完全リカバリを甘く見るな][16]

ただ、多分リカバリ後にまた失敗したらそのまま再リカバリ実行せずAMIから切り戻すと思うので、関係ないとは思います。
一応のメモみたいな感じです。


# おわりに

というわけで、AWS EC2にOracleをインストールして運用している環境のホットバックアップ戦略と、AMI+アーカイブログによる不完全リカバリ検証でした。
Oracleわからなすぎてバチクソ大変でした。
いいからこれから始めるお前らはRDSつかっとけな？な？

この記事が良いと思った方はGoodボタン、高評価、チャンネル登録よろしくお願いします。
齋藤でした～



[0]:https://aws.amazon.com/jp/blogs/news/improving-oracle-backup-and-recovery-performance-with-amazon-ebs-multi-volume-crash-consistent-snapshots/
[1]:https://cosol.jp/techdb/2020/05/backup_oracle_mysql_postgresql/
[2]:https://www.oracle.com/jp/a/tech/docs/technical-resources/1215-1500-br-biginner.pdf
[3]:https://tech.mamezou00000.com/entry/oracle-archive-log
[4]:https://www.oracle.com/jp/a/tech/docs/technical-resources/br-user-vs-rman.pdf
[5]:https://blog.serverworks.co.jp/tech/2020/03/18/ebs-dlm/
[6]:https://dev.classmethod.jp/articles/dlm-cron-expression/
[7]:https://docs.oracle.com/cd/E16338_01/server.112/b56301/onlineredo.htm
[8]:http://system3support.jp/pdf/oracle_knowledge/backup/p187.pdf
[9]:http://soldoutdb.com/gold/backup/gold7.html
[10]:https://aws.amazon.com/jp/premiumsupport/knowledge-center/cloudtrail-rate-exceeded/
[11]:https://sql-oracle.com/?p=56
[12]:https://old.insight-tec.com/blog/mailmagazine-ora3-vol356/
[13]:https://www.searchman.info/oracle_backup/1030.html
[14]:https://docs.oracle.com/cd/E16338_01/backup.112/b56270/rcmsynta2001.htm#i84135
[15]:https://docs.oracle.com/cd/E16338_01/backup.112/b56270/rcmsubcl002.htm#i78730
[16]:https://xn--w8j8bac3czf5bl7e.com/2019/08/14/%E4%B8%8D%E5%AE%8C%E5%85%A8%E3%83%AA%E3%82%AB%E3%83%90%E3%83%AA%E3%82%92%E7%94%98%E3%81%8F%E8%A6%8B%E3%82%8B%E3%81%AA-%E3%81%9D%E3%81%AE%EF%BC%922-2/