MySQL で作業してるときにうっかりオペミスしてしまったときの対処方法。
作業前にダンプ取ってて、作業終了後すぐにオペミスに気づいた場合は、そのダンプから戻せば良いですが、ダンプ取ったのが結構前だったとか、オペミスに気づいたのが時間をおいてからだったとかって時の復旧方法です。
前提条件として、以下の状況だった場合のみです。バイナリログを取ってない場合はあきらめましょう。
- バイナリログを取っている( /etc/my.cnf で log-bin=mysql-bin とか設定してある )
- mysqldump 等でバックアップしている
- バックアップ時点でのバイナリログの書き込み位置を保存している。またはバックアップした日時が特定できる
バイナリログの残し方
/etc/my.cnf に以下を追記しておくと に datadir で指定しておいたディレクトリに mysql-bin.00001 …というファイルが作成されていきます。
log-bin=mysql-bin
オペミスした時間の特定
mysqlbinlog コマンドを使用してオペミスした時間近辺で発行された SQL 文を確認し、バイナリログの書き込み位置を特定します。
例えばオペミスした時間が 2014/3/10 12:30 〜 13:00 辺りだった場合は
$ sudo -u mysql mysqlbinlog /path/to/mysql-bin.00001 \ --start-datetime '2014-03-10 12:30:00' \ --stop-datetime '2014-03-10 13:00:00' | more # at 20000 #140310 12:30:05 server id 1001 end_log_pos 20000 Query thread_id=106629 exec_time=0 error_code=0 SET TIMESTAMP=1394422205/*!*/; SET @@session.pseudo_thread_id=106629/*!*/; SET @@session.foreign_key_checks=1, @@session.sql_auto_is_null=0, @@session.unique_checks=1, @@session.autocommit=1/*!*/; SET @@session.sql_mode=0/*!*/; SET @@session.auto_increment_increment=1, @@session.auto_increment_offset=1/*!*/; /*!\C utf8 *//*!*/; SET @@session.character_set_client=33,@@session.collation_connection=33,@@session.collation_server=33/*!*/; SET @@session.lc_time_names=0/*!*/; SET @@session.collation_database=DEFAULT/*!*/; BEGIN /*!*/; # at 20001 #140310 12:30:05 server id 1001 end_log_pos 20002 Query thread_id=106629 exec_time=0 error_code=0 use `wordpress`/*!*/; SET TIMESTAMP=1394422205/*!*/; UPDATE `wp_usermeta` SET `meta_value` = 'hogehoge' WHERE `user_id` = 1 AND `meta_key` = 'user_login_log-cookie_expired' :
このように実行された SQL 文が生で表示されるので、どこでミスったか特定しましょう。
特定できたら SQL 文の直前にある # at {number}
という行を見るとバイナリログへの書き込み位置が数字で書かれているはずです。
こちらをメモっておきます。
最新バックアップした時のバイナリログ書き込み位置がわからない場合も、同様にしてバックアップ終了時間後に最初に実行された SQL 文を特定して書き込み位置を特定します。
バイナリログからの復元
まず、いきなり本番環境で戻しをすると怖いので別環境を作りましょう。
OS インストールしたり MySQL インストールしたりするのが面倒くさい場合は 網元 AMI ってので立ち上げると簡単らしいですよ (ステマ
まずは、最新のバックアップからデータを戻します。
その後、本番サーバからバイナリログをコピーしてきて先ほど特定したバックアップ後のバイナリログ書き込み位置、オペミスした直前のバイナリログ書き込み位置を元にその間に実行された SQL 文を生成して流し込みます。
バックアップ後のバイナリログ書き込み位置が 10000、オペミスした直前のバイナリログ書き込み位置が 20000 だった場合は以下のようになるでしょう。
$ mysqlbinlog /path/to/mysql-bin.00001.bak \ --start-possion 10000 --stop-possion 20000 \ | mysql -u ユーザ名 -p DB名
取ってて良かったバイナリログ。