OOS ごとバックアップ、リカバリの方法 −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− # トランザクションの特性について #--------------------------------------------------------------------------------------------- トランザクションセーフなデータベースは、必然的に以下のACID 特性が要求されます。 1. Atomicity(原子性) トランザクションブロック内の処理は「すべて実行される」か「すべて実行されない」かのいずれかであること。原子性は、begin commit rollback コマンドにて実現されています。 2. Consistency(一貫性) トランザクション内で、矛盾なくデータ操作を行うことができ、処理後のデータベースが整合性を保っていること。 3. Isolation(隔離性) データベースに対して処理を行う際に、他の処理から完全に隔離されるようにすること。隔離方式は、ロック方式とMVCC 方式がありますが、PostgreSQL もMySQL も、READ ロック、WRITE ロックが競合しない、MVCC 方式をディフォルトで採用しています。 MVCC とは、多版型同時実行制御と呼ばれており、データの一貫性を保証するためのデータ蓄積方法のことを言い、「追記方式」と「ロールバック方式」があります。 PostgreSQL ではデータを追記する追記方式を用いますが、MySQL では、変更前データをロールバックセグメントに記憶するロールバック方式を用いることで隔離性を実装しています。 4. Durability(耐久性) データベースシステムを高いパフォーマンスで運用でき、データを不整合なしにハードディスク上に記憶できること。 データを操作する時、直接ディスクを読み書きしていては効率がよくありません。そこで、RDBMS ではデータ操作をなるべく共有バッファ上で行うことでパフォーマンスを維持しています。ただし、更新データが共有バッファ上にある状態でOS のクラッシュや電源障害が起きたらどうなるでしょうか?当然データをディスクに書き込んでいないので、紛失することとなります。 そこで、PostgreSQL やMySQL では、データ紛失の危険性をWAL 方式により回避しています(耐久性を維持しています)。WAL 方式では、データをハードディスク上に直接書き込むことなく、データベースへの変更をシーケンシャルアクセスなトランザクションログに記憶し、定期的にWRITER によりハードディスクに書き込んでいます。 OS クラッシュや電源障害が発生した際は、データベースサーバーを再起動することで、自動的にトランザクションログが読み込まれ、トランザクションログを基に障害発生時までデータをロールフォアードします。また、コミットしていないデータはロールバックさせます。 ただ、トランザクションログはOS クラッシュや電源障害に対応するためのものなので、通常は数MByte 程度しかディスクに保存しません。 では、ディスク障害時の耐久性はどのように保証するのでしょうか?これは、フルバックアップデータ、アーカイブログによって保証されます。フルバックアップデータとは、ある時点での共有バッファ、ディスク上の全てのデータのバックアップ、アーカイブログとは、フルバックアップからの差分データです。 通常は、トランザクションログが上書きされる前に、アーカイブログとして保存され、リカバリに利用されます。 # バックアップについて #--------------------------------------------------------------------------------------------- サーバーがクラッシュするケースは多々あり、例えば、次の事象が考えられます。 1 : OS のクラッシュ 2 : 電源落ち 3 : ファイルシステムのクラッシュ 4 : ハードの問題 上記のうち、データベースのデータを復旧するという観点で話をすれば、おおむね2 パターンの復旧の仕方が考えられます。 ● ファイルシステムが動作する場合 1 または2 のケースは、サーバーが予期しないタイミングでクラッシュはしたけども、ファイルシステムは生きており、再起動後にアクセスできる状態です。このとき、トランザクションセーフなデータベースであれば、再起動した時点でリカバリ処理を行います。つまり、コミットされていないものに関してはロールバックを行い、コミットされているものはトランザクションログよりロールフォアードします。リカバリーの経過状況はエラーログを通じてユーザーに通知されます。 ● ファイルシステムが動作しない場合 3 または4 のケースは、サーバーが予期しないタイミングでクラッシュし、さらに再起動できないほどファイルシステムが壊れてしまった状態です。こうなるとサーバー自体を再構築するので、データベースのデータも再構築することになります。このとき復旧の種となるべきものはフルバックアップデータ、アーカイブログでしかなく、このデータを基にデータベースを復旧(リストア&リカバリ) させます。 ● バックアップ要件 トランザクションセーフなデータベースでは、フルバックアップは一つの独立したトランザクション内で行われます。すなわち、フルバックアップを開始した時点での一貫性のあるデータをバックアップできます。フルバックアップ開始時のスナップショットデータをバックアップ対象としますので、データベースサーバーを止めること無くバックアップを行うことができます。 しかし、フルバックアップのみでは障害発生時に回復できるのはフルバックアップ時のデータまでです。これでは、フルバックアップ時〜障害発生時までのデータは紛失してしまうので、フルバックアップ時からのデータベースへの変更記憶であるアーカイブログを利用して障害発生時までデータを復旧させます。 通常、アーカイブログは定期的にバックアップするものではなく、データとは別のディスクに随時記憶することで保存します。こうすることで、データが記憶してあるディスクが障害にてダウンしたとしても、アーカイブログが記憶してあるディスクにより障害発生時までのデータは保証されることになります。 ※ トランザクションログ、アーカイブログの違い トランザクションログはアーカイブログとは別ものです。トランザクションログはあくまでも上記の1 または2 のような一時的な障害のために利用するものであり、3 または4 のような恒久的な障害に利用されるものではありません。そのため、ある程度ログが蓄積されると古いログが上書きされるような仕組みになっています。 # リストア、リカバリについて #--------------------------------------------------------------------------------------------- サーバーの電源障害、OS のクラッシュ障害時のリカバリはデータベースサーバーが自動的に行います。データベースサーバーを再起動した時点でトランザクションログを基にロールフォアード、もしくはロールバックを実施し、データベースの一貫性を保証します。 その反面、ディスク障害やサーバー自体の障害時のリカバリは、サーバーを作り直し、バックアップデータより障害復旧するしかありません。 ディスク障害時の理想的なリカバリは障害発生時までデータを復旧させることです。バックアップデータとして、フルバックアップデータしか取らないシステムでは、フルバックアップデータをリストアし、障害復旧しますが、それだけでは完全に障害発生時までデータが復旧されたということにはなりません。 できるだけ障害発生時までのデータを復旧したいとのことであれば、フルバックアップからの差分データをアーカイブログとして保存し、障害発生時にはフルバックアップデータをリストア後、アーカイブログよりリカバリ(ロールフォアード) を行います。 ● リストア、リカバリ要件 フルバックアップからデータ復旧する作業をリストアと呼びます。それに対して、アーカイブログからバックアップ日時より障害発生時までのデータ復旧を行う作業をリカバリと呼びます。リカバリを行うためにはアーカイブログが必要不可欠です。 ディスク障害時には、まずフルバックアップデータよりデータ復旧を行います。その後、アーカイブログより、フルバックアップ時〜障害発生時までのデータまでのデータを復旧します。 # PostgreSQL 7 系のバックアップ、リカバリ方法 #--------------------------------------------------------------------------------------------- PostgreSQL 7 系では、アーカイブログを記憶する仕組みがないため、障害発生時までのデータを完全に復旧することができません。フルバックアップした時点までのデータが復旧されます。また、データのバックアップとは別に、システムデータをバックアップする必要があり、リストア時には、システムデータを復旧後、データを復旧します。 ● バックアップの手順 システムデータをpg_dumpall コマンドにてフルバックアップします。 # pg_dumpall -U postgres -g > [ダンプファイル名] -g にてシステム情報をダンプします ↓ データをpg_dump コマンドにてフルバックアップします。 # pg_dump -U postgres -Ft -o -b [データベース名] > [ダンプファイル名] -Ft にて出力するダンプファイル形式をtar 形式にします -o にてOID までもダンプします、これはPG にてOID を直接操作している場合は必ずつけてください -b にてラージオブジェクトをダンプします ※ ラージオブジェクトについて pg_dump では、-Fc を指定することでダンプファイル形式をPostgreSQL 独自のカスタム形式にすることができますが、-b にてラージオブジェクトを同時にダンプする際には -Fc を指定する意味はありません。 ● リストアの手順 PostgreSQL のデータディレクトリ、およびデータクラスタ領域を作成します。 # mkdir /var/pgsql # su postgres # initdb --encoding=EUC_JP --no-locale --pgdata=/var/pgsql ↓ postmaster を起動します。 # /etc/rc.d/init.d/postgresql start ↓ システムデータをpsql コマンドにてリストアします。 # su root # psql -U postgres template1 < [ダンプファイル名] ↓ データのリストアはpg_restore コマンドで行います。 # createdb -U postgres [データベース名] # pg_restore -U postgres -d [データベース名] [ダンプファイル名] -d にてリストアするデータベース名を指定します ※ ラージオブジェクトについて ラージオブジェクトをリストアする際は、初めにリストアするデータベースを作成します。その後、pg_restore の-d で指定されたデータベースに対してデータをリストアします。これ以外の方法でラージオブジェクトをリストアする方法がありません。 なお、pg_restore コマンドでは、-l で出力したリストアされるSQL ファイルに対して、SQL 順序やSQL の削除、追加作業を行い、-L で修正後のSQL ファイルを基にリストアを行うことができますが、これはダンプファイルにラージオブジェクトを含まない場合のみ有効です。そう考えると、ラージオブジェクトをダンプする場合は、pg_dump の-Fc オプションは指定する意味がありません。 また、pg_restore はpostgres ユーザーで実行しますが、テーブル自体の所有者情報はpg_restore にて完全にリストアされます。 # PostgreSQL 8 系のバックアップ、リカバリ方法 #--------------------------------------------------------------------------------------------- PostgreSQL 8 系では、フルバックアップとアーカイブログをサポートしているので、障害発生時までデータを復旧させることができます。通常、トランザクションログはpg_xlog 配下に、アーカイブログはpostgresql.conf で指定された場所(例ではpg_archive) に記憶されますが、耐久性維持のため、pg_xlog 、pg_archive は別ディスクへのシンボリックリンクで実装します。 ● バックアップ、リカバリ準備 pg_xlog を別ディスク(/backup 配下) へのシンボリックリンクとし、権限を変更します。 # cd /var/pgsql # ln -s /backup/pg_xlog ./pg_xlog # chown postgres:database /backup/pg_xlog アーカイブログを記憶するディレクトリを別ディスク上に作成し、権限を変更します。 # mkdir /backup/pg_archive # chown postgres:database /backup/pg_archive postgresql.conf にアーカイブログを作成する設定を行います。 # vi postgresql.conf archive_command = 'cp %p /backup/pg_archive/%f' とする ● バックアップの手順 pg_start_backup、pg_stop_backup 関数にてデータクラスタ全体のフルバックアップを行います。 # cd /var # psql -U postgres -c "select pg_start_backup('/backup/[日付 YYYYMMDD].tar')" template1 # tar cpf /backup/[日付 YYYYMMDD].tar ./pgsql # psql -U postgres -c "select pg_stop_backup()" template1 ※ アーカイブログの取扱いについて pg_stop_backup を実行した時点で、pg_xlog とpg_archive のディレクトリ内に00000001.00A2CC10.backup などのバックアップ履歴ファイルができます。この時点で、バックアップ履歴ファイルより前の日時のアーカイブログは削除できます。 ● リストア、リカバリの手順 リストア、リカバリは障害が発生したディスクによって対応方法が異なります。 事例1:データが記憶されているディスクに障害が発生した場合 フルバックアップデータよりデータクラスタ全体をリストアします。 # cd /var # tar xf /backup.tar このとき、/var/pgsql 配下の権限がpostgres になっていることを確認すること ↓ /usr/local/pgsql/share/recovery.conf.sample をデータクラスタ内にコピーし、編集します。 # cd /var/pgsql # cp /usr/local/pgsql/share/recovery.conf.sample ./recovery.conf # vi recovery.conf recovery_command = 'cp /backup/pg_archive/%f %p' とする ↓ postmaster を起動します。 # /etc/rc.d/init.d/postgres start postmaster を起動すると同時に、pg_archive 配下のアーカイブログを元にリストアが自動的に行われます。また、pg_archive に含まれない最新のデータに関しては、pg_xlog 配下のトランザクションログを元にリストアされます。 ↓ リカバリ完了!! 事例2:トランザクションログ、アーカイブログが記憶されているディスクに障害が発生した場合 postmaster が強制ダウンしているので、PID ファイルを削除します。 # rm -rf /var/pgsql/postmaster.pid ↓ トランザクションログを記憶するディレクトリを作成し、権限を変更します。 # mkdir /backup/pg_xlog # mkdir /backup/pg_xlog/archive_status # chown -R postgres:database /backup/pg_xlog ↓ アーカイブログを記憶するディレクトリを作成し、権限を変更します。 # mkdir /backup/pg_archive # chown -R postgres:database /backup/pg_archive ↓ pg_resetxlog コマンドにてトランザクションログ領域を初期化します。 # su postgres # pg_resetxlog -f /var/pgsql ↓ postmaster を起動します。 # /etc/rc.d/init.d/postgres start postmaster を起動すると同時に、初期化後のpg_xlog が読み込まれ、正常に運用可能となります。 ↓ 念のため、共有バッファ、データディスク上のデータのフルバックアップを行います。 # cd /var # psql -U postgres -c "select pg_start_backup('/backup/[日付 YYYYMMDD].tar')" template1 # tar cpf /backup/[日付 YYYYMMDD].tar ./pgsql # psql -U postgres -c "select pg_stop_backup()" template1 この時点では、コミット済みデータが共有バッファ上にある可能性があります。トランザクションログをフラッシュしてしまったので、このままでは次回チェックポイントまでにトランザクションログを用いてロールフォアードすることができません。ですから、フルバックアップを行うことで、共有バッファ上のデータを退避しておくのです。 ↓ リカバリ完了!! # MySQL のバックアップ、リカバリ方法 #--------------------------------------------------------------------------------------------- MySQL では、フルバックアップとアーカイブログ(MySQL ではバイナリログと呼ぶ) をサポートしているので、障害発生時までデータを復旧させることができます。通常、アーカイブログはmy.cnf のlog-bin パラメータで指定された場所に記憶されますが、耐久性維持のため、ここで指定する場所は別ディスクとします。 アーカイブログはトランザクションログとは別に管理され、常に同期書き込みされますので、PostgreSQL 8 系のように最新データのリカバリをトランザクションログに頼ることはなく、アーカイブログのみでリカバリ可能です。 ※ 注意 ) 障害発生時までリカバリするため、MyISAM、innodb などのストレージ形式に関係なくアーカイブログはとるべきです。 ● バックアップ、リカバリ準備 ディフォルトでMySQL はアーカイブログを取らないので、my.cnf のlog-bin パラメータをつけた上でmysql を起動してください。 アーカイブログを記憶するディレクトリを別ディスク(/backup 配下) 上に作成し、権限を変更します。 # mkdir /backup/my_archive # chown mysql:database /backup/my_archive my.cnf ファイルにlog-bin パラメータを追加し、編集してください。 # cd /var/mysql # vi my.cnf log-bin = /backup/my_archive/bin.log ● バックアップの手順 データをmysqldump コマンドにてフルバックアップします。 # mysqldump -u root -p[パスワード] --single-transaction --opt --all-databases --flush-logs ? > [ダンプファイル名] --all-databases はすべてのデータベースをダンプします --opt は非トランザクションセーフなデータベースをバックアップする際に共有ロックをかけます --flush-logs はバイナリログをロテートします ※ アーカイブログの取扱いについて --flush-logs オプションを指定することで、フルバックアップ完了時にアーカイブログがロテートされます。次回のフルバックアップまでの間、ロテートされたアーカイブログを開始位置として、ログが記憶されます。この時点で、ロテートされたアーカイブログより前の日時のアーカイブログは削除できます。 ※ トランザクションセーフなデータベースと非トランザクションセーフなデータベースの違い トランザクションセーフなデータベース形式(INNODB) では、--single-transaction オプションを用いることで、一つの独立したトランザクション内でバックアップが行われます。すなわち、フルバックアップを開始した時点での一貫性のあるデータをバックアップできます。 非トランザクションセーフなデータベース形式(MYISAM 等) では、ロックすることなしに、順次テーブルのselect 結果をバックアップするので、データの一貫性が失われます。これを防ぐために、--opt オプションによって、データベース全体をロックすることで一貫性のあるバックアップを行います。 ● リストア、リカバリの手順 リストア、リカバリは障害が発生したディスクによって対応方法が異なります。 事例1:データが記憶されているディスクに障害が発生した場合 mysqld を単独起動します。 # /usr/local/mysql/bin/mysqld_safe --skip-networking --skip-grant-tables & ↓ フルバックアップデータをリストアします。 # mysql -u root -p[パスワード] < [フルバックアップデータ] ↓ アーカイブログをテキスト形式に変換した後、リカバリを行います。 このとき、前回フルバックアップした時点から障害発生時までのアーカイブログを時系列順にリカバリを行うことに注意してください。 # mysqlbinlog binary-log.0000** binary-log.0000** ...(旧->新) > [変換後テキスト形式ファイル] # mysql -u root -p[パスワード] < [変換後テキスト形式ファイル] ※ 注意 リストアに失敗した場合は [変換後テキスト形式ファイル] の先頭に”SET FOREIGN_KEY_CHECKS=0;”を追加してください。 ↓ 単独起動したmysqld を停止し、通常通りmysqld を起動します。 # kill `cat /var/mysql/ns.pid` # /etc/rc.d/init.d/mysql start ↓ リカバリ完了!! 事例2:アーカイブログが記憶されているディスクに障害が発生した場合 mysqld を停止します。 # /etc/rc.d/init.d/mysql stop ↓ アーカイブログを記憶するディレクトリを作成し、権限を変更します。 # mkdir /backup/my_archive # chown -R mysql:database /backup/my_archive ↓ mysqld を起動します。 # /etc/rc.d/init.d/mysql start mysqld を起動すると同時に、my_archive が読み込まれ、正常に運用可能となります。 ↓ 念のため、共有バッファ、データディスク上のデータのフルバックアップを行います。 # mysqldump -u root -p[パスワード] --single-transaction --opt --all-databases ? > [ダンプファイル名] この時点では、コミット済みデータが共有バッファ上にある可能性があります。アーカイブログをフラッシュしてしまったので、このままでは次回チェックポイントまでにアーカイブログを用いてロールフォアードすることができません。ですから、フルバックアップを行うことで、共有バッファ上のデータを退避しておくのです。なお、アーカイブログはmysqld を起動する際にロテートされているので、ここでは、--flush-logs は指定する必要がありません。 ↓ リカバリ完了!!