Db2機能によるバックアップとリストアについてまとめたので、今度はストレージ機能(いわゆる物理バックアップ)によるバックアップとリストアのシミュレーションをしてみる。
各領域の整合性を保つため、RDBMSの機能で静止化(書き込み抑止)する、DBを非活動化して停止する、またはOSシャットダウンした上で取得する必要があるが、今回はオンラインバックアップが要件である、つまりバックアップのためのサービス停止はできないと想定し、静止化状態でのバックアップ・リストアとする。
前提環境は以下の通り。
- Db2 v11.1
- OS RHEL 7.3
- HyperVisor VMware Fusion
DBの構成は以下のようになっている。
項目 | 構成 |
---|---|
DB名 | HADRDB |
DBパス | /hadrdb/dbpath* |
ストレージ | /hadrdb/tbsp1*,/hadrdb/tbsp2* |
アクティブログ | /hadrdb/trnlog* |
アーカイブログ | /hadrdb/arclog* |
表中の"*"をつけた箇所がマウントポイントになっていて、それぞれ/dev/sdd,/dev/sde,/dev/sdf,/dev/sdg,/dev/sdhにxfsでファイルシステムを作成している。
さらに、同じディスク構成でホストdb1とdb2とでHADRを構成している。適宜HADRの状態がどうなるかもみておく。
また、強制テイクオーバー前にバックアップを取得し、リストア後に元のPRIMARY/STANDBYの状態に復帰できるかも合わせて確認する。
なお、ディスククローンできるような高級なストレージ装置など持ち合わせていないので、VMwareの仮想ディスクをまるごとコピー・コピーバックすること tarコマンドで、ストレージ機能1の代替とする。
/dev/sdd /hadrdb/dbpath xfs defaults 0 0
/dev/sde /hadrdb/tbsp1 xfs defaults 0 0
/dev/sdf /hadrdb/tbsp2 xfs defaults 0 0
/dev/sdg /hadrdb/trnlog xfs defaults 0 0
/dev/sdh /hadrdb/arclog xfs defaults 0 0
想定シナリオは以下。
- シナリオ1:オンライン中のとある時点でバックアップを取得し、さらにトランザクションが入った後に表スペース障害発生
- シナリオ2:表スペースとログも逝ってしまった(多重障害)
- シナリオ3:バックアップ取得後に被災してテイクオーバーしたものの、その後障害が発生
- シナリオ4:被災訓練を想定して事前にバックアップ、STANDBYが強制テイクオーバーし、しばらくトランザクションが入ったのちにリストア(テイクオーバー後のアーカイブログがSTANDBYに残った状態)
それでは、状況開始!!!
シナリオ1(オンライン中表スペース障害)
オンライン中
空っぽの状態でバックアップ・リストアしても虚しい(というよりもテスト要件を満たさない)ので、オンラインの取引を想定してDBを更新しながらいろいろとやってみる。
db2 "CONNECT TO HADRDB"
db2 "CREATE TABLE Hogetbl (id INTEGER, desc VARCHAR(32))"
db2 "INSERT INTO Hogetbl VALUES (1, 'hoge1')"
db2 "TERMINATE"
バックアップ取得
db2 "CONNECT TO HADRDB"
# バックアップ前にバッファプールからディスクへ書き出し
db2 "FLUSH BUFFERPOOLS ALL"
# バックアップに備えて書き込み抑止
# INCLUDE LOGSでログ書き込みも停止(デフォルト動作)
db2 "SET WRITE SUSPEND FOR DB INCLUDE LOGS"
# この時点のログ状態確認
find /hadrdb -name "*.LOG"
# ログ出力も抑止されているため、更新が待たされることを確認
db2 "INSERT INTO Hogetbl VALUES (2, 'hoge2')"
バックアップ時点のログの状態は以下の通り。
項目 | 保持ファイル |
---|---|
PRIMARYアクティブログ | S0000022.LOG〜S0000034.LOG |
PRIMARYアーカイブログ | S0000019.LOG〜S0000022.LOG |
STANDBYアクティブログ | S0000011.LOG〜S0000032.LOG |
STANDBYアーカイブログ | S0000007.LOG,S0000008.LOG,S0000011.LOG |
ここで物理バックアップ(tarで代替)。DBパス、表スペース、アクティブログ領域を対象に含め、PRIMARY/STANDBYそれぞれ実施。
cd /; tar cvf /tmp/dbbkup.tar ./hadrdb
バックアップ取得後
db2 "CONNECT TO HADRDB"
# サスペンドを解除
db2 "SET WRITE RESUME FOR DB"
この時点でようやくバックアップ直前に実行したhoge2のSQLがリターンする(文末の参考資料にある通り、タイプ2接続はデフォルト無限待ち)。
バックアップ完了後のオンライン再開中
# さらに更新を続ける
for i in $(seq 3 10); do
db2 "INSERT INTO Hogetbl VALUES ( ${i}, 'hogehoge')"
done
# 強制アーカイブ
db2 "TERMINATE"
db2 "ARCHIVE LOG FOR DB HADRDB"
# 念の為HADR状態確認(peer)
db2pd -hadr -db hadrdb
# ログ状態確認
find /hadrdb -name "*.LOG"
項目 | 保持ファイル |
---|---|
PRIMARYアクティブログ | S0000023.LOG〜S0000035.LOG |
PRIMARYアーカイブログ | S0000019.LOG〜S0000023.LOG |
STANDBYアクティブログ | S0000012.LOG〜S0000032.LOG |
STANDBYアーカイブログ | S0000007.LOG,S0000008.LOG,S0000011.LOG |
障害発生
表スペース領域のファイルシステムに障害が発生したと想定する(ログは生きている)。
rm -rf /hadrdb/tbsp[12]/*
復旧
ログは損傷していないため、DBパスと表スペース領域のみリストアする。
db2stop # インスタンス停止
# 物理バックアップからリストア
cd /; rm -rf /hadrdb/dbpath/* /hadrdb/tbsp[12]/*
tar xvf /tmp/dbbkup.tar ./hadrdb/dbpath ./hadrdb/tbsp1 ./hadrdb/tbsp2
db2start # インスタンス起動
DBをACTIVATEすると、
db2 "ACTIVATE DB HADRDB"
SQL1552N The command failed because write operations for the database are
suspended or are being suspended.
サスペンド状態で復旧してくるので、まずはこれを解除する必要がある。とはいえ、バックアップ完了後に実行した"SET WRITE RESUME FOR DB"はDBコネクションが前提となるが、この場合はCONNECTすらできない。
やり方が2通りあるのでそれぞれ見てみる。
PRIMARY復旧後のサスペンド解除
RESUME RESTART
# WRITE RESUMEで再起動
db2 "RESTART DB HADRDB WRITE RESUME"
SQL1273N An operation reading the logs on database "HADRDB" cannot continue
because of a missing log file "S0000022.LOG" on database partition "0" and log
stream "0".
ログ#22がないといわれたのでアーカイブにある#22をアクティブログディレクトリにコピーしてもう一度リスタートする。
cp /hadrdb/arclog/dbinst/HADRDB/NODE0000/LOGSTREAM0000/C0000004/S0000022.LOG /hadrdb/trnlog/NODE0000/LOGSTREAM0000/
db2 "RESTART DB HADRDB WRITE RESUME"
# HADRとログ状況確認
db2pd -hadr -db hadrdb
find /hadrdb -name "*.LOG"
HADR_STATE = PEER
項目 | 保持ファイル |
---|---|
PRIMARYアクティブログ | S0000024.LOG〜S0000036.LOG |
PRIMARYアーカイブログ | S0000019.LOG〜S0000023.LOG |
STANDBYアクティブログ | S0000012.LOG〜S0000032.LOG |
STANDBYアーカイブログ | S0000007.LOG〜S0000008.LOG,S0000011.LOG |
DBの内容を確認する。
db2 "CONNECT TO HADRDB"
db2 "SELECT * FROM Hogetbl"
クラッシュリカバリにより障害発生直前のid=10までもどった。
db2inidb
状況を巻き戻し、障害直前から状況再開。
### 擬似障害
rm -rf /hadrdb/tbsp[12]/*
### 復旧
db2stop # インスタンス停止
cd /; rm -rf /hadrdb/dbpath/* /hadrdb/tbsp[12]/*
tar xvf /tmp/dbbkup.tar ./hadrdb/dbpath ./hadrdb/tbsp1 ./hadrdb/tbsp2
db2start # インスタンス起動
# ロールフォワードペンディング状態へ
db2inidb HADRDB as mirror
# 確認
db2 GET DB CFG FOR HADRDB | grep -i pend
# ロールフォワード実行
db2 "ROLLFORWARD DB HADRDB TO END OF LOGS"
db2 "ROLLFORWARD DB HADRDB STOP"
# 検索してみる
db2 "CONNECT TO HADRDB"
db2 "SELECT * FROM Hogetbl" # id=10までロールフォワード
HADBの状態を確認する。
db2pd -hadr -db hadrdb
# HADR is not active.ときたので再始動
db2 "START HADR ON DB HADRDB AS PRIMARY"
db2pd -hadr -db hadrdb
PEERになった。
シナリオ2(オンライン中に表スペースとアクティブログ領域障害)
HADR含め、データは整合しているので、このままオンラインを継続する。
オンラインバックアップ
db2 "CONNECT TO HADRDB"
db2 "INSERT INTO Hogetbl VALUES (11, 'hoge11')"
db2 "FLUSH BUFFERPOOLS ALL"
db2 "SET WRITE SUSPEND FOR DB INCLUDE LOGS"
cd /; tar cvf /tmp/dbbkup2.tar ./hadrdb
find /hadrdb -name "*.LOG"
db2 "SET WRITE RESUME FOR DB"
ログの状態は以下の通り。
項目 | 保持ファイル |
---|---|
PRIMARYアクティブログ | S0000025.LOG〜S0000037.LOG |
PRIMARYアーカイブログ | S0000019.LOG〜S0000025.LOG |
STANDBYアクティブログ | S0000015.LOG〜S0000035.LOG |
STANDBYアーカイブログ | S0000007.LOG,S0000008.LOG,S0000011.LOG |
オンライン中
# オンライン中(ログアーカイブも発生)
for i in $(seq 21 30); do
db2 "INSERT INTO Hogetbl VALUES (${i}, 'geho')"
done
db2 "TERMINATE"
db2 "ARCHIVE LOG FOR DB HADRDB"
db2 "CONNECT TO HADRDB"
for i in $(seq 31 40); do
db2 "INSERT INTO Hogetbl VALUES (${i}, 'geho')"
done
find /hadrdb -name "*.LOG" # アーカイブには#19〜#26まで
障害発生(表スペースとログの多重)
rm -rf /hadrdb/tbsp[12]/* /hadrdb/trnlog/*
復旧(表スペースとログ)
PRIMARYはログも壊れているので、バックアップ取得時点に(つまりid=11追加まで)戻る。STANDBY側が進んだ状態になってしまうので、STANDBY側も復旧が必要になる。
db2stop force
cd /;
rm -rf /hadrdb/dbpath/* /hadrdb/tbsp[12]/* /hadrdb/trnlog/*
tar xvf /tmp/dbbkup2.tar ./hadrdb/dbpath ./hadrdb/tbsp1 ./hadrdb/tbsp2 ./hadrdb/trnlog
db2start
リカバリスタート
db2 "ACTIVATE DB HADRDB"
db2 "RESTART DB HADRDB WRITE RESUME"
db2pd -hadr -db hadrdb
db2 "CONNECT TO HADRDB"
db2 "SELECT * FROM Hogetbl"
id=11まで戻り、HADRもpeer。
シナリオ3(STANDBY強制TAKEOVER後の表スペース障害)
データはid=11まで入っている状態から状況開始。
db2 "CONNECT TO HADRDB"
db2 "INSERT INTO Hogetbl VALUES(12, 'piyo')"
db2 "FLUSH BUFFERPOOLS ALL"
db2 "SET WRITE SUSPEND FOR DB INCLUDE LOGS"
cd /; tar cvf /tmp/dbbkup3.tar ./hadrdb # PRIMARY/STANDBY
db2 "SET WRITE RESUME FOR DB"
for i in $(seq 13 20); do
db2 "INSERT INTO Hogetbl VALUES(${i}, 'geho')"
done
db2 "TERMINATE"
db2 "ARCHIVE LOG FOR DB HADRDB"
db2 "CONNECT TO HADRDB"
db2 "INSERT INTO Hogetbl VALUES(21, 'gehhogehho')"
# --- 被災想定 ---
db2 "TERMINATE"
db2 "DEACTIVATE DB HADRDB"
db2stop
# 強制テイクオーバー
db2 "TAKEOVER HADR ON DB HADRDB BY FORCE"
db2 "CONNECT TO HADRDB"
db2 "SELECT * FROM Hogetbl"
# 旧PRIMARYで最後に追加したid=21まで存在
db2 "INSERT INTO Hogetbl VALUES(22, 'standbyhoge')" # さらに更新
泣きっ面に蜂でここで障害が発生(オフサイトの構成をケチるからこうなる?)。バックアップ断面(プライマリサスペンド中)から戻す。
rm -rf /hadrdb/tbsp[12]/* # 表スペース障害
db2stop force # インスタンス停止
rm -rf /hadrdb/dbpath/*
cd /; tar xvf /tmp/dbbkup3.tar ./hadrdb/dbpath ./hadrdb/tbsp1 ./hadrdb/tbsp2
db2start
db2 "ACTIVATE DB HADRDB" # これはうまくいく
db2 "CONNECT TO HADRDB" # SQL1776N The command cannot be issued on an HADR database. Reason code = "1".
db2 "TAKEOVER HADR ON DB HADRDB BY FORCE"
SQL1273N An operation reading the logs on database "" cannot continue because
of a missing log file "" on database partition "" and log stream "".
ログがないので致命的に動けなくなってしまう。STANDBY時はPRIMARYの言いなりでログを管理し、テイクオーバーした時点でようやく自分でログの管理を始める。この場合、メタデータや表スペースがSTANDBY状態に戻るのに、ログだけはテイクオーバー(昇格)後のデータしか持っていないため、テイクオーバー時に必要なログがない。
状況を巻き戻して、再度ログ領域も含めてリストア実施する必要がある(つまりTAKEOVER直前まで戻る)。
db2stop force
rm -rf /hadrdb/dbpath/* /hadrdb/tbsp[12]/* /hadrdb/trnlog/*
cd /; tar xvf /tmp/dbbkup3.tar ./hadrdb/dbpath ./hadrdb/tbsp1 ./hadrdb/tbsp2 ./hadrdb/trnlog
db2start
db2 "ACTIVATE DB HADRDB"
db2pd -hadr -db hadrdb # STANDBYのまま
db2 "TAKEOVER HADR ON DB HADRDB BY FORCE"
db2pd -hadr -db hadrdb # PRIMARY昇格
db2 "CONNECT TO HADRDB"
db2 "SELECT * From Hogetbl" # id=12の時点(つまりバックアップ取得時点)まで戻った
というわけで、強制TAKEOVERしたら早めにバックアップを取っておいた方が安心できる。
シナリオ4(テイクオーバーのフォールバック)
さっきの被災は実はフェイク(訓練)だった。よかった、誰も悲惨な目にあってはいなかった。
というわけでもともとの構成に戻さなくてはならない場合。こういうときはPRIMARY/STANDBY側で全て(アクティブログも含めて)を戻してしまえばよい。のだが、ここでひとつ注意しておかなければいけない点がある。
HADRのSTANDBYでは、通常時ログのアーカイブは行わず、テイクオーバーした時点で自律的にログのアーカイブを開始する。つまり、テイクオーバー直前までCURRENTだったログが、テイクオーバー後にアーカイブされ(仮にS0010.LOGとする)、DBパス、表スペースおよびアクティブログのすべてを戻した場合、STANDBYだったときのS0010.LOGがアクティブログディレクトリに復元される。アーカイブログとアクティブログの両方に同じシーケンスナンバーのログがあり、かつアーカイブログのほうが新しいという状態になる。
この状態でPRIMARYでクラッシュリカバリ(RESTART DB)したときに、アーカイブにあるログからアクティブログ領域へログファイルを持ってくるという動作をする。すると、PRIMARYとSTANDBYとでLSNが不整合(PRIMARY < STANDBY)となり、クラッシュリカバリに失敗する(STANDBYデータベースが落ちる)。
なお、RESTART DBする前にPRIMARY側でACTIVATEコマンドを実行するとエラーになるが、この後のRESTART DB時にSTANDBY側でこのようなログの更新は行われないように見える。またRESTART DBではなくdb2inidb
からのROLL FORWARDではこの現象は発生しない。
いずれにせよ、STANDBYにアーカイブログはいらないので、訓練やテストなどでテイクオーバーした後は、戻しのときにSTANDBY側のアーカイブログを削除しておくのが吉。
db2stop force
# STANDBY
rm -rf /hadrdb/arclog/*
# PRIMARY/STANDBY
rm -rf /hadrdb/dbpath/* /hadrdb/tbsp[12]/* /hadrdb/trnlog/*
cd /; tar xvf /tmp/dbbkup3.tar ./hadrdb/dbpath ./hadrdb/tbsp1 ./hadrdb/tbsp2 ./hadrdb/trnlog
db2start
# STANDBY
db2 "ACTIVATE DB HADRDB"
# PRIMARY
db2 "RESTART DB HADRDB WRITE RESUME"
db2 "CONNECT TO HADRDB"
db2 "SELECT * FROM Hogetbl" # id=12まで
db2 "TERMINATE"
db2pd -hadr -db hadrdb # peer
ポイントを簡単にまとめ
バックアップ・リカバリの留意点
物理バックアップでは、論理バックアップ(BACKUP DATABASE)でDb2が自動でやってくれるような各データの整合性確保を自分でやらなければならない。RPOがバックアップ取得時であれば、ログも含めてその時点まで戻してやるのがシンプルではある。
実際には、表スペースだけ障害になったのか、つまりログが生きているのかどうかすぐにはわからないケースもあるので、まずは表スペースとDBパスを戻してdb2inidbからのROLL FORWARD(またはRESTART DBでクラッシュリカバリ)を試み、ダメだったらログも戻す等、状況に応じてやれるところまでやるしかない。
HADRでの留意点
STANDBY側ではPRIMARY側からログを制御され、TAKEOVER後にSTANDBY側で設定した内容で動く。TAKEOVER時にSTANDBY時代のログは切り捨てられて整理される。このため、TAKEOVER前に取った表スペース・DBパスを戻すとログと整合性がとれなくなる
どこまで復旧要件があるのか次第だが、被災したその日にリストアが必要になるようなケースを想定する、あるいは想定しなくてもよい理由が思い浮かばないのなら、TAKEOVER後にも同じようにバックアップをとってよくのが吉。
参考資料
SET WRITEコマンド
RESTART DATABASEコマンド
db2inidbコマンド
JDBCタイムアウトパラメータ
-
AWSのEBSスナップショットなどでも同様 ↩