前回の記事では、Amazon Relational Database Service (Amazon RDS) for Oracle において「データベース全体を指定した時点の状態に復元する」ことをテーマとした内容をご紹介いたしました。
今回は前回の記事に関連して、「特定のテーブルのみ指定した時点の状態に復元する」ことに着目し、前回と同様チュートリアルを中心に説明・紹介したいと思います。
特定テーブルに対する POINT IN TIME RECOVERY
「特定のテーブルのみ指定した時点の状態に復元する」ことは Oracle データベース標準搭載されている Flashback 機能でも実現は可能です。しかし Flashback 機能だけですべてのケースに対応できるわけではありません。
例えば、一定の時間が経過し UNDO が残っていない…といった状態では、Flashback 機能でカバーすることは困難になってしまいます。
このような場合、対処方法としてはテーブル単位で Amazon RDS for OracleのPOINT IN TIME RECOVERY を実施する方法が挙げられます。
チュートリアルの事前準備
- Amazon RDS for Oracle
- データベースへの接続用端末を一台
- 周辺端末、ツール
チュートリアルを開始する前に、Amazon RDS for Oracle を用意し、端末に Oracle クライアントをインストールしてください。
今回の例ではテーブル等のデータベースの内容を確認するため、SQL 発行ツールは Oracle SQL*Plus、端末は Windows を利用しています。
チュートリアル
チュートリアルは以下2つの構成(コンポーネント)で実現しています。
- AWS RDS for Oracle
- Amazon Elastic Compute Cloud (Amazon EC2)
※データベースクライアントツールをインストール済み
1.Amazon RDS for Oracle を作成
- 最小構成で問題ありません。
- データベース作成には10分以上要する場合があります。
2.Oracle データベースへの接続
データベース接続する必要なホスト名、ポート番号、サービス名、データベースユーザー名、パスワードを入力し、Oracleクライアントツールで接続します。
3.Oracle データベーステーブル作成およびデータ投入
このチュートリアルでは、復元できたかを確認しやすいように、一秒間隔で「現在の時刻の文字列」と「現在の時刻のタイムスタンプ」をデータとしてテーブルに挿入しています。
テーブル名「Table01s」の作成SQL文サンプル
create table "Table01s" (Id NUMBER(19) generated by default on null as identity constraint "PK_Table01s" primary key, TableName NVARCHAR2(2000), InsertDateTimeString NVARCHAR2(2000), InsertDateTime TIMESTAMP(7) not null );
テーブル名「Table02s」の作成SQL文サンプル
create table "Table02s" (Id NUMBER(19) generated by default on null as identity constraint "PK_Table02s" primary key, TableName NVARCHAR2(2000), InsertDateTimeString NVARCHAR2(2000), InsertDateTime TIMESTAMP(7) not null );
一秒間隔でデータを挿入するC# コードのサンプル
// loop 1200 time for( int i = 0 ; i < 1200 ; i++ ) { //insert into Table01s await OracleDbConnection.Table01s.AddAsync( new Table01() ); //insert into Table02s await OracleDbConnection.Table02s.AddAsync( new Table02() ); //COMMIT await OracleDbConnection.SaveChangesAsync(); //Sleep one Second Thread.Sleep(1000); }
4.挿入されたデータを確認(一部)
select * from "Table01s"; Id TableName InsertDateTimeString InsertDateTime ------- ------------- ------------------------- ---------------------------- ::: 112 Table01 8/15/2019 10:29:50 AM 2019-08-15 10:29:50.4830807 113 Table01 8/15/2019 10:29:51 AM 2019-08-15 10:29:51.5250651 114 Table01 8/15/2019 10:29:52 AM 2019-08-15 10:29:52.5660862 115 Table01 8/15/2019 10:29:53 AM 2019-08-15 10:29:53.6071142 116 Table01 8/15/2019 10:29:54 AM 2019-08-15 10:29:54.6745007 117 Table01 8/15/2019 10:29:55 AM 2019-08-15 10:29:55.7155107 118 Table01 8/15/2019 10:29:56 AM 2019-08-15 10:29:56.7575129 119 Table01 8/15/2019 10:29:57 AM 2019-08-15 10:29:57.7975582 120 Table01 8/15/2019 10:29:58 AM 2019-08-15 10:29:58.8375971 121 Table01 8/15/2019 10:29:59 AM 2019-08-15 10:29:59.9284349 ★ 122 Table01 8/15/2019 10:30:00 AM 2019-08-15 10:30:00.9704357 123 Table01 8/15/2019 10:30:02 AM 2019-08-15 10:30:02.0124255 124 Table01 8/15/2019 10:30:03 AM 2019-08-15 10:30:03.0544300 125 Table01 8/15/2019 10:30:04 AM 2019-08-15 10:30:04.0954498 126 Table01 8/15/2019 10:30:05 AM 2019-08-15 10:30:05.1716260 127 Table01 8/15/2019 10:30:06 AM 2019-08-15 10:30:06.2136436 128 Table01 8/15/2019 10:30:07 AM 2019-08-15 10:30:07.2585555 129 Table01 8/15/2019 10:30:08 AM 2019-08-15 10:30:08.2995733 130 Table01 8/15/2019 10:30:09 AM 2019-08-15 10:30:09.3405906 131 Table01 8/15/2019 10:30:10 AM 2019-08-15 10:30:10.3846636 select count(*) from "Table01s"; count(*) -------- 1200 select * from "Table02s"; Id TableName InsertDateTimeString InsertDateTime ------- ------------- ------------------------- ---------------------------- ::: 112 Table02 8/15/2019 10:29:50 AM 2019-08-15 10:29:50.4832277 113 Table02 8/15/2019 10:29:51 AM 2019-08-15 10:29:51.5252166 114 Table02 8/15/2019 10:29:52 AM 2019-08-15 10:29:52.5662312 115 Table02 8/15/2019 10:29:53 AM 2019-08-15 10:29:53.6072965 116 Table02 8/15/2019 10:29:54 AM 2019-08-15 10:29:54.6746576 117 Table02 8/15/2019 10:29:55 AM 2019-08-15 10:29:55.7156602 118 Table02 8/15/2019 10:29:56 AM 2019-08-15 10:29:56.7576595 119 Table02 8/15/2019 10:29:57 AM 2019-08-15 10:29:57.7977028 120 Table02 8/15/2019 10:29:58 AM 2019-08-15 10:29:58.8377429 121 Table02 8/15/2019 10:29:59 AM 2019-08-15 10:29:59.9285832 ★ 122 Table02 8/15/2019 10:30:00 AM 2019-08-15 10:30:00.9705783 123 Table02 8/15/2019 10:30:02 AM 2019-08-15 10:30:02.0125774 124 Table02 8/15/2019 10:30:03 AM 2019-08-15 10:30:03.0545819 125 Table02 8/15/2019 10:30:04 AM 2019-08-15 10:30:04.0956067 126 Table02 8/15/2019 10:30:05 AM 2019-08-15 10:30:05.1717714 127 Table02 8/15/2019 10:30:06 AM 2019-08-15 10:30:06.2137886 128 Table02 8/15/2019 10:30:07 AM 2019-08-15 10:30:07.2587005 129 Table02 8/15/2019 10:30:08 AM 2019-08-15 10:30:08.2997199 130 Table02 8/15/2019 10:30:09 AM 2019-08-15 10:30:09.3407368 131 Table02 8/15/2019 10:30:10 AM 2019-08-15 10:30:10.3848197 select count(*) from "Table02s"; count(*) -------- 1200
5.POINT IN TIME RECOVERY(データベース全体) 実行
RDSデータベースを選択 > アクション > 特定時点への復元を選択
復元したい時刻を選択し、データベースサーバ構成を最初から入力してください。
以下の画面では2019年8月15日10:30:00 UTC+9に設定しています。
6.POINT IN TIME RECOVERY 後のデータ確認
select * from "Table01s"; Id TableName InsertDateTimeString InsertDateTime ------- ------------- ------------------------- ---------------------------- ::: 112 Table01 8/15/2019 10:29:50 AM 2019-08-15 10:29:50.4830807 113 Table01 8/15/2019 10:29:51 AM 2019-08-15 10:29:51.5250651 114 Table01 8/15/2019 10:29:52 AM 2019-08-15 10:29:52.5660862 115 Table01 8/15/2019 10:29:53 AM 2019-08-15 10:29:53.6071142 116 Table01 8/15/2019 10:29:54 AM 2019-08-15 10:29:54.6745007 117 Table01 8/15/2019 10:29:55 AM 2019-08-15 10:29:55.7155107 118 Table01 8/15/2019 10:29:56 AM 2019-08-15 10:29:56.7575129 119 Table01 8/15/2019 10:29:57 AM 2019-08-15 10:29:57.7975582 120 Table01 8/15/2019 10:29:58 AM 2019-08-15 10:29:58.8375971 121 Table01 8/15/2019 10:29:59 AM 2019-08-15 10:29:59.9284349 ★ select * from "Table02s"; Id TableName InsertDateTimeString InsertDateTime ------- ------------- ------------------------- ---------------------------- ::: 112 Table02 8/15/2019 10:29:50 AM 2019-08-15 10:29:50.4832277 113 Table02 8/15/2019 10:29:51 AM 2019-08-15 10:29:51.5252166 114 Table02 8/15/2019 10:29:52 AM 2019-08-15 10:29:52.5662312 115 Table02 8/15/2019 10:29:53 AM 2019-08-15 10:29:53.6072965 116 Table02 8/15/2019 10:29:54 AM 2019-08-15 10:29:54.6746576 117 Table02 8/15/2019 10:29:55 AM 2019-08-15 10:29:55.7156602 118 Table02 8/15/2019 10:29:56 AM 2019-08-15 10:29:56.7576595 119 Table02 8/15/2019 10:29:57 AM 2019-08-15 10:29:57.7977028 120 Table02 8/15/2019 10:29:58 AM 2019-08-15 10:29:58.8377429 121 Table02 8/15/2019 10:29:59 AM 2019-08-15 10:29:59.9285832 ★
データベースが、復元時に指定した時刻である午前10時30分00秒の状態に戻ったことが確認できました。
ここまでは前回の記事の内容とほぼ同じですが、異なるポイントとしては、テーブルをもうひとつ用意して、同じデータをinsertしていることです。
7.データベースリンク作成
POINT IN TIME RECOVERY で新規作成されたデータベースから古いデータベースへのデータベースリンクを作成してください。
CREATE DATABASE LINK origin_db02 CONNECT TO admin IDENTIFIED BY xxxxxx USING '(DESCRIPTION = (ADDRESS = (PROTOCOL = TCP) (HOST = awscomt.chyqoeuzkmwi.ap-northeast-1.rds.amazonaws.com) (PORT = 1521) ) (CONNECT_DATA = (SID = awscomt) ) )';
8.ダンプファイル作成
復元したいテーブルのみをダンプファイルへエクスポートします。
以下のサンプルは Table01s テーブルのみをダンプファイルへエクスポートするPL/SQLです。
DECLARE hdn1 NUMBER; BEGIN hdn1 := DBMS_DATAPUMP.open( operation => 'EXPORT', job_mode => 'SCHEMA', job_name => null); DBMS_DATAPUMP.ADD_FILE( handle => hdn1, filename => 'expdb_tables.dmp', directory => 'DATA_PUMP_DIR', filetype => dbms_datapump.ku$_file_type_dump_file); DBMS_DATAPUMP.ADD_FILE( handle => hdn1, filename => 'expdb_tables.log', directory => 'DATA_PUMP_DIR', filetype => dbms_datapump.ku$_file_type_log_file); DBMS_DATAPUMP.METADATA_FILTER( handle => hdn1, name => 'SCHEMA_EXPR', value => 'IN (''ADMIN'')'); DBMS_DATAPUMP.METADATA_FILTER( handle => hdn1, name => 'NAME_EXPR', value => 'IN (''Table01s'')', object_path => 'TABLE'); DBMS_DATAPUMP.START_JOB(hdn1); END; /
9.ダンプファイル転送
origin_db02データベースリンクを通してダンプファイルexpdb_tables.dmpを古いデータベースへ運搬します。
BEGIN DBMS_FILE_TRANSFER.PUT_FILE( source_directory_object => 'DATA_PUMP_DIR', source_file_name => 'expdb_tables.dmp', destination_directory_object => 'DATA_PUMP_DIR', destination_database => 'origin_db02' ); END; /
10.ダンプファイルでテーブル復元
TABLE_EXISTS_ACTIONの値をREPLACEに設定して、既存のテーブルの内容を上書きします。
DECLARE hdn1 NUMBER; BEGIN hdn1 := DBMS_DATAPUMP.open( operation => 'IMPORT', job_mode => 'SCHEMA', job_name => null); DBMS_DATAPUMP.ADD_FILE( handle => hdn1, filename => 'tab1_copied.dmp', directory => 'DATA_PUMP_DIR', filetype => dbms_datapump.ku$_file_type_dump_file); DBMS_DATAPUMP.ADD_FILE( handle => hdn1, filename => 'imp.log', directory => 'DATA_PUMP_DIR', filetype => dbms_datapump.ku$_file_type_log_file); DBMS_DATAPUMP.METADATA_FILTER( handle => hdn1, name => 'SCHEMA_EXPR', value => 'IN (''ADMIN'')'); DBMS_DATAPUMP.METADATA_FILTER( handle => hdn1, name => 'NAME_EXPR', value => 'IN (''Table01s'')', object_path => 'TABLE'); DBMS_DATAPUMP.SET_PARAMETER(hdn1,'TABLE_EXISTS_ACTION','REPLACE'); DBMS_DATAPUMP.START_JOB(hdn1); END; /
11.復元されたテーブル確認
テーブル「Tables01s」が復元される前には1200行あったデータが、121行となりました。
また、最新データの時刻も10時30分と表示されていることから、無事に復元が完了できたことが確認できます。
select * from "Table01s"; Id TableName InsertDateTimeString InsertDateTime ------- ------------- ------------------------- ---------------------------- ::: 112 Table01 8/15/2019 10:29:50 AM 2019-08-15 10:29:50.4830807 113 Table01 8/15/2019 10:29:51 AM 2019-08-15 10:29:51.5250651 114 Table01 8/15/2019 10:29:52 AM 2019-08-15 10:29:52.5660862 115 Table01 8/15/2019 10:29:53 AM 2019-08-15 10:29:53.6071142 116 Table01 8/15/2019 10:29:54 AM 2019-08-15 10:29:54.6745007 117 Table01 8/15/2019 10:29:55 AM 2019-08-15 10:29:55.7155107 118 Table01 8/15/2019 10:29:56 AM 2019-08-15 10:29:56.7575129 119 Table01 8/15/2019 10:29:57 AM 2019-08-15 10:29:57.7975582 120 Table01 8/15/2019 10:29:58 AM 2019-08-15 10:29:58.8375971 121 Table01 8/15/2019 10:29:59 AM 2019-08-15 10:29:59.9284349 ★ select count(*) from "Table01s"; count(*) -------- 121
テーブル「Table02s」はPOINT IN TIME RECOVERY前の状態を確認できます。
select * from "Table02s"; Id TableName InsertDateTimeString InsertDateTime ------- ------------- ------------------------- ---------------------------- ::: 112 Table02 8/15/2019 10:29:50 AM 2019-08-15 10:29:50.4832277 113 Table02 8/15/2019 10:29:51 AM 2019-08-15 10:29:51.5252166 114 Table02 8/15/2019 10:29:52 AM 2019-08-15 10:29:52.5662312 115 Table02 8/15/2019 10:29:53 AM 2019-08-15 10:29:53.6072965 116 Table02 8/15/2019 10:29:54 AM 2019-08-15 10:29:54.6746576 117 Table02 8/15/2019 10:29:55 AM 2019-08-15 10:29:55.7156602 118 Table02 8/15/2019 10:29:56 AM 2019-08-15 10:29:56.7576595 119 Table02 8/15/2019 10:29:57 AM 2019-08-15 10:29:57.7977028 120 Table02 8/15/2019 10:29:58 AM 2019-08-15 10:29:58.8377429 121 Table02 8/15/2019 10:29:59 AM 2019-08-15 10:29:59.9285832 ★ 122 Table02 8/15/2019 10:30:00 AM 2019-08-15 10:30:00.9705783 123 Table02 8/15/2019 10:30:02 AM 2019-08-15 10:30:02.0125774 124 Table02 8/15/2019 10:30:03 AM 2019-08-15 10:30:03.0545819 125 Table02 8/15/2019 10:30:04 AM 2019-08-15 10:30:04.0956067 126 Table02 8/15/2019 10:30:05 AM 2019-08-15 10:30:05.1717714 127 Table02 8/15/2019 10:30:06 AM 2019-08-15 10:30:06.2137886 128 Table02 8/15/2019 10:30:07 AM 2019-08-15 10:30:07.2587005 129 Table02 8/15/2019 10:30:08 AM 2019-08-15 10:30:08.2997199 130 Table02 8/15/2019 10:30:09 AM 2019-08-15 10:30:09.3407368 131 Table02 8/15/2019 10:30:10 AM 2019-08-15 10:30:10.3848197 select count(*) from "Table02s"; count(*) -------- 1200
最後に、不要なデータベースとダンプファイルなどはお忘れなく削除してください。
まとめ
AWS RDS for Oracle の POINT IN TIME RECOVERY と Oracle のデータベースリンク、データポンプを組み合わせることで、指定したテーブルの復元に成功しました。
なお、復元したテーブルに外部キーで参照されている場合は、残念ながら別の対処方法を考える必要がありますのでご注意ください。