Oracle DataGuard

# Oracle Dataguard

以下の構成の前提でOracle Data Guard を構築する。

<strong>`OS ホスト名`</strong>

    * プライマリ：ora1
    * スタンバイ：ora2

<strong>`DB_UNIQUE_NAME`</strong>

    * 両方ともに DG

<strong>`Oracle Netサービス名`</strong>

    * プライマリ向け: PROD
    * スタンバイ向け: DR


## 1.構築前事前準備

DataGuard の通信の受け口になるネットサービス名をまずはローカルネーミングで登録する。
RMAN での `duplicate database` コマンド時、及び、アーカイブログの転送で受け口となる。
なお、プライマリ/スタンバイ両方で同一の定義しておかないと、RMAN での `duplicate database` コマンド時に内部的にプライマリ/スタンバイ間で双方向通信するようなのでエラーとなってしまうため注意。

```ruby:①-1_ネットサービス名登録(プライマリ/スタンバイ共通)(tnsnames.ora)
PROD =
  (DESCRIPTION =
    (ADDRESS = (PROTOCOL = TCP)(HOST = ora1)(PORT = 9999))
    (CONNECT_DATA =
      (SERVER = DEDICATED)
      (SERVICE_NAME = DG)
      (UR=A)
    )
  )
DR =
  (DESCRIPTION =
    (ADDRESS = (PROTOCOL = TCP)(HOST = ora2)(PORT = 9999))
    (CONNECT_DATA =
      (SERVER = DEDICATED)
      (SERVICE_NAME = DG)
      (UR=A)
    )
  )
```
(参考) NOMOUNT状態のスタンバイDBにつなぐためには(UR=A)が必須

```ruby:①-2_リスナーへの静的サービス登録(プライマリ/スタンバイ共通)(listener.ora)
DATAGUARD =
  (DESCRIPTION_LIST =
    (DESCRIPTION =
      (ADDRESS = (PROTOCOL = TCP)(HOST = ora1 or ora2)(PORT = 9999))
 )
)
SID_LIST_DATAGUARD =
        (SID_LIST =
        (SID_DESC =
        (SID_NAME = DG)
        (ORACLE_HOME =  /opt/app/oracle/product/dbhome_1/19.3.0)
        )
)
```

```ruby:①-3_強制ロギング有効化(プライマリ側)
alter database force logging;
```

```ruby:①-4_スタンバイREDOログファイル作成(プライマリ側)
--スタンバイREDO作成。



--スタンバイREDOを確認。1行以上出力があれば作成されている状態と判断。
COL STATUS FOR A20
SELECT GROUP#,THREAD#,SEQUENCE#,ARCHIVED,STATUS FROM V$STANDBY_LOG;
```

```ruby:①-5_sysdbaで繋げるようにパスワードファイル作成(プライマリ/スタンバイ共通)
orapwd file=orapwDG password=P#ssw0rd entries=10 force=y format=19.3
```

