Amazon RDS for Oracle ~ファイル転送~

マツナミです。

前回の時点では、次にRDS for Oracleの監視について整理したいと思ってましたが 諸事情により今回はDatapumpのダンプファイルの転送について試してみたいと思います。

DatapumpではダンプファイルがDBサーバ上に出力されるという制限があるため、OSログインのできないRDSでは取り扱いが難しいです。
DBLINKを用いて別DBに転送することはできますが、RDSに転送した場合はそのRDSインスタンスから取り出す方法を考える必要がありますし、OracleDB on EC2に転送する場合は、OracleDBのライセンス購入が別途必要になります。

そこで、今回はRDSからOracle ClientでダンプファイルをEC2に送受信する方法を試してみましたので、その手順について記載します。 基本的にはAWSホワイトペーパー記載の内容です。

 

環境構築

EC2にOracle Clientを導入します。

今回、対象OSはWindows Server 2012 R2としてOracle Clientのインストール・タイプはランタイムとしました。

tnsnames.oraにはRDSへの接続設定を記載します。以下の手順では接続記述子をTESTDBとしています。 インストール手順などの詳細は割愛します。

 

動作検証のスキーマを作成

今回の動作検証で使うユーザhogeを作ります。

  1. SQL> create user hoge identified by hogehoge;
  2. ユーザーが作成されました。
  3. SQL> grant create session to hoge;
  4. 権限付与が成功しました。
  5. SQL> grant create table to hoge;
  6. 権限付与が成功しました。
  7. SQL> alter user hoge quota unlimited on USERS;
  8. ユーザーが変更されました。

動作検証用のテーブルを作成

スキーマだけでもいいんですが、エクスポート・インポートするのでテーブルも一応作っておきます。

  1. SQL> connect hoge/hogehoge@TESTDB
  2. 接続されました。
  3. SQL> create table testtbl(id number, name varchar2(30));
  4. 表が作成されました。
  5. SQL> insert into testtbl values(1,'fuga');
  6. 1行が作成されました。
  7. SQL> commit;
  8. コミットが完了しました。
  9. SQL> exit

 

Datapumpエクスポートの実行

EC2上でexpdpを実行し、ダンプファイルが作成されたことを確認します。

