Oracle Database Active Data guardを使ってみた（ノウハウ）

# はじめに
仕事でOracle DatabaseのActive Data Guardを使ったので、
そのノウハウ、メモ書きです。

OracleDBのバージョンは11.2.0.3とちょっと古めです。
またオンプレミスからAWSのEC2に同期して、さらにもう1段同期するというカスケード構成を作りました。

# Data Guard / Active Data Guardとは
Data GuardはOracleDBのEnterprise Editionに含まれているレプリケーション機能です。
フィジカル・スタンバイの場合は、REDOをプライマリDBからスタンバイDBに転送、スタンバイDBに適用します。（仕事ではこちらを使いました。）
ロジカル・スタンバイの場合は、プライマリDBで実行されたSQL文をスタンバイDBでも実行します。
Data GuardのスタンバイDBは、マウント状態で中身を参照することはできませんが、
Active Data GuardではスタンバイDBをRead Onlyでオープンでき、中身を参照することができます。
なお、Active Data Guardの利用には別途ライセンスが必要です。

# ADGカスケード構成
ADGによるレプリケーションはカスケードすることができます。
例えば、プライマリDB→スタンバイDB①→スタンバイDB②という構成です。
カスケードできるのは30個までらしいです。（そんなにしないと思いますが・・・）

なぜこのような構成にしたかと言うと、
・プライマリDB@オンプレは本番稼働中なのでめったに触れない。
・でもこのDBで色々テストをする必要がある
・プライマリDB→スタンバイDB①のADG構成を作るために本番作業が必要+長時間のサービス影響が出る
などなどの理由によりスタンバイDB②を作り、ここで諸々のテストを実施しました。
（時にはスタンバイDB①から新たにスタンバイDB②を作り直したりも・・）
※スナップショットスタンバイが使えたのでは、という気持ちは無くはないです。

あとリアルタイムに同期されるのは1段目（プライマリDBと直接通信しているスタンバイDB）だけで、
2段目以降のスタンバイDBは、非同期更新となります。（REDO切り替わりのタイミングで反映）
※12c以降はリアルタイムでもできるようになっているとの噂。

# スタンバイDBの作成
スタンバイDBを作成する時は、プライマリDBのRMANバックアップから作成(複製)したり、
もしくはFROM ACTIVE DATABASE句を利用して、稼働中のDBから直接複製するのが定石です。
例えカスケード先のスタンバイDBであっても。。