```ruby:①-6_適切な初期化パラメータ設定(プライマリ側)
alter system set LOG_ARCHIVE_CONFIG='DG_CONFIG=(PROD,DR)'scope=spfile;
alter system set LOG_ARCHIVE_DEST_STATE_2='ENABLE' scope=spfile;
alter system SET LOG_ARCHIVE_DEST_2='service=DR LGWR ASYNC NOAFFIRM delay=0 compression=disable max_failure=0 reopen=5 db_unique_name=DR net_timeout=30 valid_for=(online_logfile,all_roles)' scope=spfile;
alter system set STANDBY_FILE_MANAGEMENT='AUTO' scope=spfile;
alter system set FAL_SERVER='DR' scope=spfile;
alter system set FAL_CLIENT='PROD' scope=spfile;
alter system set REMOTE_LOGIN_PASSWORDFILE=EXCLUSIVE scope=spfile;
alter system set LOG_ARCHIVE_MAX_PROCESSES=2 scope=spfile;
```
```ruby:①-6_適切な初期化パラメータ設定(スタンバイ側(プライマリ側との差異は以下のRMANコマンドで自動的にスタンバイ構築時に反映可能))
alter system set LOG_ARCHIVE_CONFIG='DG_CONFIG=(PROD,DR)'scope=spfile;
alter system set LOG_ARCHIVE_DEST_STATE_2='ENABLE' scope=spfile;
alter system set log_archive_dest_2="service=PROD ASYNC NOAFFIRM delay=0 compression=disable max_failure=0 reopen=5 db_unique_name=PROD net_timeout=30 valid_for=(online_logfile,all_roles)" scope=spfile;
alter system set STANDBY_FILE_MANAGEMENT='AUTO' scope=spfile;
alter system set FAL_SERVER='PROD' scope=spfile;
alter system set FAL_CLIENT='DR' scope=spfile;
alter system set REMOTE_LOGIN_PASSWORDFILE=EXCLUSIVE scope=spfile;
alter system set LOG_ARCHIVE_MAX_PROCESSES=2 scope=spfile;
```
* (ご参考)初期化パラメータ: https://docs.oracle.com/cd/E82638_01/sbydb/creating-oracle-data-guard-physical-standby.html#GUID-0FD317D6-1A49-4DB3-A141-D00CB3089E84
* COMPRESSION オプションは、Advanced Compression オプションが必要。
* LOG_ARCHIVE_MAX_PROCESSESは通常レプリケーション先+1でOK

## 2.スタンバイデータベース作成(プライマリ側)(RMANアクティブデータベースコピー)
```ruby:インスタンス作成sample
rman TARGET SYS/P#ssw0rd@PROD AUXILIARY SYS/P#ssw0rd@DR;

DUPLICATE TARGET DATABASE FOR STANDBY FROM ACTIVE DATABASE DORECOVER
   SPFILE 
      SET DB_UNIQUE_NAME='DG'
      SET FAL_SERVER='PROD'
      SET FAL_CLIENT='DR'
      SET LOG_ARCHIVE_DEST_2='service=PROD ASYNC NOAFFIRM delay=0 compression=disable max_failure=0 reopen=5 db_unique_name=DG net_timeout=30 valid_for=(online_logfile,all_roles)' 
   NOFILENAMECHECK;
```

* duplicate先にすでにspfileが存在するとエラー(`RMAN-05537: DUPLICATE without TARGET connection when auxiliary instance is started with spfile cannot use SPFILE clause`)
* CRSのリソース登録されているとエラーになる。(`ORA-65500: could not modify DB_UNIQUE_NAME, resource exists`)
*  NOFILENAMECHECKは、プライマリ・スタンバイサーバの同一パスに制御ファイル、REDOログ、データファイルを配置する場合は付与要。
* フラッシュデータベースはソースDBで有効化されていてもターゲットDBで初期状態として無効化される


## 3.スタンバイREDO作成

リアルタイム適用する場合は作成

