DB2DBA: 本当は怖いNONRECOVERABLE LOAD

DB2のLOADコマンドは大量のレコードを高速に投入可能ですが、投入レコードのロギングがなされていないため、LOAD後にROLLFORWARDリカバリを可能にするには実行後にDBまたは表スペースのバックアップが必要になります。バックアップされていない表スペースはBACKUP PENDING状態となり、更新が不可能になります。

とはいえ、常にバックアップを取得するのも面倒なので、LOAD後のBACKUP PENDINGを回避できるNONRECOVERABLEオプションも用意されており、よく利用されています。

```
LOAD inputfile OF DEL REPLACE INTO table NONRECOVERABLE ;
```

しかし、これは文字通りテーブルをリカバリ不可能な状態にしてしまうことを忘れてはなりません。以下のようにROLLFORWARDリカバリが実施された場合、対象の表はアクセス不可能になってしまいます。

1. バックアップ（オンラインバックアップ）の取得
1. NONRECOVERABLE LOADの実行
1. 1. のバックアップをRESTORE
1. 2. より後ろの時点までROLLFORWARDを実行

DBをリカバリしてサービス再開したら使えないテーブルがあって再度サービス停止、なんてことにならないようにしましょう。

なお、NONRECOVERABLE LOAD実行前までのROLLFORWARDであれば、表のステータスとしては正常な状態でリカバリするすることが可能です。

# テストしてみた
## 環境準備
- OS: Windows 10 Home Edition
- DB2: DB2 Express-C 11.1

テストテーブルとしてtab1とtab2を作成し、オンラインバックアップを取得します。
sampleデータベースは事前にアーカイブロギング設定に変更済みです。

```
PS C:\WINDOWS\system32> db2 "create table svc34.tab1 (id int not null primary key)"
DB20000I  SQL コマンドが正常に完了しました。
PS C:\WINDOWS\system32> db2 "create table svc34.tab2 (id int not null primary key)"
DB20000I  SQL コマンドが正常に完了しました。
PS C:\WINDOWS\system32> db2 "insert into svc34.tab1 values (1)"
DB20000I  SQL コマンドが正常に完了しました。
PS C:\WINDOWS\system32> db2 "insert into svc34.tab2 values (1)"
DB20000I  SQL コマンドが正常に完了しました。
PS C:\WINDOWS\system32> db2 "backup db sample online to C:\DB2\backup\sample"

バックアップは成功しました。 このバックアップ・イメージのタイム・スタンプは
20161127224919 です。
```

## NONRECOVERABLE LOADの実施
tab1にNONRECOVERABLE LOADを実行した後、コミットポイントを設けるためtab2を更新します。

```
PS C:\WINDOWS\system32> db2 connect to sample

   データベース接続情報

 データベース・サーバー = DB2/NT64 11.1.0
 SQL 許可 ID            = SVC34
 ローカル・データベース別名 = SAMPLE

PS C:\WINDOWS\system32> db2 "load from NUL of del replace into svc34.tab1 nonrecoverable"
SQL3109N  ユーティリティーが、ファイル "NUL"
からデータのロードを開始しています。

SQL3500W  ユーティリティーが "2016-11-27 22:50:13.601399" に "LOAD"
フェーズを開始しています。

-- 省略 --

SQL3515W  ユーティリティーは、"2016-11-27 22:50:13.716912" に "BUILD"
フェーズを完了しました。


読み込まれた行数        = 0
スキップされた行数      = 0
ロードされた行数        = 0
拒否された行数          = 0
削除された行数          = 0
コミットされた行数      = 0

PS C:\WINDOWS\system32> db2 "insert into svc34.tab2 values (2)"
DB20000I  SQL コマンドが正常に完了しました。
```

## LOAD後時点へのROLLFORWARD