ですが、スタンバイDBから別のスタンバイDBを複製することもできます。
ただし、その場合はFROM ACTIVE DATABASEは使えず（バージョンや、パッチ適用状況によっては使える）
バックアップから複製することになります。
(Step by Step method to create Primary/Standby Database from Standby Backup ( Doc ID 1604251.1 ) や、
Active duplicate should work when connected to Standby as source DB ( Doc ID 11715084.8 ) に、
その情報があるっぽいけど、今MOSのアカウントが有効じゃないので見えず・・）

複製する際の注意点です。

## 制御ファイルをスタンバイDB用にする
いまDBがプライマリかスタンバイかは、制御ファイルに書いてあります。
なので、プライマリDBの制御ファイルをコピーしてもそのままでは使えず、スタンバイDB用の制御ファイルにする必要があります。
とはいえ、コマンド一発です。

```sql:スタンバイ制御ファイルの作成
SQL> ALTER DATABASE CREATE STANDBY CONTROLFILE AS '/tmp/boston.ctl';
```

参考→[スタンバイ・データベース用の制御ファイルの作成](https://docs.oracle.com/cd/E16338_01/server.112/b56302/create_ps.htm#i68937)

## DB_FILE_NAME_CONVERT / LOG_FILE_NAME_CONVERT
プライマリDBとスタンバイDBのディレクトリ構成が異なる時にこれらの初期化パラメータを使って、
パス・ファイル名の変換を行います。
DB_FILE_NAME_CONVERTの対象はDBファイルとtempファイル。
LOG_FILE_NAME_CONVERTの対象はオンラインREDOログファイルだけです。アーカイブREDOログやスタンバイREDOログは含まれません。

また変換ルールに制限はありませんが、値に文字数制限があります。（確か255文字だったような）

スタンバイDBのバックアップから複製しても、プライマリDBのパスが残っていることがあります。（原因不明・・・）
なので、その場合でもプライマリDBからの変換ルールも記載しておいた方が安全です。

複製後にDB_FILE_NAME_CONVERTを削除するとDBをオープンできなくなります。
たぶん制御ファイルの中には元のDBのままのパスで記載されており、
それをDB_FILE_NAME_CONVERTで読み替えているのかな。。と思われます。

RMANの`SET NEWNAME FOR DATAFILE`+`SWITCH DATAFILE ALL;`をするとDB_FILE_NAME_CONVERTは不要で、
こちらは制御ファイルの中身を書き換えているようです。
参考→[SWITCH](https://docs.oracle.com/cd/E16338_01/backup.112/b56270/rcmsynta2020.htm)

## 複製後のrecoveryで失敗してOpenできない・・
rman duplicateは、restoreして、recoveryを実行します。
自分の経験だとほとんどの場合recoveryが失敗して、整合性エラーでDBがオープンできません。
プライマリDBからアーカイブREDOログを持ってきて、適用してあげればそのうちオープンできるようにできますが、
それよりはfal_serverを設定した上でopenすると、勝手に必要なアーカイブREDOログをfalサーバから持ってきて、
必要なところまで適用してオープンすることができます。
その時falサーバ側のLOG_ARCHIVE_CONFIGにDG_CONFIGを設定しておく必要があります。
tnsnames.oraにfalサーバのエントリも必要です。

```shell:LOG_ARCHIVE_CONFIGの記載例
LOG_ARCHIVE_CONFIG='DG_CONFIG=(chicago,boston)'
```

# 保護モード
DG/ADGの保護モードには3種類あります。[Data Guardの保護モード](https://docs.oracle.com/cd/E16338_01/server.112/b56302/protection.htm)
**最大保護**はスタンバイDBのスタンバイREDOログへの書き込みを以ってコミット完了とします。そのため性能影響を受けます。
またスタンバイDBが停止するとコミット完了できなくなります。
**最大パフォーマンス**では、トランザクションはプライマリDB内で完結し、スタンバイDBへのREDO転送は非同期で行われます。（これがデフォルトです。）
**最大可用性**は通常時は最大保護と同じ挙動をしますが、スタンバイDBとの通信が途絶えると最大パフォーマンスと同じ挙動を取ります。

保護モードはREDO転送属性（LOG_ARCHIVE_DEST_nで指定）と密接な関係があります。
具体的にはAFFIRM/NOAFFIRMとSYNC/ASYNCです。保護モードに合わせた属性を指定する必要があります。
参考↓
[表5-1 データ保護モードに対するREDO転送の必須属性](https://docs.oracle.com/cd/E16338_01/server.112/b56302/protection.htm#CHDHFHJI)
[LOG_ARCHIVE_DEST_n](https://docs.oracle.com/cd/E16338_01/server.112/b56311/initparams125.htm)

# REDO転送のチューニング
プライマリDB@オンプレからスタンバイDB@AWSへのREDO転送を効率的に行うため、チューニングをしました。

## REDO圧縮
LOG_ARCHIVE_DEST_nのCOMPRESSIONを使い、REDO圧縮を行いました。
ただしこのオプションを使うには別途拡張圧縮オプションが必要です。

## オンラインREDOログのサイズ拡張
小さいREDOログだと頻繁にログスイッチが発生してしまうため、
REDOログのサイズを大きくしました。
ただ大きすぎるとなかなかログスイッチが発生しなくなるため、
ARCHIVE_LAG_TARGETで定期的にログスイッチが発生するようにしました。（5分間隔）

## buffer_sizeを変更
プライマリDB側(listener.ora)とスタンバイDB側(tnsnames.ora)でbuffer_sizeを増やしておきます。
それぞれSEND_BUF_SIZE/RECV_BUF_SIZEがあります。
参考→[I/Oバッファ・スペースの構成](https://docs.oracle.com/cd/E16338_01/network.112/b56288/performance.htm)

# ロール変換
DG/ADGでは、プライマリDB、スタンバイDBのそれぞれのロール(プライマリ/スタンバイ)を切り替えることで、
スイッチオーバー、フェイルオーバーを実施します。
スイッチオーバーは計画的なロール変換を指し、フェイルオーバーはプライマリDB障害時の(強制)ロール変換を指します。

## スイッチオーバー
スイッチオーバーは以下のような流れで行います。

1. プライマリDBにてswitch overコマンドを実行してロールをスタンバイに変更します。の時にプライマリREDOからEnd-of-REDOがスタンバイDBに送られます。

2. スタンバイDBでEnd-of-REDOを受信し、適用するとプライマリへのロール変換が可能になります。
その状態は`SELECT SWITCHOVER_STATUS FROM V$DATABASE;`で確認できます。
これの結果が`TO_PRIMARY`か`SESSIONS ACTIVE`であればスイッチオーバが可能です。
それ以外(NOT ALLOWEDなど)の時はスイッチオーバできないので、手動などうにかEnd-of-REDOまでを適用してください。
→カスケードした状態で、スタンバイDB①のMRPを止めた後にプライマリDBでスイッチオーバコマンドを実行したため、
スタンバイDB②までEnd-of-REDOが転送されず、スタンバイDB②をプライマリに昇格できなかった。なんてこともありました。。。

3. 2の結果ロール変換が可能な状態であれば、スタンバイDBでロール変換を実施。新プライマリDBをOpenする。

4. 問題なくスイッチオーバが完了したら、新プライマリDBから新スタンバイDBへのREDO転送設定を施す。

という感じになります。

スイッチオーバー手順の流れの詳細はこちら→[フィジカル・スタンバイ・データベースへのスイッチオーバーの実行](https://docs.oracle.com/cd/E16338_01/server.112/b56302/role_management.htm#i1026464)

## フェイルオーバー
フェイルオーバーはスタンバイDBへのREDO転送・適用状況により手順が変わってきます。

1. プライマリDBでの操作が可能で、スタンバイDBへのFLUSH REDOができれば、それでも問題なし
1. プライマリDBでの操作はできないが、アーカイブREDOログにアクセスできるのであれば、手動でスタンバイDBに転送し適用する
1. アーカイブREDOログにもアクセスできない場合は、データ消失を承知の上、フェイルオーバーに進む

のどれかになります。

1と2の場合は、`ALTER DATABASE COMMIT TO SWITCHOVER TO PRIMARY WITH SESSION SHUTDOWN;`を実行してフェイルオーバーが可能です。
3の場合は、`ALTER DATABASE ACTIVATE PHYSICAL STANDBY DATABASE;`を実行して強制的にスタンバイDBをプライマリDBとします。

フェイルオーバー手順の流れの詳細はこちら→[フィジカル・スタンバイ・データベースへのフェイルオーバーの実行](https://docs.oracle.com/cd/E16338_01/server.112/b56302/role_management.htm#i1026491)

# Active Data Guardに関する小ネタ
## スタンバイDBのOpenの仕方
スタンバイDBがアイドル状態からSQL*PlusのSTARTUPコマンドを実行すると、自動的にRead Onlyモードでオープンされます。

またマウント状態からOpenするには、一度MRPを止めてからOpenして、再度MRPを起動します。

```SQL
SQL> ALTER DATABASE RECOVER MANAGED STANDBY DATABASE CANCEL;
SQL> ALTER DATABASE OPEN;
SQL> ALTER DATABASE RECOVER MANAGED STANDBY DATABASE USING CURRENT LOGFILE DISCONNECT;
```

## オープンモードを確認
`SELECT open_mode FROM V$DATABASE;`を実行するとオープンモードが確認できます。
OPENしただけの状態では、`READ ONLY`となり、参照モード（変更は反映されない）。
その後MRPを起動すると、`READ ONLY WITH APPLY`になり、プライマリDBの変更内容が参照できるモード（リアルタイム問合せモード）。

## 同期ラグを確認
`V$DATAGUARD_STATS`を参照して、同期ラグを確認することができます。
大事なのは`APPLY LAG`（適用遅延）と`TRANSPORT LAG`（転送遅延）
適用遅延には転送遅延も含まれるので、常に`APPLY LAG`>`TRANSPORT LAG`となるはずですが、
`TRANSPORT LAG`>`APPLY LAG`となることがあります。
多分転送は終わっているけど、適用はまだしていない分の遅延が`APPLY LAG`には反映されていないのかなと想像します。
（`APPLY LAG`はREDOを適用しようとして初めてどれくらいプライマリDBから遅れているかを認識するものかと）

各カラム詳細はこちら→[V$DATAGUARD_STATS](https://docs.oracle.com/cd/E16338_01/server.112/b56311/dynviews_1104.htm)

知らなかったけど適用ラグのヒストグラム(`V$STANDBY_EVENT_HISTOGRAM`)とかあるらしいです。

## スタンバイDBでのデータファイルの追加（これはADGに限った話ではないですが。）
初期化パラメータ`STANDBY_FILE_MANAGEMENT`がAUTOになっていて、
DB_FILE_NAME_CONVERTが適切に設定されていれば、プライマリDBで作成されたデータファイルは、
スタンバイDBでも作成されます。

ですが、想定していない場所や`STANDBY_FILE_MANAGEMENT`がMANUALだった場合は、
UNNAMEDxxx(xxxはfile#)というファイルが作られ、同期が止まってしまいます。
そうなったら、スタンバイDBで手動でデータファイルを作成し、同期を再開させる必要があります。

データファイルを作成（再作成、リネーム）するには`STANDBY_FILE_MANAGEMENT`をMANUALにした状態で、
`alter database create datafile 'UNNAMEDxxxのフルパス' as '正しい位置のデータファイルのフルパス';`
と実行します。
これにより意図した場所にデータファイルが作成され、MRPを起動することで同期が再開されます。

## スタンバイDBを別のプライマリDBとして利用する
あんまりそういう場面はないかと思いますが。。。
同期に関する諸々の設定(プライマリDBのLOG_ARCHIVE_DEST_nだったり、LOG_ARCHIVE_CONFIGだったり）や、
スタンバイDBのMRPを停止した状態で、
`ALTER DATABASE ACTIVATE PHYSICAL STANDBY DATABASE;`を実行すると、
そのスタンバイDBは、プライマリDBから切り離され、別のプライマリDBとして扱われます。
（アーカイブREDOのシーケンスも1(0だったかな)からスタートします。）

将来DBを複製する必要がある時に、これを使って予めDBを用意・同期しておいて、
複製するタイミングで同期を切り離す。という使い方ができるかもしれません。
（切り離すときは一瞬で終わり、ほぼ事前に複製の準備がしておけるのがメリット。）

# おわりに
さすがOracle謹製のツールだけあって、ADGはかなり堅いツールであった印象です。
実際にはRACだったり、ASMだったりの組み合わせで複雑な構成だったのにも関わらず、問題なく稼働してくれました。