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

Amazon RDS

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

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