```
PS C:\WINDOWS\system32> db2 connect reset
DB20000I  SQL コマンドが正常に完了しました。
PS C:\WINDOWS\system32> db2 deactivate db sample
DB20000I  DEACTIVATE DATABASE コマンドが正常に完了しました。
PS C:\WINDOWS\system32> db2 "restore db sample from C:\DB2\backup\sample"
SQL2539W  リストアするバックアップ・イメージに指定された名前がターゲット・データ
ベースの名前と同じになっています。
バックアップ・イメージ・データベースと同じ既存データベースをリストアすると、現行
のデータベースがバックアップ・バージョンによって上書きされます。
続けますか。 (y/n) y
DB20000I  RESTORE DATABASE コマンドが正常に完了しました。
PS C:\WINDOWS\system32> db2 "rollforward db sample to end of logs and stop"

                                 ロールフォワード状況

 入力データベース別名                   = sample
 状況を返したメンバーの数               = 1

 メンバー ID                            = 0
 ロールフォワード状況                   = 非ペンディング
 次に読み込むログ・ファイル             =
 処理したログ・ファイル            = S0000003.LOG - S0000004.LOG
 最後にコミットしたトランザクション     = 2016-11-27-13.50.30.000000 UTC

DB20000I  ROLLFORWARD コマンドが正常に完了しました。
PS C:\WINDOWS\system32> db2 connect to sample

   データベース接続情報

 データベース・サーバー = DB2/NT64 11.1.0
 SQL 許可 ID            = SVC34
 ローカル・データベース別名 = SAMPLE

PS C:\WINDOWS\system32> db2 "select * from svc34.tab1"

ID
-----------
SQL1477N  表 "SVC34.TAB1" で、表スペース "2" 内のオブジェクト "20"
にはアクセスできません。  SQLSTATE=55019
PS C:\WINDOWS\system32> db2 "select * from svc34.tab2"

ID
-----------
          1
          2

  2 レコードが選択されました。
```
ROLLFORWARDによりtab2のデータはリカバリできましたが、NONRECOVERABLE LOADが実行されていたtab1がアクセス不可能になっています。**ROLLFOWARD時には警告すら出力されません！**

この状態の表にLOAD QUERYを出すと、使用不可 (Unavailable) のステータスが返されます。

```
PS C:\WINDOWS\system32> db2 "load query table svc34.tab1"
表の状態:
  使用不可
```

これを解消するには表を一度DROPして再度CREATEするか、次に示すようにLOAD前の時点にDBを戻すしかありません。

## LOAD直前時点までのROLLFORWARD
```
PS C:\WINDOWS\system32> db2 connect reset
DB20000I  SQL コマンドが正常に完了しました。
PS C:\WINDOWS\system32> db2 deactivate db sample
DB20000I  DEACTIVATE DATABASE コマンドが正常に完了しました。
PS C:\WINDOWS\system32> db2 "restore db sample from C:\DB2\backup\sample"
SQL2539W  リストアするバックアップ・イメージに指定された名前がターゲット・データ
ベースの名前と同じになっています。
バックアップ・イメージ・データベースと同じ既存データベースをリストアすると、現行
のデータベースがバックアップ・バージョンによって上書きされます。
続けますか。 (y/n) y
DB20000I  RESTORE DATABASE コマンドが正常に完了しました。
PS C:\WINDOWS\system32> db2 "rollforward db sample to 2016-11-27-22.50.12 using local time and stop"

                                 ロールフォワード状況

 入力データベース別名                   = sample
 状況を返したメンバーの数               = 1

 メンバー ID                            = 0
 ロールフォワード状況                   = 非ペンディング
 次に読み込むログ・ファイル             =
 処理したログ・ファイル            = S0000003.LOG - S0000004.LOG
 最後にコミットしたトランザクション     = 2016-11-27-22.49.24.000000 Local

DB20000I  ROLLFORWARD コマンドが正常に完了しました。
PS C:\WINDOWS\system32> db2 activate db sample
DB20000I  ACTIVATE DATABASE コマンドが正常に完了しました。
PS C:\WINDOWS\system32> db2 connect to sample

   データベース接続情報

 データベース・サーバー = DB2/NT64 11.1.0
 SQL 許可 ID            = SVC34
 ローカル・データベース別名 = SAMPLE

PS C:\WINDOWS\system32> db2 "select * from svc34.tab1"

ID
-----------
          1

  1 レコードが選択されました。

PS C:\WINDOWS\system32> db2 "select * from svc34.tab2"

ID
-----------
          1

  1 レコードが選択されました。

```

