[AWS] Data Pump のダンプファイルをRDS for Oracle インスタンスへ転送する

こんにちは、菊池です。

まずは、こちらをご覧ください。

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

Oracle Databaseのデータをインポート/エクスポートするツール、Data Pumpを使ったRDS間でデータ移行を紹介しています。マネージドサービスであるRDSでは、DBをホストしているインスタンスのOSレイヤにアクセスする手段がありません。そのためRDSインスタンスへダンプファイルを転送するために、転送元/転送先のDB同士をDBLinkで接続しています。

この方法が可能であれば問題ありませんが、転送元/転送先のDBが直接通信可能である必要がありますので、環境上の制約により難しいケースもあると思います。オンプレミスからの移行では、AWS上のRDSに直接通信が到達できないこともあるでしょう。

そこで今回は、Data Pumpでエクスポートしたダンプファイルを、RDSインスタンスにインポートさせる手法を紹介します。やり方は、以下のホワイトペーパーにて紹介されています。

ファイルとして外部に出力することで、S3を経由する方法や、複数の環境へインポートしたい場合にも取り回しが容易になります。

前提環境

上記のように、EC2に保存されたData PumpのダンプファイルをRDS(Oracle)のインスタンスにインポートします。

  • EC2:Amazon Linux 2017.09.1 64bit HVM
  • RDS:Oracle Database 12c SE2

ダンプファイルのインポート

ダンプファイルのインポート方法は、前述のホワイトペーパーに記載されています。

EC2上で Oracle UTL_FILE パッケージを利用し、DATA_PUMP_DIRディレクトリオブジェクトにコピーするためのPerlスクリプトを実行しています。

大まかな手順は以下の通りです。

  • 実行環境の準備
  • スクリプトの作成・実行
  • 確認

実行環境の準備

まずは前提パッケージをインストールします。

$ sudo yum install -y perl-ExtUtils-MakeMaker perl-DBI gcc 

続いて、Oracle Instant ClientのパッケージをOracle公式よりダウンロードし、展開します。以下のパッケージをダウンロードします。

  • instantclient-basic-linux.x64-12.2.0.1.0.zip
  • instantclient-sqlplus-linux.x64-12.2.0.1.0.zip
  • instantclient-tools-linux.x64-12.2.0.1.0.zip
  • instantclient-sdk-linux.x64-12.2.0.1.0.zip

以下のように、ホームフォルダ以下に展開します。

$ ls -l ~/oracle/
合計 70008
-rw-r--r-- 1 ec2-user ec2-user 68965195  2月 28 23:49 instantclient-basic-linux.x64-12.2.0.1.0.zip
-rw-r--r-- 1 ec2-user ec2-user   674743  3月  1 00:07 instantclient-sdk-linux.x64-12.2.0.1.0.zip
-rw-r--r-- 1 ec2-user ec2-user   904309  2月 28 23:49 instantclient-sqlplus-linux.x64-12.2.0.1.0.zip
-rw-r--r-- 1 ec2-user ec2-user  1132671  2月 28 23:50 instantclient-tools-linux.x64-12.2.0.1.0.zip
drwxrwxr-x 4 ec2-user ec2-user     4096  3月  1 00:07 instantclient_12_2

展開したディレクトリにパスを通しておきます。

$ export ORACLE_BASE=$HOME/oracle
$ export ORACLE_HOME=$ORACLE_BASE/instantclient_12_2
$ export PATH=$ORACLE_HOME:$PATH
$ export TNS_ADMIN=$HOME/etc
$ export LD_LIBRARY_PATH=$ORACLE_HOME:$LD_LIBRARY_PATH
$ mkdir $ORACLE_HOME/log

Oracle DBDモジュールもダウンロードして展開します。

$ wget http://www.cpan.org/authors/id/P/PY/PYTHIAN/DBD-Oracle-1.74.tar.gz
$ tar xzf DBD-Oracle-1.74.tar.gz
$ cd DBD-Oracle-1.74

DBD::Oracleをインストールします。

$ perl Makefile.PL
$ make
$ sudo make install

問題なければ、以下のように、DBIのバージョンが確認できます。

$ perl -e 'use DBI; print $DBI::VERSION,"\n";'
1.627

最後に、SQL*Plusで接続して確認するため、tnsnames.oraファイルを作成しておきます。

$ cat $HOME/etc/tnsnames.ora
mydb =
(DESCRIPTION=
  (ADDRESS=(PROTOCOL=tcp)
      (HOST=test.xxxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com)
      (PORT=1521))
  (CONNECT_DATA=(SERVICE_NAME=ORCL))
)

スクリプトとインポートの実行

インポートを実行するスクリプトを用意します。3行目から6行目の部分を、転送先RDSインスタンスに合わせて編集します。

use DBI;

my $RDS_PORT=1521;
my $RDS_HOST="test.xxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com";
my $RDS_LOGIN="user/password";
my $RDS_SID="ORCL";

my $dirname = "DATA_PUMP_DIR";
my $fname   = $ARGV[0];

my $data  = "dummy";
my $chunk = 8192;

my $sql_open   = "BEGIN perl_global.fh := utl_file.fopen(:dirname,:fname,'wb',:chunk);END;";
my $sql_write  = "BEGIN utl_file.put_raw(perl_global.fh,:data,true); END;";
my $sql_close  = "BEGIN utl_file.fclose(perl_global.fh); END;";
my $sql_global = "create or replace package perl_global as fh utl_file.file_type; end;";

my $conn = DBI->connect('dbi:Oracle:host='.$RDS_HOST.';sid='.$RDS_SID.';port='.$RDS_PORT, $RDS_LOGIN, '') || die ( $DBI::errstr . "\n") ;

my $updated=$conn->do($sql_global);
my $stmt = $conn->prepare($sql_open);
$stmt->bind_param_inout(":dirname", \$dirname, 12);
$stmt->bind_param_inout(":fname", \$fname, 12);
$stmt->bind_param_inout(":chunk", \$chunk, 4);
$stmt->execute() || die($DBI::errstr . "\n");

open(INF,$fname) || die "\nCan't open $fname for reading: $!\n";
binmode(INF);
$stmt = $conn->prepare($sql_write);
my %attrib = ('ora_type','24');
my $val=1;
while ($val >0) {
    $val = read(INF, $data, $chunk);
    $stmt->bind_param(":data", $data, \%attrib);
    $stmt->execute() || die ($DBI::errstr . "\n");
};
die "Problem copying: $!\n" if $!;
close INF || die "Can't close $fname: $!\n";
$stmt = $conn->prepare($sql_close);
$stmt->execute() || die ($DBI::errstr . "\n");

準備できたら実行です。引数に、転送したいダンプファイルを指定し、実行します。

$ perl transfer.pl test.dmp

エラーなく完了したら、次の確認を行います。

ダンプファイルの確認

SQL*PlusでRDSに接続します。

$ sqlplus user/password@mydb

DATA_PUMP_DIRを参照してみます。

SQL> select * from table (rdsadmin.rds_file_util.listdir(p_directory => 'DATA_PUMP_DIR'));

FILENAME
--------------------------------------------------------------------------------
TYPE	     FILESIZE MTIME
---------- ---------- ---------
datapump/
directory	 4096 04-MAR-18

test.dmp
file	     68965195 04-MAR-18

2 rows selected.

test.dmpが転送されていることが確認できました!ここまでできたら、冒頭のブログの記事と同様、DBMS_DATAPUMPパッケージでインポートしましょう。

まとめ

以上です。

OSにアクセスできないRDSでは、インポートにも工夫が必要です。ファイルを直接転送できることで、移行方法の選択肢が広がることでしょう。