```ruby:スタンバイREDOログ作成。
-- プライマリで実行して出力した以下のSQL実行結果をスタンバイで実行。
select 'alter database add standby logfile group ' || 
to_number((select count(*) from v$log) + rownum) || '(''/opt/app/oracle/oradata/dg/REDO0' || 
to_number((select count(*) from v$log) + rownum) || '.LOG'') SIZE ' || 
(select max( bytes/1024/1024) from v$log) || 'M;' as standbyredo
from v$log
union all
select 'alter database add standby logfile group ' || 
((select max(group#) from v$log) + (select count(*) from v$log) + 1) || '(''/opt/app/oracle/oradata/dg/REDO0' || 
((select max(group#) from v$log) + (select count(*) from v$log) + 1) || '.LOG'') SIZE ' || 
(select max( bytes/1024/1024) from v$log) || 'M;' 
from dual;
```
* スタンバイREDOログは、REDOログと同じサイズである必要があり、REDOログよりも少なくとも1つ多くのスタンバイログのグループが必要。この条件を満たさないとリアルタイム適用ができず、単純にプライマリでログスイッチが発生した際にそのREDOがスタンバイに転送され、その内容が適用されるのでスタンバイへの反映が遅れる
* 参考URL
https://www.it-swarm-ja.tech/ja/oracle-11g-r2/%E6%96%B0%E3%81%97%E3%81%84%E3%83%87%E3%83%BC%E3%82%BF%E3%81%8C%E3%83%97%E3%83%A9%E3%82%A4%E3%83%9E%E3%83%AA%E3%83%87%E3%83%BC%E3%82%BF%E3%83%99%E3%83%BC%E3%82%B9%E3%81%AB%E6%8C%BF%E5%85%A5%E3%81%95%E3%82%8C%E3%81%9F%E3%81%A8%E3%81%8D%E3%81%AB%E3%80%81%E3%83%87%E3%83%BC%E3%82%BF%E3%81%8C%E3%82%B9%E3%82%BF%E3%83%B3%E3%83%90%E3%82%A4%E3%83%87%E3%83%BC%E3%82%BF%E3%83%99%E3%83%BC%E3%82%B9%E3%81%AB%E5%8F%8D%E6%98%A0%E3%81%95%E3%82%8C%E3%82%8B%E3%81%BE%E3%81%A7%E3%81%AB%E3%81%8B%E3%81%8B%E3%82%8B%E6%A8%99%E6%BA%96%E7%9A%84%E3%81%AA%E6%99%82%E9%96%93%E9%81%85%E5%BB%B6%E3%81%AF%E3%81%A9%E3%82%8C%E3%81%8F%E3%82%89%E3%81%84%E3%81%A7%E3%81%99%E3%81%8B%E3%80%82/l966400523/



## 4.スタンバイデータベース作成後の状態確認、及び、REDO APPLY

```ruby:インスタンスのロールを確認
COL DB_UNIQUE_NAME FOR A20
COL DATABASE_ROLE FOR A20
COL OPEN_MODE FOR A30
SELECT DB_UNIQUE_NAME,DATABASE_ROLE,OPEN_MODE FROM V$DATABASE;
```

```ruby:REDO-APPLYの起動・停止・状態確認(スタンバイ側実行)
-- フォアグラウンド起動(起動状態=上記OPEN_MODEがスタンバイDB側でREAD ONLY WITH APPLY)
ALTER DATABASE RECOVER MANAGED STANDBY DATABASE;

-- バックグラウンド起動(起動状態=上記OPEN_MODEがスタンバイDB側でREAD ONLY WITH APPLY)
ALTER DATABASE RECOVER MANAGED STANDBY DATABASE DISCONNECT FROM SESSION;

-- 停止(停止状態=上記OPEN_MODEがスタンバイDB側でREAD ONLY)
ALTER DATABASE RECOVER MANAGED STANDBY DATABASE CANCEL;

-- 状態確認(MPR0プロセスの状態)
SELECT PROCESS, STATUS FROM V$MANAGED_STANDBY WHERE PROCESS LIKE 'MRP%';

-- プライマリ側でアーカイブREDOログ化されたものがスタンバイ側に転送されて逐次適用される。適用完了自体がAPPLIED列から判別可能
col NAME format a80;
col APPLIED format a15;
SELECT SEQUENCE#,NAME,FIRST_TIME, NEXT_TIME,APPLIED FROM V$ARCHIVED_LOG ORDER BY SEQUENCE#;

-- 表示されたLOW~HIGHの番号のアーカイブREDOが適用されていない
SELECT * FROM V$ARCHIVE_GAP;

-- FAL 機構関連初期化パラメータをスタンバイDB側で設定することで自動でアーカイブギャップは解消する。定期的に転送されていないREDOが無いかプライマリ側に確認しにいってくれる
FAL_SERVER=<プライマリDBへのネットサービス名>
```

* DB再起動により、REDO-APPLYは停止状態に遷移
* MOUNTモードかつREDO-APPLY起動中にopenは出来ない。インスタンスをopenしたい場合は、一度REDO-APPLYを停止→open→REDO-APPLYを起動とする。ただ、スタンバイDBのデータの読み取りにはライセンスとして Active Data Guard が必要。

