2012年6月1日金曜日

mysqlのバックアップとリカバリ


mysqlサーバを運用する上でバックアップをどうするか検討する。
データベースエンジンはInnoDBを使うことを前提とする。
事前にmysqlサーバの設定は確認しておいてほしい。


何をバックアップするか
バックアップ対象として考えられるのは次だろう。

① データベースファイル
② バイナリ(ロールフォワード)ログ
③ 設定ファイル(my.cnf)
④ クエリ、更新、エラーログ
⑤ 実行ファイル

①はテーブルやその中身が含まれるので当然バックアップが必要である。

②は①をバックアップした時点から、ロールフォワードさせる(障害発生直前までの状態に
戻す)ために利用するため、①とセットで必要である。
バイナリログはmysqlの機能で保存するようなことはできないため、
cpなりscpしてコピーをとっておけばいい。

③はサイズの少ないただのテキストファイルであり工数もかからないので
保存しておいて損はないだろう。こちらもcpなりscpでもしておけばよい。

④は何かの解析に利用する必要性があるのならとってもいてもいだろう。

⑤はインストールしなおせばいいのでわざわざバックアップしなくてもいいだろう。


ここでは①のバックアップ手順を検討し、
また②を使いどのようにリカバリするのかをまとめておく。


バックアップ手順
【コールドバックアップ(cold backup)】
mysqlを停止してバックアップを取得する。

プロセスを停止する。
# /etc/init.d/mysqld stop

バックアップをとる。
# cp -rp /var/lib/mysql/ /バックアップ領域/

プロセスを起動させる。
# /etc/init.d/mysqld start

※mysqld_safe経由でmysqldデーモンは起動させるべきである。
起動スクリプトの中でmysqld_safeが利用されているため上記の起動手段をとっている。




【オンラインバックアップ(online backup)】
mysqldumpコマンドのオプションによって大きく2通り手段がある。

・読み取り許可、更新不許可のバックアップ
# mysqldump -uroot --all-databases --lock-all-tables --master-data=2 --flush-logs --quick > dump.sql

・ロックかけないバックアップ
スナップショットを利用してバックアップを取得する。
# mysqldump -uroot --all-databases --single-transaction --master-data=2 --flush-logs --quick > dump.sql

--lock-all-tables
共有ロックをかける。読み込みはできるが、更新処理はできなくなる。

--single-transaction
ロックをかけないでバックアップできる。
ある時点でのスナップショットをとる機能を利用して実現させる。
スナップショットとは、同一トランザクション内であれば
それ以降のSELECTの結果が、たとえ更新があったとしても
スナップショット取得時点のテーブルの中身が返される。
それを応用しているのである。

スナップショットの取得は一瞬で終わる。
しかしその前に、共有(読み取り)ロック、コマンドで表現すれば、
FLUSH TABLES WITH READ LOCK というロックをかける。
そのロックにはどの程度時間がかかるのか。それは下を参考にしてほしい。
http://stnard.jp/2010/04/26/151/

--master-data=2
バックアップファイルの先頭に、バイナリログの位置情報が記録される。
その情報がないと、リストア時どこからバイナリログをあてるか分からなくなる。

--flush-logs
バイナリログをローテーションさせる。
バックアップ取得のタイミングでバイナリログが新しくなる。

--quick
出力をメモリ上のバッファに貯めないで直接標準出力させる。


マスタ、スレーブ構成にしてれば当然スレーブ側でバックアップしたいだろう。
しかしスレーブでバックアップし、マスタをリストアする場合、
マスタではどのバイナリログをあてるのか分からない。
mysql5.5以降では--dump-slaveという引数が使える。
このオプションを使えばマスタのバイナリログの位置情報を記録してくれる。



リカバリ手順
mysqldumpで取得したバックアップファイルからリストアする。
# mysql -u root < dump.sql

バイナリログをどこからあてるかを確認する。
この例ではバイナリログファイル名はmysqld-bin.000004、位置は25327698から必要であることが分かる。
言い換えるとそれより前のログは不要である。
# less dump.sql
CHANGE MASTER TO MASTER_LOG_FILE='mysqld-bin.000004', MASTER_LOG_POS=25327698;

ロールフォワードさせるために必要なログを抽出する。
# mysqlbinlog --position=25327698 mysqld-bin.000004 > binglog.sql

ロールフォワードさせる。
# mysql -u root < binlog.sql

ここで注意が必要である。
バイナリログがローテートされているのであれば、
それ以後のログも適用させなければならない。
mysqld-bin.000004の次にmysqld-bin.000005というファイルがある場合。

# mysqlbinlog mysqld-bin.000005 > binglog2.sql
# mysql -u root < binlog2.sql



バイナリログのローテート
バイナリログをいつまでも保存しておく必要はない。
ではどのログなら削除できるのだろうか。

サーバ上のバイナリログをリストさせる。
mysqldが起動してからのバイナリログがすべて表示されるはずである。
# mysql -u root  -e "show binary logs"
+-------------------+------------+
| Log_name          | File_size  |
+-------------------+------------+
| mysqld-bin.000001 | 1073742054 | 
| mysqld-bin.000002 | 1073742048 | 
| mysqld-bin.000003 | 1073742014 | 
| mysqld-bin.000004 | 1073741891 | 
| mysqld-bin.000005 | 1073741958 | 
+-------------------+------------+

バイナリログを削除する。
先のリカバリ時にはmysqld-bin.000004からが必要だったことを確認した。
そのためそれ以前のログはは削除してもよいことになる。
# mysql -u root -e "PURGE MASTER LOGS TO 'mysqld-bin.000004';"

上記コマンドはレプリケーション中でも利用できる。
削除予定のバイナリログを読み込んでいるスレーブがあればエラーで停止する。
ただし、スレーブが休止中であれば読み込みセッションが開いていないため、削除される。
削除されると休止中スレーブが復帰しても複製はできない。

バイナリログを再び確認してみる。
# mysql -u root  -e "show binary logs"
+-------------------+------------+
| Log_name          | File_size  |
+-------------------+------------+
| mysqld-bin.000004 | 1073741891 | 
| mysqld-bin.000005 | 1073741958 | 
+-------------------+------------+

日にちを指定してログを消すこともできる。
全体バックアップを取得したら、それ以前のバイナリログは削除して構わない。
例えば1日1回、0時にフルバックアップしているのなら、
その日より前のバイナリファイルを定期的に消していけばばよいだろう。
# mysql -u root -e "PURGE MASTER LOGS BEFORE 'YYYY-MM-DD hh:mm:ss';"

バックアップの成功の可否を見たのちに、削除処理用のツールをまわしてもよい。

#!/bin/sh

DATE=`date  -d "2 days ago" +"%Y-%m-%d 00:00:00"`
mysql -u root -e "PURGE MASTER LOGS BEFORE '$DATE';"

1日前でいいのだが、ディスクに余裕があれば、
念のため数日分残しておいても問題はない。