> expdp master/********@TESTDB directory=DATA_PUMP_DIR dumpfile=hoge.dmp schemas=hoge

> sqlplus master/********@TESTDB

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

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

export.log
file             1415 18-02-04

hoge.dmp
file           286720 18-02-04

ダンプファイルをEC2に転送

AWSホワイトぺーパーのp.23-27にて、EC2からRDSにファイルを転送するスクリプトが記載されています。

それを少し修正することでRDSからEC2に転送することが可能です。
なお、$RDS_PORTから$dirnameの箇所は環境に応じて修正してください。

  1. my $RDS_PORT=4080;
  2. my $RDS_HOST="myrdshost.xxx.us-east-1-devo.rds-dev.amazonaws.com";
  3. my $RDS_LOGIN="orauser/orapwd";
  4. my $RDS_SID="myoradb";
  5. my $dirname = "DATA_PUMP_DIR";
  6. my $fname = $ARGV[0];
  7. my $data = "dummy";
  8. my $chunk = 32767;
  9. my $sql_open = "BEGIN perl_global.fh := utl_file.fopen(:dirname,:fname,'rb',:chunk);END;";
  10. my $sql_read = "BEGIN utl_file.get_raw(perl_global.fh,:data,:chunk); END;";
  11. my $sql_close = "BEGIN utl_file.fclose(perl_global.fh); END;";
  12. my $sql_global = "create or replace package perl_global as fh utl_file.file_type; end;";
  13. my $conn = DBI->connect('dbi:Oracle:host='.$RDS_HOST.';sid='.$RDS_SID.';port='.$RDS_PORT, $RDS_LOGIN, '') || die ( $DBI::errstr . "\n") ;
  14. my $updated=$conn->do($sql_global);
  15. my $stmt = $conn->prepare($sql_open);
  16. $stmt->bind_param_inout(":dirname", \$dirname, 12);
  17. $stmt->bind_param_inout(":fname", \$fname, 12);
  18. $stmt->bind_param_inout(":chunk", \$chunk, 5);
  19. $stmt->execute() || die($DBI::errstr . "\n");
  20. open(INF,"> $fname") || die "\nCan't open $fname for writing: $!\n";
  21. binmode(INF);
  22. $stmt = $conn->prepare($sql_read);
  23. my %attrib = ('ora_type','24');
  24. my $val=0;
  25. while ($val==0){
  26. # $stmt->execute() writes ORA-1403(NO_DATA_FOUND) message to STDERR.
  27. # To hidden the message, do "open STDERR" at first.
  28. open STDERR, '>', undef;
  29. $stmt->bind_param_inout(":data", \$data, $chunk, \%attrib);
  30. $stmt->bind_param_inout(":chunk", \$chunk, 5);
  31. $stmt->execute();
  32. $val = $stmt->err;
  33. if($val != 0){
  34. if($val == 1403){ last; }
  35. open(STDERR, ">&STDOUT");
  36. die "$DBI::errstr \nProblem copying: $!\n" if $!;
  37. };
  38. print INF $data || die "\nWriting File error: $!\n";
  39. };
  40. close INF || die "Can't close $fname: $!\n";
  41. $stmt = $conn->prepare($sql_close);
  42. $stmt->execute() || die ($DBI::errstr . "\n");

このperlスクリプトコマンドプロンプトから実行してみます。
ここでは、Oracle Clientインストール時に導入されるperl.exeを利用しています。

> set ORACLE_HOME=D:\Oracle\product\12.1.0\client_1

> %ORACLE_HOME%\perl\bin\perl.exe getrds.pl hoge.dmp 

> dir
:
2018/02/04  16:51           286,720 hoge.dmp
:

カレントフォルダにhoge.dmpが転送されてきたことを確認できました。

ダンプファイルの削除と動作検証用スキーマの削除

検証のためにDBサーバ上のダンプファイルと動作検証用のスキーマを削除します。

  1. SQL> drop user hoge cascade;
  2. ユーザーが削除されました。
  3. SQL> exec utl_file.fremove('DATA_PUMP_DIR','hoge.dmp');
  4. PL/SQLプロシージャが正常に完了しました。
  5. SQL>

ダンプファイルをRDSに転送

EC2からRDSへのダンプファイルの転送は前述のとおり、AWS資料に記載されていますので、環境設定だけ変えればそのまま使えます。

  1. use DBI;
  2. my $RDS_PORT=4080;
  3. my $RDS_HOST="myrdshost.xxx.us-east-1-devo.rds-dev.amazonaws.com";
  4. my $RDS_LOGIN="orauser/orapwd";
  5. my $RDS_SID="myoradb";
  6. my $dirname = "DATA_PUMP_DIR";
  7. my $fname = $ARGV[0];
  8. my $data = "dummy";
  9. my $chunk = 8192;
  10. my $sql_open = "BEGIN perl_global.fh := utl_file.fopen(:dirname,:fname,'wb',:chunk);END;";
  11. my $sql_write = "BEGIN utl_file.put_raw(perl_global.fh,:data,true); END;";
  12. my $sql_close = "BEGIN utl_file.fclose(perl_global.fh); END;";
  13. my $sql_global = "create or replace package perl_global as fh utl_file.file_type; end;";
  14. my $conn = DBI->connect('dbi:Oracle:host='.$RDS_HOST.';sid='.$RDS_SID.';port='.$RDS_PORT, $RDS_LOGIN, '') || die ( $DBI::errstr . "\n") ;
  15. my $updated=$conn->do($sql_global);
  16. my $stmt = $conn->prepare($sql_open);
  17. $stmt->bind_param_inout(":dirname", \$dirname, 12);
  18. $stmt->bind_param_inout(":fname", \$fname, 12);
  19. $stmt->bind_param_inout(":chunk", \$chunk, 4);
  20. $stmt->execute() || die($DBI::errstr . "\n");
  21. open(INF,$fname) || die "\nCan't open $fname for reading: $!\n";
  22. binmode(INF);
  23. $stmt = $conn->prepare($sql_write);
  24. my %attrib = ('ora_type','24');
  25. my $val=1;
  26. while ($val >0) {
  27. $val = read(INF, $data, $chunk);
  28. $stmt->bind_param(":data", $data, \%attrib);
  29. $stmt->execute() || die ($DBI::errstr . "\n");
  30. };
  31. die "Problem copying: $!\n" if $!;
  32. close INF || die "Can't close $fname: $!\n";
  33. $stmt = $conn->prepare($sql_close);
  34. $stmt->execute() || die ($DBI::errstr . "\n");

RDSのディレクトリオブジェクト上に転送されたことを確認します。

sqlplus master/********@TESTDB
SQL> select * from table
  2      (rdsadmin.rds_file_util.listdir(p_directory => 'DATA_PUMP_DIR'));

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

export.log
file             1415 18-02-04

hoge.dmp
file           286720 18-02-04

 

インポートの実行

転送したファイルをDatapumpインポートします。

>impdp master/********@TESTDB directory=DATA_PUMP_DIR dumpfile=hoge.dmp

Import: Release 12.1.0.2.0 - Production on 日 2月 4 17:00:25 2018

Copyright (c) 1982, 2014, Oracle and/or its affiliates.  All rights reserved.

接続先: Oracle Database 12c Standard Edition Release 12.1.0.2.0 - 64bit Production
マスター表"MASTER"."SYS_IMPORT_FULL_01"は正常にロード/アンロードされました
"MASTER"."SYS_IMPORT_FULL_01"を起動しています: master/********@TESTDB directory=DATA_PUMP_DIR dumpfile=hoge.dmp
オブジェクト型SCHEMA_EXPORT/USERの処理中です
オブジェクト型SCHEMA_EXPORT/SYSTEM_GRANTの処理中です
オブジェクト型SCHEMA_EXPORT/DEFAULT_ROLEの処理中です
オブジェクト型SCHEMA_EXPORT/TABLESPACE_QUOTAの処理中です
オブジェクト型SCHEMA_EXPORT/PRE_SCHEMA/PROCACT_SCHEMAの処理中です
オブジェクト型SCHEMA_EXPORT/TABLE/TABLEの処理中です
オブジェクト型SCHEMA_EXPORT/TABLE/TABLE_DATAの処理中です
. . "HOGE"."TESTTBL"                            5.484 KB       1行がインポートされました
オブジェクト型SCHEMA_EXPORT/TABLE/STATISTICS/TABLE_STATISTICSの処理中です
オブジェクト型SCHEMA_EXPORT/STATISTICS/MARKERの処理中です
ジョブ"MASTER"."SYS_IMPORT_FULL_01"が日 2月 4 08:00:53 2018 elapsed 0 00:00:25で正常に完了しました

> sqlplus master/********@TESTDB

SQL> select * from hoge.testtbl;

        ID NAME
---------- ------------------------------
         1 fuga

SQL>

EC2から送信したダンプファイルを用いてhogeスキーマとその中のテーブルがインポートされたことが確認できました。

 

まとめ

Oracle Clientを使ってダンプファイルを送受信することを確認しました。
RDSを利用している場合、EC2にOracle Clientを導入するケースも多いので役立つこともあるのではないでしょうか。

ちなみに、S3に直接ファイルを転送する場合はalexandria-plsql-utilsを利用する方式がありますが
この方式だとオプショングループでAPEX機能を追加する必要があるため、本番稼働中のシステムに導入するのは若干敷居が高いかもしれません。が、一度検証してみたいとは思っています。

Datapumpのダンプファイルの転送方式にもいろいろあるので、要件に応じて適切な方式を選択していきたいものですね。

 

 

 

AWS相談会