LOAD実行後の更新は失われてしまいますが、正常な状態にリカバリできました。

# 対策
- プロダクションシステムでは可能な限り使用しない
    - 使用する場合、表の再作成と再LOADで復旧可能なもの、LOAD前後で更新を挟まずに別途バックアップが実施されるケース等に限定する
- DBのリカバリプランを事前に策定しておく
- LIST HISTORYコマンドでLOADの実行履歴を確認する

```
PS C:\WINDOWS\system32> db2 "list history load since 20161127224919 for db sample"

                    sample の履歴ファイルのリスト

突き合わせファイル項目数 = 1


 Op Obj Timestamp+Sequence Type Dev Earliest Log Current Log  Backup ID
 -- --- ------------------ ---- --- ------------ ------------ --------------
  L  T  20161127225013001   R    S  S0000005.LOG S0000005.LOG
 ----------------------------------------------------------------------------
  "SVC34   "."TAB1" は 1 表スペースに常駐します:

 00001 USERSPACE1
 ----------------------------------------------------------------------------
    Comment: DB2
 開始時刻: 20161127225013
   終了時刻: 20161127225013
     状況: A
 ----------------------------------------------------------------------------
  EID: 29 ロケーション: NUL

 DDL: [LOADID: 6364.2016-11-27-22.50.13.479003.0 (2;20)] [*LOCAL.DB2.161127135009] OFFLINE LOAD DEL AUTOMATIC INDEXING R
EPLACE NON-RECOVERABLE SVC34   .TAB1, CPU_PARALLELISM [4], DISK_PARALLELISM [2], DATA BUFFER [7267075], TEMPFILES PATH [
C:\DB2\NODE0000\SQL00001\load\DB200002.PID\DB200014.OID], DUMPFILE [none]
 ----------------------------------------------------------------------------
```



# 類似の機能について
NOT LOGGED INITIALLYによりロギングが停止された表もROLLFOWARD時により同様の状態となります。また、NOT LOGGEDが指定されたLOB列はROLLFORWARDによりデータが失われます（該当のLOB列のみ）。

TRUNCATEやIMPORTのREPLECEオプションも削除対象レコードをロギングしませんが、こちらの検証結果によると実行前後どちらの時点へのROLLFORWARDでも問題なくリカバリできるようです。

- [DB2 LUW Truncate, Restore and Rollforward](https://db2talk.com/2016/03/03/db2-10-5-truncates-restore-and-rollforward/)
- [DB2 LUW Import from /dev/null, Restore and Rollforward](https://db2talk.com/2016/03/08/db2-luw-import-from-devnull-restore-and-rollforward/)

# 参考資料

- IBM Knowledge Center
    - DB2 for Linux UNIX and Windows 11.1.0
        - [LOAD コマンド](http://www.ibm.com/support/knowledgecenter/ja/SSEPGG_11.1.0/com.ibm.db2.luw.admin.cmd.doc/doc/r0008305.html)
        - [ロードの概要](http://www.ibm.com/support/knowledgecenter/ja/SSEPGG_11.1.0/com.ibm.db2.luw.admin.dm.doc/doc/c0004587.html)
        - [ロード操作時およびロード操作後の表スペースの状態](http://www.ibm.com/support/knowledgecenter/ja/SSEPGG_11.1.0/com.ibm.db2.luw.admin.dm.doc/doc/c0051301.html)
- IBM developerWorks
    - [DB2 Basics: Demystifying table and table space states](http://www.ibm.com/developerworks/data/library/techarticle/dm-0407melnyk/)