RDS for Oracle環境でData Pumpを利用する

2017.04.21

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

西澤です。RDS for Oracle環境で、Data Pumpを試す機会があったので、まとめておきたいと思います。

Data Pumpとは?

Oracleのバックアップリストアツールと言えば、以前よりあるexp/impコマンドが有名ですが、10gより非推奨となっています。exp/impコマンドはクライアントツールだった為、サーバとクライアント間でのデータ転送が必要でしたが、Data Pumpはデータベースサーバ側で処理される為、性能が大きく改善されています。ただし、データベースサーバ側のディスクを利用する為、ディスク使用量には注意が必要です。

Oracle Data Pump は、Oracle のインストールから Amazon RDS DB インスタンスに大量のデータを移行する際に推奨される方法です。Oracle Data Pump は、次のようなさまざまなシナリオで使用できます。

  • Oracle データベースを使用した Amazon EC2 インスタンスから Oracle DB インスタンスにデータをインポートする
  • Oracle DB インスタンス上のデータベースから別の Oracle DB インスタンスにデータをインポートする
  • VPC 内の Oracle DB インスタンス上のデータベースから別の Oracle DB インスタンスにデータをインポートする (VPC の有無は問わない)
  • ローカルの Oracle データベースから Amazon RDS DB インスタンスにデータをインポートする

Amazon RDS での Oracle へのデータのインポート - Amazon Relational Database Service

Data Pumpを利用したバックアップ/リストアの概要

RDS for Oracle環境でもData Pumpを利用することができますが、データベースサーバの完全な管理者権限があるわけではありませんので、DBMS_DATAPUMPDBMS_FILE_TRANSFER等のプロシージャを利用する必要があります。今回は、RDS for Oracle同士でData Pumpを利用できるかを検証しました。本来であれば、RDSスナップショット機能を利用したバックアップ/リストアが手順も簡易で利用しやすいのですが、より高速にバックアップ/リストアをすることができないか調査の為にお客様からのお問合わせに基づき検証を行いました。

Oracle_DataPump

  1. oracledb1にてDBMS_DATAPUMPを利用してデータをDATA_PUMP_DIRにダンプファイルを出力
  2. oracledb1からoracledb2に対してデータベースリンクを作成
  3. oracledb1のDATA_PUMP_DIRからoracledb2のDATA_PUMP_DIRにダンプファイルを転送
  4. oracledb2にてDBMS_DATAPUMPを利用してDATA_PUMP_DIRのダンプファイルからリストア

RDS for Oracle環境でData Pumpを試してみた

それでは、順番に進めてみます。今回は下記のバージョンで試してみました。

select * from v$version;

BANNER
--------------------------------------------------------------------------------
Oracle Database 11g Release 11.2.0.4.0 - 64bit Production
PL/SQL Release 11.2.0.4.0 - Production
CORE 11.2.0.4.0 Production
TNS for Linux: Version 11.2.0.4.0 - Production
NLSRTL Version 11.2.0.4.0 - Production

5 rows selected

RDS環境ではDATA_PUMP_DIRを変更することはできませんので、デフォルトのパスが利用されます。

SELECT directory_name, directory_path FROM dba_directories
WHERE directory_name='DATA_PUMP_DIR';

DIRECTORY_NAME DIRECTORY_PATH
------------------------------ ----------------------
DATA_PUMP_DIR /rdsdbdata/datapump

1 rows selected

DBMS_DATAPUMPでエクスポート

書式に慣れるまでがちょっとわかりづらいですが、基本的には下記の通りに実行すればバックアップが取得可能です。ダンプファイルやログファイルを指定している"filename"とスキーマ名(下記例では"ADMIN"としています)だけ環境に合わせて修正してご利用ください。

DECLARE
hdnl NUMBER;
BEGIN
hdnl := DBMS_DATAPUMP.open( operation => 'EXPORT', job_mode => 'SCHEMA', job_name=>null);
DBMS_DATAPUMP.ADD_FILE( handle => hdnl, filename => 'tab1.dmp', directory => 'DATA_PUMP_DIR', filetype => dbms_datapump.ku$_file_type_dump_file);
DBMS_DATAPUMP.add_file( handle => hdnl, filename => 'exp.log', directory => 'DATA_PUMP_DIR', filetype => dbms_datapump.ku$_file_type_log_file);
DBMS_DATAPUMP.METADATA_FILTER(hdnl,'SCHEMA_EXPR','IN (''ADMIN'')');
DBMS_DATAPUMP.start_job(hdnl);
END;
/

anonymous block completed

