RMANバックアップからトランスポータブル表領域セットを作成し、Oracle Cloud Database Serviceに移行する

## 概要
オンプレミス環境にあるOracle Databaseをアクティブなデータファイルにアクセスせず、RMANバックアップからトランスポータブル表領域セットを作成し、Oracle Cloud Infrastructure(以下、OCI) Database Service VM(以下、DBCS)に移行する手順を確認・検証します。

この移行方法のメリットには下記のようなものがあります。

* 移行元の表領域を`READ ONLY`にする必要が無い
* アクティブなデータファイルにアクセスする必要が無い(本番環境へのI/Oの影響を抑えられる)
* ソース・データベースとターゲット・データベースで、互換性のあるデータベース・キャラクタ・セットを使用していれば異なるバージョン間で移行できる
* エンディアンが同じであれば、データ変換無しで異なるバージョン間で移行できる

トランスポータブル表領域を使用したOracle Database 19cへのデータの移行は、多くのバージョンから移行可能ですが詳細については下記のドキュメントをご確認下さい。

* [プラットフォーム間でのデータ・トランスポート](https://docs.oracle.com/cd/F19136_01/admin/transporting-data.html#GUID-FE3003B9-605A-4269-B167-005AC778C870)
* [データのトランスポートに関する一般的な制限事項](https://docs.oracle.com/cd/F19136_01/admin/transporting-data.html#GUID-28800719-6CB9-4A71-95DD-4B61AA603173)
* [データのトランスポートの互換性に関する注意事項](https://docs.oracle.com/cd/F19136_01/admin/transporting-data.html#GUID-AC5C8D9D-796A-4EDE-AC35-AF963ECCFE5C)

なおOracle GoldenGateを組み合わせることで、この移行を実施した後のデータを同期させることができます。
下記の図のように、ソースDBでのRMANバックアップ取得の前にデータ・キャプチャを開始しておき、ターゲットDBに表領域をインポートした後に差分適用をすることで、移行作業に伴うダウンタイムを極小化できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1199005/24295a22-22c3-ff3a-348a-7ebed2d100f3.png "Oracle GoldenGateを利用した移行")



## 検証環境
今回はOCI上のComputeインスタンスにインストールされたOracle Databaseで稼働するデータベースをオンプレミス環境に見立てて、DBCSに移行を行います。
OCIの大阪リージョンをソース(移行元)、東京リージョンのDBCSをターゲット(移行先)とします。
またOracle Databaseには最新のPSU, RUを適用します。

| |環境|Oracle Databaseバージョン|表領域名|暗号化|
|:-|:-|:-|:-|:-|
|**ソース(移行元)**|OCI Compute上のOracle DB(大阪リージョン)|12.1.0.2 (non-CDB構成)|users|無し|
|**ターゲット(移行先)**|OCI DBCS VM(東京リージョン)|19c (CDB構成)|users|移行後に暗号化|

一般的にはデータベースをPublic Subnetに置くことは少ないですが、今回は検証のためこのようにしています。
![oci.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1199005/47a8ca05-6c7c-1f09-d6d6-93f3ed8c01c3.png "検証環境の構成")


### スペック
| |シェイプ|OCPU数|メモリ|ストレージ|
|:-|:-|:-|:-|:-|:-|
|**ソース(移行元)**|VM.Standard.E3.Flex|4|32GB|Block Volume (Higher Performance)<br>400GB x 3|
|**ターゲット(移行先)**|VM.Standard2.4|4|60GB|2048GB|

### 使用するデータ
検証で使用するデータは[Swingbench](https://www.dominicgiles.com/swingbench.html)で作成します。
下記のようにデータを作成すると、`DBA_SEGMENTS`で約1048GBのデータが作成される事が確認できます。

```
./oewizard \
-cs //12c-ee.publicsubnet.testvcn.oraclevcn.com:1521/testdb \
-dba "sys as sysdba" -dbap password \
-u scott -p tiger \
-ts users \
-scale 1000 \
-part \
-nocompress \
-create \
-cl \
-ts users \
-noindexes \
-v &
```
```
SQL> SELECT owner,
            SUM(bytes/1024/1024/1024) "BYTES(GB)"
       FROM dba_segments
      WHERE owner = 'SCOTT'
   GROUP BY owner;

OWNER    BYTES(GB)
------- ----------
SCOTT   1048.61005
```

このデータを用いて移行に掛かる時間も計測します。尚、各工程ごとの時間は記事最後尾にまとめて記載しています。

## 表領域のチェック
トランスポートする表領域に、その表領域に含まれていない表の索引などが無いか(自己完結型の表領域セットであるか)をチェックします。
`DBMS_TTS`パッケージの`TRANSPORT_SET_CHECK`プロシージャを実行し、`TRANSPORT_SET_VIOLATIONS`ビューをSELECTすることで確認することができます。

```
SQL> EXECUTE DBMS_TTS.TRANSPORT_SET_CHECK('users', TRUE);
PL/SQL procedure successfully completed.

SQL> SELECT * FROM TRANSPORT_SET_VIOLATIONS;
no rows selected	/* ←何も選択されなければ自己完結型の表領域セットです */
```

## ソース側データベースのバックアップ
RMANでソース側データベースのバックアップを取得します。
このバックアップは通常の運用て取得しているバックアップでもかまいません。
下記のようなコマンドでバックアップを取得します。

```
RMAN> run {
allocate channel ch1 device type disk format='/mnt/backup/testdb12c_%U';
backup database include current controlfile plus archivelog;
BACKUP SPFILE format '/mnt/backup/testdb12c_spfile_%U';
}
```
ここで取得した時点のデータで、後程トランスポータブル表領域セットとして作成します。

## RMANバックアップからのトランスポータブル表領域セットの作成
取得したバックアップからトランスポータブル表領域セットを作成します。
その際、RMANが表領域のリストアおよびリカバリを実行するために補助インスタンスを作成するため、下表のような設定が必要です。

||設定内容|
|:-|:-|
|**TABLESPACE DESTINATION**|トランスポータブル表領域セットが出力される場所|
|**AUXILIARY DESTINATION**|補助インスタンスに必要なファイルが出力される場所|

上記の場所はそれぞれトランスポータブル表領域セットが出力される場所と、補助インスタンスによるリストアおよびリカバリを実行する際に使用されるため、十分な空き領域が必要です。

`TABLESPACE DESTINATION`と`AUXILIARY DESTINATION`の場所が決まったら、`TRANSPORT TABLESPACE`コマンドを実行してトランスポータブル表領域セットを作成します。
バックアップからトランスポータブル表領域セットを作成するため、指定した時間やSCNがバックアップ取得以降のものだと `RMAN-06024: no backup or copy of the control file found to restore` となり実行できません。取得したバックアップのチェックポイントSCN、時間を確認して、PITR(Point-in-Timeリカバリ)可能なポイントを指定するようにします。

また途中、`RMAN-05026: WARNING: presuming following set of tablespaces applies to specified Point-in-Time` が出力されますが問題ありません。

```
RMAN> TRANSPORT TABLESPACE users
TABLESPACE DESTINATION '/mnt/rman_tts_dest'
AUXILIARY DESTINATION '/mnt/rman_tts_aux'
UNTIL SCN 13476996;
```
補助インスタンスが自動的に起動しリストアとリカバリが実行され、オブジェクト・メタデータをエクスポートするためにData Pumpが実行されます。
RMANによる`TRANSPORT TABLESPACE`が完了すると補助インスタンスと補助インスタンスに関するファイルは自動的に削除され`TABLESPACE DESTINATION`で指定した場所に下記のようなファイルが出力されます。

|ファイル|用途|
|:-|:-|
|dmpfile.dmp|オブジェクト・メタデータ<br>データファイルと共にData Pumpでインポートする際に利用する|
|impscrpt.sql|トランスポータブル表領域セットをインポートする際のサンプルスクリプト<br>(今回は利用しません)|
|xxxxx.dbf|トランスポータブル表領域のデータファイル<br>表領域のデータファイルの数だけ出力される|

`TABLESPACE DESTINATION`で指定した場所に出力されたファイル全てを、ターゲット(移行先)であるDBCSがアクセスできる場所に転送します。
ここでは移行先の `/fs/rman_tts` ディレクトリをファイルの転送先とします。

ファイルはDBCSのファイルシステム上に保存しても良いですが、ファイルサイズが大きい場合は [Oracle Cloud Infrastructure File Storage Service](https://docs.oracle.com/ja-jp/iaas/Content/File/Concepts/filestorageoverview.htm "ファイル・ストレージの概要 - Oracle Cloud Infrastructureドキュメント") を利用して、DBCSのOSからマウントした大容量記憶域にファイルを保存します。

## ターゲットDBでのユーザ作成
表領域をインポートする際、その表領域に含まれるオブジェクトのオーナーユーザを予め作成しておく必要があります。
ソース(移行元)DBで下記のようなSQLを実行し、表領域内オブジェクトのオーナーを確認します。

```
SQL> SELECT DISTINCT owner FROM dba_tables WHERE tablespace_name = 'USERS';

OWNER
------
SCOTT
```

ソースDBで確認したユーザをターゲットDBで作成します。

```
SQL> CREATE USER scott IDENTIFIED BY tiger;
User created.

SQL> GRANT connect, resource TO scott;
Grant succeeded.
```

## ディレクトリ・オブジェクトの作成
トランスポータブル表領域セットをインポートするためには、インポートするオブジェクト・メタデータ(dmpfile.dmp)が格納されているディレクトリを示すディレクトリ・オブジェクトを、DB上で作成しておく必要があります。
Data Pumpインポートを実行する際には`SYSTEM`ユーザで実行するため、ディレクトリ・オブジェクトの`READ, WRITE`権限は`SYSTEM`ユーザに付与します。

```
SQL> CREATE DIRECTORY tts_dir AS '/fs/rman_tts';
Directory created.

SQL> GRANT read, write ON DIRECTORY tts_dir TO system;
Grant succeeded.
```

## 表領域のインポート
Data Pumpインポートを使用してターゲットDBに表領域をインポートします。
`dumpfile`にはオブジェクト・メタデータ、`transport_datafiles`にはデータファイルを指定します。
`remap_tablespace`を指定することで、ソースDBでの表領域名とは別の名前の表領域としてインポートすることができます。下記の例では`users`表領域から`users2`表領域に名前を変えてインポートしています。

```
impdp system/password@10.0.20.3:1521/testdb_pdb1.publicsubnet.testvcn.oraclevcn.com \
   directory=tts_dir \
   dumpfile='dmpfile.dmp' \
   transport_datafiles= \
     '/fs/rman_tts/o1_mf_users_j50lm1g2_.dbf', \
     '/fs/rman_tts/o1_mf_users_j50lm1gk_.dbf' \
   remap_tablespace=users:users2 \
   logfile=tts_import_dbcs.log
```

インポートした表領域を確認します。
`USERS2`表領域が`READ ONLY`となっており、暗号化されずに存在していることがわかります。
この暗号化されていない表領域は後程、暗号化します。

```
SQL> SELECT tablespace_name, status, encrypted FROM dba_tablespaces;

TABLESPACE_NAME                STATUS    ENC
------------------------------ --------- ---
SYSTEM                         ONLINE    YES
SYSAUX                         ONLINE    YES
UNDOTBS1                       ONLINE    YES
USERS                          ONLINE    YES
TEMP                           ONLINE    YES
USERS2                         READ ONLY NO
```

## インポートした表領域のデータファイルの移動
インポートした表領域のデータファイルは元々データファイルのあった場所のままとなっているため、データファイルの場所を移動します。

```
SQL> SELECT tablespace_name, file_name FROM dba_data_files ORDER by tablespace_name, file_name;

TABLESPACE FILE_NAME
---------- -------------------------------------------------------------------------------------
SYSAUX     +DATA/TESTDB_NRT1X5/BE4313B0D3113EDBE05302140A0AED34/DATAFILE/sysaux.271.1068012569
SYSTEM     +DATA/TESTDB_NRT1X5/BE4313B0D3113EDBE05302140A0AED34/DATAFILE/system.276.1068012561
UNDOTBS1   +DATA/TESTDB_NRT1X5/BE4313B0D3113EDBE05302140A0AED34/DATAFILE/undotbs1.272.1068012577
USERS      +DATA/TESTDB_NRT1X5/BE4313B0D3113EDBE05302140A0AED34/DATAFILE/users.275.1068012549
USERS2     /fs/rman_tts/o1_mf_users_j50lm1g2_.dbf
USERS2     /fs/rman_tts/o1_mf_users_j50lm1gk_.dbf
```

下記のようなコマンドでデータファイルを移動できます。
元々データファイルのあった場所からはデータファイルは無くなり、指定した新しい場所に移動されます。

```
-- ALTER DATABASE MOVE DATAFILE文を出力するSQL
SELECT 'ALTER DATABASE MOVE DATAFILE ''' || file_name || ''' TO ''+DATA'';' 
  FROM dba_data_files
 WHERE tablespace_name = 'USERS2'
 ORDER by file_name;

-- データファイルをASM上(+DATA)に移動
ALTER DATABASE MOVE DATAFILE '/fs/rman_tts/o1_mf_users_j50lm1g2_.dbf' TO '+DATA';
```

## 表領域の暗号化
DBCSではデータの暗号化を[強く推奨](https://docs.oracle.com/ja-jp/iaas/Content/Security/Reference/dbaas_security.htm "データベースの暗号化とキーの管理 - Oracle Cloud Infrastructureドキュメント")しているため表領域を暗号化します。
暗号化はオンラインでも実行可能ですが、ここでは変換に補助領域が不要なオフラインでの暗号化をします。
下記のコマンドで表領域をOFFLINEにした後、オフライン変換で暗号化します。

```
-- 表領域のOFFLINE
SQL> ALTER TABLESPACE users2 OFFLINE;

-- 表領域をオフライン変換で暗号化
SQL> ALTER TABLESPACE users2 ENCRYPTION OFFLINE ENCRYPT;
```
なお、DBCS上のOracle Database 19cでは暗号化されていない表領域を新規に作成することはできませんが、トランスポータブル表領域をインポートする場合は暗号化されていなくても実行できます。
詳細な動作については [Oracle Database Tablespace Encryption Behavior in Oracle Cloud (Doc ID 2359020.1)](https://support.oracle.com/epmos/faces/DocContentDisplay?id=2359020.1) をご確認下さい。

暗号化が完了したら表領域を`ONLINE`にし、`READ WRITE`にします。
また必要に応じて表領域を使用するユーザにQUOTAを設定します。

```
-- 表領域のONLINE
SQL> ALTER TABLESPACE users2 ONLINE;

-- 表領域のREAD WRITE
SQL> ALTER TABLESPACE users2 READ WRITE;

-- ユーザへのQUOTA設定
SQL> ALTER USER scott QUOTA UNLIMITED ON users2;
```

## 掛かった時間
各作業工程での実行時間です。
使用するリージョンやシェイプの構成などで移行に掛かる時間は変わりますので、参考情報としてご留意頂ければと思います。

|工程|時間|
|:-|:-|
|ソースDBでのRMANバックアップ|38分27秒|
|RMANバックアップからのトランスポータブル表領域セットの作成|1時間57分12秒|
|トランスポータブル表領域セットをターゲットホストにSCP転送(大阪 -> 東京)|3時間12分26秒|
|トランスポータブル表領域のインポート|1分30秒|
|インポートした表領域のデータファイルの移動|4時間17分6秒<br>(1データファイルあたり約7分50秒)|
|インポートした表領域のオフライン暗号化|3時間14分33秒|

**2021年4月9日追記**
[Exadata Cloud Serviceでの時間計測](https://qiita.com/ymaejima/items/f1083b0d24891ea32750 "Exadata Cloud Serviceでトランスポータブル表領域セットをインポートし暗号化する")も記事にしました。