```
SQL> alter database open;
alter database open
*
ERROR at line 1:
ORA-10456: cannot open standby database; media recovery session may be in progress
```

## 4.フェイルオーバー

```ruby:スイッチオーバー実行手順
1.プライマリDBがスタンバイロールに変換可能か確認(プライマリ側)
select switchover_status from v$database;
SWITCHOVER_STATUS
--------------------
TO STANDBY

2.プライマリDBをスタンバイロールにスイッチオーバー(プライマリ側)
alter database commit to switchover to standby with session shutdown;

3.プライマリDBをスタンバイロールとして起動(プライマリ側)
shutdown abort;
startup mount;
select database_role from v$database;
DATABASE_ROLE
----------------
PHYSICAL STANDBY

4.スタンバイDBをプライマリロールに変換可能か確認(スタンバイ側)
select switchover_status from v$database;
SWITCHOVER_STATUS
--------------------
TO PRIMARY

5.スタンバイDBをプライマリロールとして起動(スタンバイ側)
alter database commit to switchover to primary with session shutdown;
alter database open;
select database_role from v$database;
DATABASE_ROLE
----------------
PRIMARY

6.Redo Applyを開始(スタンバイ側)
```

```ruby:フェイルオーバー実行手順
1.プライマリDBからスタンバイDBに未送信のREDO全部送付(プライマリ側)
ALTER SYSTEM FLUSH REDO TO <target_db_name>;
※target_db_nameは送信先のdb_unique_name

2.Redo-Applyの適用状態確認(スタンバイ側)
col NAME format a80;
col APPLIED format a15;
SELECT SEQUENCE#,NAME,FIRST_TIME, NEXT_TIME,APPLIED FROM V$ARCHIVED_LOG ORDER BY SEQUENCE#;

3.Redo-Apply停止(スタンバイ側)
ALTER DATABASE RECOVER MANAGED STANDBY DATABASE CANCEL;

4-1.残存REDOを全て適用しきる(スタンバイ側)
ALTER DATABASE RECOVER MANAGED STANDBY DATABASE FINISH;
→4-1実施の場合は5に遷移

4-2.即時にプライマリDBへと移行する(スタンバイ側)
ALTER DATABASE ACTIVATE PHYSICAL STANDBY DATABASE;
→4-2実施の場合は7に遷移。この場合その時点で未適用のREDOは無視される。
未適用REDO分のデータはロストする形になるので実施するかは要件による。この処理を実施するとDBはマウントモードになる。

5.プライマリに遷移可能かを確認(スタンバイ側)
SELECT SWITCHOVER_STATUS FROM V$DATABASE;
SWITCHOVER_STATUS
-----------------
TO PRIMARY

6.プライマリDBへとスイッチオーバー(スタンバイ側)
ALTER DATABASE COMMIT TO SWITCHOVER TO PRIMARY WITH SESSION SHUTDOWN;

7.データベースOPEN(スタンバイ側)

8.データベースバックアップ(スタンバイ側)
```

```ruby:保護モード確認
select protection_mode from v$database;

PROTECTION_MODE
------------------------------------------------------------
MAXIMUM PERFORMANCE

-- MAXIMUM PROTECTION   - 保護モード
-- MAXIMUM AVAILABILITY - 可用性モード
-- MAXIMUM PERFORMANCE  - パフォーマンスモード
-- RESYNCHRONIZATION    - 再同期化モード
-- UNPROTECTED          - 保護対象外
--                        (プライマリがマウント済だがオープンされていない場合に発生)

モード変更
alter database set standby database to MAXIMIZE<???>;
```

dgmgrlでの Data Guard 管理


```
dgmgrl sys/P#ssw0rd@prod << EOF
disable configuration;
remove configuration;
create configuration 'Broker' as primary database is PROD connect identifier is PROD;
ADD DATABASE DR AS CONNECT IDENTIFIER IS DR;
enable configuration;
edit database prod set property StaticConnectIdentifier = 'PROD';
edit database dr set property StaticConnectIdentifier = 'DR';
EOF
```