ちなみに、スキーマ全体ではなく、特定のテーブルのみを対象として、エクスポートをする場合は下記のような指定(下記は、TABLE_A,TABLE_B,TABLE_Cのみを対象とする例)が可能でした。また、"NOT IN"を利用することで特定のテーブルを除外することもできるようです。

DECLARE
hdnl NUMBER;
BEGIN
hdnl := DBMS_DATAPUMP.open( operation => 'EXPORT', job_mode => 'SCHEMA', job_name=>null);
DBMS_DATAPUMP.ADD_FILE( handle => hdnl, filename => 'expdb_tables.dmp', directory => 'DATA_PUMP_DIR', filetype => dbms_datapump.ku$_file_type_dump_file);
DBMS_DATAPUMP.add_file( handle => hdnl, filename => 'expdb_tables.log', directory => 'DATA_PUMP_DIR', filetype => dbms_datapump.ku$_file_type_log_file);
DBMS_DATAPUMP.METADATA_FILTER( handle => hdnl, name => 'SCHEMA_EXPR', value => 'IN (SCHEMA_NAME)');
DBMS_DATAPUMP.METADATA_FILTER( handle => hdnl, name => 'NAME_EXPR', value => 'IN (TABLE_A,TABLE_B,TABLE_C)', object_path=>'TABLE');
DBMS_DATAPUMP.start_job(hdnl);
END;
/

DATA_PUMP_DIRの確認方法

ちなみに、作成されたログファイルもRDS内のディレクトリに作成される為、下記のように参照します。

# DATA_PUMP_DIR内のファイル一覧取得
SELECT * FROM TABLE(RDSADMIN.RDS_FILE_UTIL.LISTDIR('DATA_PUMP_DIR')) ORDER BY MTIME;

FILENAME TYPE FILESIZE MTIME
--------------------------------- ---------- ---------------------- ---------------
tab1.dmp file 188772352 17-04-05
exp.log file 30581 17-04-05
datapump/ directory 4096 17-04-05

3 rows selected
# ログファイルの確認方法
SELECT TEXT FROM TABLE(RDSADMIN.RDS_FILE_UTIL.READ_TEXT_FILE('DATA_PUMP_DIR','exp.log'));

TEXT
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
"ADMIN"."SYS_EXPORT_SCHEMA_01"を起動しています:
BLOCKSメソッドを使用して見積り中です...
オブジェクト型SCHEMA_EXPORT/TABLE/TABLE_DATAの処理中です
BLOCKSメソッドを使用した見積り合計: 245.2 MB
オブジェクト型SCHEMA_EXPORT/USERの処理中です
オブジェクト型SCHEMA_EXPORT/SYSTEM_GRANTの処理中です
オブジェクト型SCHEMA_EXPORT/ROLE_GRANTの処理中です
オブジェクト型SCHEMA_EXPORT/DEFAULT_ROLEの処理中です
オブジェクト型SCHEMA_EXPORT/PRE_SCHEMA/PROCACT_SCHEMAの処理中です
オブジェクト型SCHEMA_EXPORT/SEQUENCE/SEQUENCEの処理中です
オブジェクト型SCHEMA_EXPORT/TABLE/TABLEの処理中です
オブジェクト型SCHEMA_EXPORT/TABLE/COMMENTの処理中です
オブジェクト型SCHEMA_EXPORT/TABLE/INDEX/INDEXの処理中です
オブジェクト型SCHEMA_EXPORT/TABLE/INDEX/FUNCTIONAL_INDEX/INDEXの処理中です
オブジェクト型SCHEMA_EXPORT/TABLE/CONSTRAINT/CONSTRAINTの処理中です
オブジェクト型SCHEMA_EXPORT/TABLE/INDEX/STATISTICS/INDEX_STATISTICSの処理中です
オブジェクト型SCHEMA_EXPORT/TABLE/INDEX/STATISTICS/FUNCTIONAL_INDEX/INDEX_STATISTICSの処理中です
オブジェクト型SCHEMA_EXPORT/TABLE/CONSTRAINT/REF_CONSTRAINTの処理中です
オブジェクト型SCHEMA_EXPORT/TABLE/TRIGGERの処理中です
オブジェクト型SCHEMA_EXPORT/TABLE/STATISTICS/TABLE_STATISTICSの処理中です
. . "ADMIN"."TABLE1" 46.36 MB 6259行がエクスポートされました
. . "ADMIN"."TABLE2" 46.50 MB 5463行がエクスポートされました
. . "ADMIN"."TABLE3" 19.79 MB 8151行がエクスポートされました
. . "ADMIN"."TABLE4" 0 KB 0行がエクスポートされました
:::
マスター表"ADMIN"."SYS_EXPORT_SCHEMA_01"は正常にロード/アンロードされました
******************************************************************************
ADMIN.SYS_EXPORT_SCHEMA_01に設定されたダンプ・ファイルは次のとおりです:
/rdsdbdata/datapump/tab1.dmp
ジョブ"ADMIN"."SYS_EXPORT_SCHEMA_01"が水 4月 5 13:31:18 2017 elapsed 0 00:01:41で正常に完了しました

301 rows selected

データベースリンク作成

oracledb2側で事前に作成したユーザを利用してoracledb1からデータベースリンクを作成します。今回は管理者ユーザとして作成したadminをそのまま使ってしまいました。

create database link dst_rds connect to admin identified by UserPassword
using '(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=oracledb2.xxxxxxxxxxxx.us-west-2.rds.amazonaws.com)(PORT=1521))(CONNECT_DATA=(SID=ORCL)))';

create database linkが正常に実行されました。

ダンプファイル転送

ダンプファイルは作成したデータベースリンクを介して、oracledb1からDBMS_FILE_TRANSFERパッケージを利用して転送を行います。

BEGIN
DBMS_FILE_TRANSFER.PUT_FILE(
source_directory_object => 'DATA_PUMP_DIR',
source_file_name => 'tab1.dmp',
destination_directory_object => 'DATA_PUMP_DIR',
destination_file_name => 'tab1_copied.dmp',
destination_database => 'dst_rds'
);
END;
/

anonymous block completed

しばらく待ってから、oracledb2側でダンプファイルがDATA_PUMP_DIRに配置されたことを確認します。

SELECT * FROM TABLE(RDSADMIN.RDS_FILE_UTIL.LISTDIR('DATA_PUMP_DIR')) ORDER BY MTIME;

FILENAME TYPE FILESIZE MTIME
---------------------------- ---------- ---------------------- -------------------------
datapump/ directory 4096 17-04-05
tab1_copied.dmp file 188772352 17-04-05

2 rows selected

DBMS_DATAPUMPでインポート

ここまで来ればもう操作はほとんど同じです。oracldb2側でDBMS_DATAPUMPパッケージを利用してインポートを行います。

DECLARE
hdnl NUMBER;
BEGIN
hdnl := DBMS_DATAPUMP.open( operation => 'IMPORT', job_mode => 'SCHEMA', job_name=>null);
DBMS_DATAPUMP.ADD_FILE( handle => hdnl, filename => 'tab1_copied.dmp', directory => 'DATA_PUMP_DIR', filetype => dbms_datapump.ku$_file_type_dump_file);
DBMS_DATAPUMP.add_file( handle => hdnl, filename => 'imp.log', directory => 'DATA_PUMP_DIR', filetype => dbms_datapump.ku$_file_type_log_file);
DBMS_DATAPUMP.METADATA_FILTER(hdnl,'SCHEMA_EXPR','IN (''ADMIN'')');
DBMS_DATAPUMP.start_job(hdnl);
END;
/

anonymous block completed

インポート結果を確認しておきましょう。

SELECT TEXT FROM TABLE(RDSADMIN.RDS_FILE_UTIL.READ_TEXT_FILE('DATA_PUMP_DIR','imp.log'));

TEXT
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
マスター表"ADMIN"."SYS_IMPORT_SCHEMA_01"は正常にロード/アンロードされました
"ADMIN"."SYS_IMPORT_SCHEMA_01"を起動しています:
オブジェクト型SCHEMA_EXPORT/USERの処理中です
ORA-31684: オブジェクト型USER:"ADMIN"はすでに存在します
オブジェクト型SCHEMA_EXPORT/SYSTEM_GRANTの処理中です
ORA-31685: 権限が不十分なため、オブジェクト型SYSTEM_GRANT:"ADMIN"が失敗しました。エラー文は次のとおりです:
GRANT EXEMPT REDACTION POLICY TO "ADMIN" WITH ADMIN OPTION
ORA-31685: 権限が不十分なため、オブジェクト型SYSTEM_GRANT:"ADMIN"が失敗しました。エラー文は次のとおりです:
GRANT ALTER DATABASE LINK TO "ADMIN" WITH ADMIN OPTION
ORA-31685: 権限が不十分なため、オブジェクト型SYSTEM_GRANT:"ADMIN"が失敗しました。エラー文は次のとおりです:
GRANT ALTER PUBLIC DATABASE LINK TO "ADMIN" WITH ADMIN OPTION
ORA-31685: 権限が不十分なため、オブジェクト型SYSTEM_GRANT:"ADMIN"が失敗しました。エラー文は次のとおりです:
GRANT EXEMPT IDENTITY POLICY TO "ADMIN" WITH ADMIN OPTION
ORA-31685: 権限が不十分なため、オブジェクト型SYSTEM_GRANT:"ADMIN"が失敗しました。エラー文は次のとおりです:
GRANT EXEMPT ACCESS POLICY TO "ADMIN" WITH ADMIN OPTION
オブジェクト型SCHEMA_EXPORT/ROLE_GRANTの処理中です
ORA-31685: 権限が不十分なため、オブジェクト型ROLE_GRANT:"ADMIN"が失敗しました。エラー文は次のとおりです:
GRANT "CONNECT" TO "ADMIN" WITH ADMIN OPTION
ORA-31685: 権限が不十分なため、オブジェクト型ROLE_GRANT:"ADMIN"が失敗しました。エラー文は次のとおりです:
GRANT "RESOURCE" TO "ADMIN" WITH ADMIN OPTION
ORA-31685: 権限が不十分なため、オブジェクト型ROLE_GRANT:"ADMIN"が失敗しました。エラー文は次のとおりです:
GRANT "AQ_USER_ROLE" TO "ADMIN" WITH ADMIN OPTION
ORA-31685: 権限が不十分なため、オブジェクト型ROLE_GRANT:"ADMIN"が失敗しました。エラー文は次のとおりです:
GRANT "RECOVERY_CATALOG_OWNER" TO "ADMIN" WITH ADMIN OPTION
ORA-31685: 権限が不十分なため、オブジェクト型ROLE_GRANT:"ADMIN"が失敗しました。エラー文は次のとおりです:
GRANT "CTXAPP" TO "ADMIN" WITH ADMIN OPTION
オブジェクト型SCHEMA_EXPORT/DEFAULT_ROLEの処理中です
オブジェクト型SCHEMA_EXPORT/PRE_SCHEMA/PROCACT_SCHEMAの処理中です
オブジェクト型SCHEMA_EXPORT/SEQUENCE/SEQUENCEの処理中です
オブジェクト型SCHEMA_EXPORT/TABLE/TABLEの処理中です
オブジェクト型SCHEMA_EXPORT/TABLE/TABLE_DATAの処理中です
. . "ADMIN"."TABLE1" 46.36 MB 6259行がインポートされました
. . "ADMIN"."TABLE2" 46.50 MB 5463行がインポートされました
. . "ADMIN"."TABLE3" 19.79 MB 8151行がインポートされました
. . "ADMIN"."TABLE4" 0 KB 0行がインポートされました
:::
オブジェクト型SCHEMA_EXPORT/TABLE/INDEX/INDEXの処理中です
オブジェクト型SCHEMA_EXPORT/TABLE/INDEX/FUNCTIONAL_INDEX/INDEXの処理中です
オブジェクト型SCHEMA_EXPORT/TABLE/CONSTRAINT/CONSTRAINTの処理中です
オブジェクト型SCHEMA_EXPORT/TABLE/INDEX/STATISTICS/INDEX_STATISTICSの処理中です
オブジェクト型SCHEMA_EXPORT/TABLE/INDEX/STATISTICS/FUNCTIONAL_INDEX/INDEX_STATISTICSの処理中です
オブジェクト型SCHEMA_EXPORT/TABLE/CONSTRAINT/REF_CONSTRAINTの処理中です
オブジェクト型SCHEMA_EXPORT/TABLE/TRIGGERの処理中です
オブジェクト型SCHEMA_EXPORT/TABLE/STATISTICS/TABLE_STATISTICSの処理中です
ジョブ"ADMIN"."SYS_IMPORT_SCHEMA_01"が完了しましたが、11エラーが水 4月 5 14:44:06 2017 elapsed 0 00:01:10で発生しています

316 rows selected

システム系のテーブルは権限不足でエラーとなっていましたが、移行したかったテーブル情報は全て移行されていることを確認できました。

最初の注意に記載した通り、RDS側にファイルが生成されることになる為、ゴミ掃除も忘れずやっておきましょう。例えば、エクスポートログを削除する場合は下記のようになります。

EXEC UTL_FILE.FREMOVE('DATA_PUMP_DIR','exp.log');

anonymous block completed

まとめ

OracleのDBMS_DATAPUMPプロシージャを利用したエクスポート/インポートを試してみました。始めは少々とっつきにくい印象でしたが、データの受け渡し性能が高いことは間違いないので、やってみると非常に便利であることがわかりました。やっぱり手を動かすの大事ですね。

どこかの誰かのお役に立てば嬉しいです。