サーバーが完膚なきまでに死んでも
MySQLのデータを失わないための表
技
それでも失ったら申し訳ない
2018/03/10
yoku0825
PHPerKaigi 2018
この話をしようと思った理由
みなさんは お題箱 というサービスをご存知ですか?
俺はよく知りません()
Peing -質問箱- と別のものだということだけ知っています
1/88
この話をしようと思った理由
1月6日 19時頃にサーバ会社様(ConoHa様)にて発生
した障害が原因で、お題箱のデータベースサーバのシス
テムが破損してしまい、本日までサービスが稼働できな
い状況になっておりました。
サーバ会社の担当者様のご尽力もあり、1月9日 17時現
在、ひとまず復旧することができましたことをご報告い
たします。
1/6 19時頃から発生していた障害と投稿データの消失につ
いて – お題箱ブログ
2/88
この話をしようと思った理由
1月6日 19時頃にサーバ会社様(ConoHa様)にて発生
した障害が原因で、お題箱の データベースサーバ のシ
ステムが破損してしまい、本日までサービスが稼働でき
ない状況になっておりました。
サーバ会社の担当者様のご尽力もあり、1月9日 17時現
在、ひとまず復旧することができましたことをご報告い
たします。
1/6 19時頃から発生していた障害と投稿データの消失につ
いて – お題箱ブログ
3/88
( ´-`).oO(
yoku0825は激怒した
yoku0825にはサービスがわからぬ
yoku0825はDBAである
ALTER TABLEをし、 と遊んで暮らしてきた
けれどもデータベース障害に対しては、
人一倍に敏感であった
4/88
この データベース
サーバ がMySQLの
ことなのかどうか知
らないんですが
5/88
思ったん
です
6/88
( ´-`).oO(
yoku0825は激怒はしていないけど寂しくなった
もしお題箱の使っているデータベースがMySQLで
そのバックアップが正しく設計されていれば
こんな悲劇は起こらなかったかも知れない
7/88
(`・ω・) キリッ
くどいようだけどお題箱のデータベースが
MySQLかどうかは知らない
8/88
「サーバーが完膚なきまで
に死んでもMySQLのデー
タを失わないための表技」
はじまりまじまり
9/88
ちなみにPHP出て
きません(・∀・)ゞ
PHPerKaigi #とは何だったのか
10/88
\こんにちわ/
yoku0825@とある企業のDBA
オラクれない‐
ポスグれない‐
マイエスキューエる‐
生息域
Twitter: @yoku0825‐
Blog: 日々の覚書‐
MyNA ML: 日本MySQLユーザ会‐
MySQL Casual: Slack‐
11/88
はなすこと
ハードウェア障害とかでマスターがブートできないケース
ストレージ障害とかでファイルが救出できないケース
うっかりオペミスでデータをロールバックしないといけない
ケース
をカバーするためのバックアップとリストア手法
12/88
はなさないこと
ダウンタイムを短くするための冗長構成
バックアップ手法の優劣
リカバリーを速くする手段や方法
技術的な詳細
黒魔術
13/88
おしながき
バックアップのリストアの基本的な考え方1.
バックアップ手法のおさらい2.
複数のバックアップ手法を組み合わせて運用する3.
日々のバックアップにまつわるタスク4.
どこがどう壊れた時にどうリストアするか5.
ぼくがかんがえたさいきょうのサーバー1台で自作サービス
を作ってる時のちょっとのコストで実現するバックアップの
しくみ
※感じ方には個人差があります
6.
14/88
おしながき
バックアップのリストアの基本的な考え方1.
バックアップ手法のおさらい2.
複数のバックアップ手法を組み合わせて運用する3.
日々のバックアップにまつわるタスク4.
どこがどう壊れた時にどうリストアするか5.
ぼくがかんがえたさいきょうのサーバー1台で自作サービス
を作ってる時のちょっとのコストで実現するバックアップの
しくみ
※感じ方には個人差があります
6.
15/88
バックアップのリストアの基本的な考え方
mysqld --initialize して以来、こんなクエリーを突っ込んだ
MySQLがあるじゃろ?
最新の SELECT * FROM t1 は (1), (2), (3), (4), (5)‐
16/88
バックアップのリストアの基本的な考え方
新しく空っぽのMySQLを用意して、最初のクエリーから最後の
クエリーまで 過不足なく正しい順番で 適用するじゃろ?
INSERT INTO t1 VALUES (1)‐
17/88
バックアップのリストアの基本的な考え方
新しく空っぽのMySQLを用意して、最初のクエリーから最後の
クエリーまで 過不足なく正しい順番で 適用するじゃろ?
INSERT INTO t1 VALUES (2)‐
18/88
バックアップのリストアの基本的な考え方
新しく空っぽのMySQLを用意して、最初のクエリーから最後の
クエリーまで 過不足なく正しい順番で 適用するじゃろ?
INSERT INTO t1 VALUES (3)‐
19/88
バックアップのリストアの基本的な考え方
新しく空っぽのMySQLを用意して、最初のクエリーから最後の
クエリーまで 過不足なく正しい順番で 適用するじゃろ?
DELETE FROM t1 WHERE id = 1‐
20/88
バックアップのリストアの基本的な考え方
新しく空っぽのMySQLを用意して、最初のクエリーから最後の
クエリーまで 過不足なく正しい順番で 適用するじゃろ?
INSERT INTO t1 VALUES (1)‐
21/88
中略
22/88
バックアップのリストアの基本的な考え方
最後まで適用し終えれば、またデータは (1), (2), (3), (4),
(5) に戻るんじゃ
23/88
バックアップのリストアの基本的な考え方
全ての更新履歴があればそれを 全て正しく リプレイするこ
とで最後のデータが手に入る
リプレイを任意の場所で停止することで、任意の時点のデー
タの切断面を取り出すことができる
データの切断面をスナップショットという‐
任意の時点のデータの切断面から、更新履歴を使ったリプレ
イを再開することができる
スナップショット(ここではフルバックアップをリストアしたもの)に
続きの更新履歴をリプレイすることで更に別のスナップショットを作
成することができる
‐
24/88
バックアップのリストアの基本的な考え方
リプレイした時に 実行結果が異なる関数 を使っている場合
は 正しくリプレイできない ので、バックアップとして信頼
することができない
有名なのは NOW() はいいけど SYSDATE() はダメとか RAND() はダメ
とか
‐
25/88
おしながき
バックアップのリストアの基本的な考え方1.
バックアップ手法のおさらい2.
複数のバックアップ手法を組み合わせて運用する3.
日々のバックアップにまつわるタスク4.
どこがどう壊れた時にどうリストアするか5.
ぼくがかんがえたさいきょうのサーバー1台で自作サービス
を作ってる時のちょっとのコストで実現するバックアップの
しくみ
※感じ方には個人差があります
6.
26/88
バックアップ手法のおさらい
フルバックアップ
mysqldを停止させた状態でdatadir(/var/lib/mysql など)の丸ごと
コピー(コールドバックアップ && 物理バックアップ)
‐
mysqldump --single-transaction --all-databases (ホットバック
アップ && 論理バックアップ)
‐
xtrabackup --socket=/var/lib/mysql/mysql.sock --user=root --
stream=tar /var/lib/mysql | .." (ホットバックアップ && 物理バ
ックアップ)
‐
27/88
バックアップ手法のおさらい
フルバックアップ
その時点のデータの切断面(スナップショット)を作成する‐
経過はかっ飛ばしてその時点のデータだけを取り出すのでここからデ
ータを 遡ることはできない
‐
リプレイの手間を省けるのでバイナリーログを使ったリストアより
(同じ時点に着地するなら)早く済む
‐
28/88
バックアップ手法のおさらい
フルバックアップ
29/88
バックアップ手法のおさらい
バイナリーログバックアップ
crondでキックするなら rsync, scp, etc.‐
デーモン化してリアルタイムに mysqlbinlog -R --stop-never --raw‐
レプリケーションスレーブを作って log_slave_updates を有効にし
ておけばスレーブのdatadirにもバイナリーログが溜まる
‐
差分ではなく増分バックアップに当たる‐
30/88
バックアップ手法のおさらい
バイナリーログバックアップ
さっきの図で言うところのブロックを1つずつ記録したものがバイナ
リーログの「イベント」
厳密には「1行または1クエリーが1イベント」で「BEGIN, COMMITでそれぞれ1
イベント」なのでちょっとズレるけどまあそう思っていてそこまで問題はないはず
‐
バイナリーログにはヘッダーと複数のイベントが記録されている‐
イベントは「バイナリーログファイル名」と「ポジション(バイナリ
ログファイルの先頭からのオフセットバイト数)」で識別される
‐
初期化されて以降全てのバイナリーログがあれば任意の時点のスナッ
プショットが復元可能
‐
31/88
バックアップ手法のおさらい
バイナリーログバックアップ
32/88
バックアップ手法のおさらい
差分バックアップ
xtrabackupならできる‐
mysqldumpはテーブル構成による‐
無理してやることない‐
33/88
バックアップ手法のおさらい
物理バックアップ
MySQLがデータの格納に使っているファイルをそのまま複製する方
法
同じCPUアーキテクチャー, 同じバージョン, 同じコンフィグでリストア推奨
‐
mysqldを停止してから /var/lib/mysql をそのままtarとかrsyncと
か
mysqldを停止した状態でないと起動できないかもしれないデータセットが出来上
がる
地味に「マスターにバイナリーログが残っていれば起動するだけですぐさまレプリ
ケーションを再開できる」ので楽
‐
xtrabackup もこれ
こっちはmysqldを停止することなく(というかこっちはmysqldが動いてないと取
れない)物理バックアップが可能
‐
概ねリストアが早くてファイルサイズが大きい
インデックス(ソート済みのデータの部分集合)もファイルの中に入っている
ファイルが断片化していればバックアップも断片化の影響を受けたまま
‐
34/88
バックアップ手法のおさらい
論理バックアップ
ファイル構造に依存せずにSQLでデータを取ってくる(要は全部のテ
ーブルに SELECT * FROM .. を繰り返す)
なのでバージョンをまたいだりコンフィグが変わっている場合でもリストアができ
る
というかバージョンアップの手順の一つとして後悔されていたりもする(ex. 5.5
からmysqldump + 5.6にそれをリストア)
MySQL :: MySQL 5.6 リファレンスマニュアル :: 2.11.1.3 MySQL 5.5 から
5.6 へのアップグレード
‐
mysqldump がその代表格
派生としてパラレルでバックアップを採れる mydumper, mysqlpump
‐
概ねリストアが遅めでファイルサイズが小さい
インデックスやトランザクション管理領域はリストア時に新たに構築される
‐
35/88
バックアップ手法のおさらい
バイナリーログバックアップ
バイナリーログファイルには既に「直列化された(=正しい順番で並
んだ)」バイナリーログイベントが並んでいる
‐
なので出力されたバイナリーログファイルを抜け漏れなくちゃんと回
収してやればそれだけでOK
ただし レプリケーションフィルター を使用していないこと
‐
36/88
おしながき
バックアップのリストアの基本的な考え方1.
バックアップ手法のおさらい2.
複数のバックアップ手法を組み合わせて運用する3.
日々のバックアップにまつわるタスク4.
どこがどう壊れた時にどうリストアするか5.
ぼくがかんがえたさいきょうのサーバー1台で自作サービス
を作ってる時のちょっとのコストで実現するバックアップの
しくみ
※感じ方には個人差があります
6.
37/88
バックアップ手法の組み合わせ
フルバックアップ vs バイナリーログバックアップ
データを失わない、という文脈で大事になるのはバイナリーログバックア
ップ
‐
マスターが完膚なきまでに壊れてバイナリーログを取り出せなくなった時
に、どれだけ他のストレージにバイナリーログをコピーできているかがキ
モ
‐
38/88
バックアップ手法の組み合わせ
フルバックアップ vs バイナリーログバックアップの間隔
フルバックアップはリストアにかかる時間の短縮に重要
本当はバックアップとリストアにかかる時間を測って妥協できるポイントを探すの
が良いんだろうけれど、よくわからなければ取り敢えず日次かしらん
‐
バイナリーログバックアップはデータの完全性に重要
cronベースでrsyncやscpでやるだけでもノーガードよりはマシ(cronの実行間隔
が最大でデータをロストしうる期間になる)
レプリケーションスレーブを作って log_slave_updates で運用する、MySQL 5.6
とそれ以降の mysqlbinlog --read-from-remote-server --stop-never --raw なら
ほぼリアルタイムに回収できる
‐
39/88
バックアップ手法の組み合わせ
バックアップ、マスターから取るか? スレーブから取る
か?
コールドバックアップの場合はスレーブ一択(のはず)‐
mysqldump の場合、どっちから取るかで --master-data または --
dump-slave を打ち分けないといけない
‐
どっちでもいいと思うけど、バイナリーログバックアップと向き先を
合わせないといけない(いけなくはないけど手間が一気に増える)
‐
40/88
向き先を合わせる話の前に
4つ目のクエリーの時点で取ったフルバックアップに、6つ目の
クエリーの履歴からリプレイしちゃうと…?
あるいは1つ目のクエリーの履歴からリプレイしちゃうと…?
繋ぎ間違えるとデータは正しくリストアできない
41/88
向き先を合わせる #とは
マスターのバイナリーログとスレーブのバイナリーログ
バイナリーログのイベントはバイナリーログのファイル名とポジショ
ンで識別される
‐
が、これらはマスターとスレーブで完全に独立している‐
つまり、マスターのポジションを記録したフルバックアップにスレー
ブのバイナリーログを 過不足なく 適用するには、ポジションをマッ
ピングする必要がある
逆にスレーブのポジションを記録したフルバックアップにマスターのバイナリーロ
グを適用するのも鬼門
‐
これを解消してくれるのがMySQL 5.6で追加された GTID‐
42/88
向き先を合わせる #とは
マスターではこんな感じ
$ mysqlbinlog mysql-bin.008547 --start-position=70540318 | less
# at 70540318
#180301 16:27:14 server id 57913 end_log_pos 70540383 CRC32 0x24
734a71 GTID [commit=yes]
SET @@SESSION.GTID_NEXT= 'd47cc0b1-6c70-11e7-aeea-70106f4d304c:39
55500836'/*!*/;
..
INSERT INTO t1 VALUES (1, 2, 3, 4, 5)
/*!*/;
43/88
向き先を合わせる #とは
でもスレーブだと全然違うファイル名とポジションに書かれ
ている
$ mysqlbinlog mysql-bin.014800 | less
# at 216401979
#180301 16:27:14 server id 57913 end_log_pos 216402044 CRC32 0xf
8740c26 GTID [commit=yes]
SET @@SESSION.GTID_NEXT= 'd47cc0b1-6c70-11e7-aeea-70106f4d304c:39
55500836'/*!*/;
..
INSERT INTO t1 VALUES (1, 2, 3, 4, 5)
/*!*/;
44/88
\(^o^)/ な
んてこったい
45/88
mysqldumpのマスターとスレーブの打ち分け
gitd_mode= OFFの場合
体感ではマスターで mysqldump --master-data のパターンが多い気
がしますよ
‐
フルバックアップ元 コマンド例 バイナリーログコピー元
マスター mysqldump –master-
data
マスター
マスター x(この組み合わせは不可) スレーブ
スレーブ mysqldump –master-
data
マスター スレーブ(*2)
スレーブ mysqldump –dump-slave
(*1)
スレーブ マスター
(*1) –dump-slave はバックアップ中に STOP SLAVE してしまう
ので注意
(*2) ただし レプリケーションフィルター を使用していないこ
46/88
Thank you @mita2 !
47/88
mysqldumpのマスターとスレーブの打ち分け
gitd_mode= ONの場合
全ての組み合わせで特にオプションなく取得できる‐
リストアする時にdatadirを初期化しておく
厳密には gtid_executed が吹っ飛べばいいので RESET MASTER でもいい
‐
バックアップ元 コマンド例 バイナリーログコピー元
マスター mysqldump マスター
マスター mysqldump スレーブ
スレーブ mysqldump スレーブ
スレーブ mysqldump マスター
48/88
マスターとスレーブのバイナリーログ with GTID
gtid_mode=ON になっている場合、
マスターで更新がコミットされるたびにGlobal
Transaction IDがマスターのバイナリーログに埋め込まれ
スレーブも自分が実行したGTIDをおぼえておくようになる
(バイナリーログまたは mysql.gtid_executed テーブルに記
録)
既に実行済みのGTIDを振られたクエリーを複数回受信した
場合、最初の1回以外はクエリーがスキップされる
つまり、 ポジションを特に気にせずにあるだけ食わせても
二重適用にはならない
49/88
マスターとスレーブのバイナリーログ with GTID
ただし4つ目のクエリー時点のスナップショットに対して8番目
の更新履歴しかないバイナリーログを食わせるとかするのはダメ
少し余裕をもって過去のものから食わせる
50/88
GTIDは
いいぞ
51/88
余談
でもGTID有効にすると sql_slave_skip_counter が使えなくな
るでしょう?
それを使わなきゃいけない時は既にデータが壊れてるんだからおとなしく
メンテ入れて戻した方が後々幸せデース
52/88
おしながき
バックアップのリストアの基本的な考え方1.
バックアップ手法のおさらい2.
複数のバックアップ手法を組み合わせて運用する3.
日々のバックアップにまつわるタスク4.
どこがどう壊れた時にどうリストアするか5.
ぼくがかんがえたさいきょうのサーバー1台で自作サービス
を作ってる時のちょっとのコストで実現するバックアップの
しくみ
※感じ方には個人差があります
6.
53/88
日々のバックアップにまつわるタスク
バックアップはちゃんと取れているか
取ったつもりのバックアップがちゃんと取れていないとかなしい‐
tar: ibdata1: file changed as we read it とか注意‐
標準エラー出力、「 それをすてるなんて とんでもない! 」‐
最低限、開始, 終了と標準エラー出力だけでもバックアップスクリプ
トのログをちゃんと吐かせておく
‐
54/88
日々のバックアップにまつわるタスク
バックアップは「正しいか」
コールドバックアップのベリファイは難しい…
コールドバックアップとは「別に」 mysqldump も たまにとるとか
OPTIMIZE TABLE, CHECK TABLE を定期的にかけていれば、壊れたブロックが検出さ
れた時点ではまだ壊れる前のバックアップが残っているはず
‐
xtrabackupなら取得したあと --apply-log‐
mysqldumpなら最後の1行が -- Dump completed on ... になってる
ことを確認するとか
‐
バイナリーログなら mysqlbinlog でちゃんとデコードできるか
ただし # Warning: this binlog is either in use or was not closed
properly. は特に気にしなくて大丈夫
‐
55/88
日々のバックアップにまつわるタスク
スナップショット(フルバックアップ)は厳密にいつのも
の?
mysqldump は mysqldump プロセスが起動してちょっとの時点のスナ
ップショットを、 xtrabackup は xtrabackup プロセスが終了する直
前くらいのスナップショットを取る
‐
バックアップが2時に始まって4時に終わった。3時の時点に戻したい
時に使うのは今朝のバックアップ? 昨日のバックアップ?
そもそも開始時間と終了時間がわからなければ問答無用で昨日のバックアップとい
う安牌を切ることになる
バックアップのログ大事
‐
56/88
日々のバックアップにまつわるタスク
コンフィグファイルのバックアップ
データ的には変わってないけれどアプリケーションが受け取る結果セ
ットの見え方が変わったりして復旧出来てないように見えることがあ
る
特に skip_character_set_client_handshake という 邪悪な 設定がされている場
合、これがあるのとないのでは盛大に結果セットが変わる
‐
何かの対応でパラメーターを変えたヤーツとかが、リストアしてみた
ら反映されてなくて同じ轍を踏むとか
‐
57/88
おしながき
バックアップのリストアの基本的な考え方1.
バックアップ手法のおさらい2.
複数のバックアップ手法を組み合わせて運用する3.
日々のバックアップにまつわるタスク4.
どこがどう壊れた時にどうリストアするか5.
ぼくがかんがえたさいきょうのサーバー1台で自作サービス
を作ってる時のちょっとのコストで実現するバックアップの
しくみ
※感じ方には個人差があります
6.
58/88
どこがどう壊れた時にどうリストアするか
単純なクラッシュ(=最新のデータにリストアできればい
い)とデータの復元が必要(=過去のスナップショットをリ
ストアする)な場合とで大別できると思う
前者はレプリケーションスレーブがあると圧倒的に楽‐
59/88
どこがどう壊れた時にどうリストアするか
最新のデータにリストアできればいい場合
マスターが起動 スレーブ リストア
できる ない 不要(*1)
できない ない 最新のフルバックアップ +
バイナリーログ
できる ある 不要(*1)、またはスレーブ
からデータコピー(*2)
できない ある スレーブからデータコピー
(*2)、あるいはスレーブを
昇格
(*1) マスターのパラメーターが「ちゃんとクラッシュセーフに
設定してある」場合に限る
(*2) レプリケーションフィルターを使っていないこと
60/88
どこがどう壊れた時にどうリストアするか
マスターのパラメーターが「ちゃんとクラッシュセーフに設
定してある」とは
テーブルはInnoDBのみ(MyISAMはどう頑張ってもクラッシュアン
セーフ)
‐
skip_innodb_doublewrite とかダメ‐
innodb_flush_log_at_trx_commit=1, sync_binlog=1
MySQL 5.6とそれ以降ではバイナリーログもクラッシュリカバリーに使用するの
でバイナリーログのsyncも重要
‐
61/88
どこがどう壊れた時にどうリストアするか
データの復元(=最新以外の場所にリストア)の場合
スレーブの有無に関わらず、リストアする時点から見て最新のフルバ
ックアップとそれ以降のバイナリーログ
‐
マスターのOSが起動できない、ストレージ障害のケースを考える
と、如何に必要なバイナリーログを外部に保管しておくかがキモ
‐
マスターが起動 外部保管しておかないといけないもの
できる リストア時点から見た最新のフルバックア
ップ
できない(*) リストア時点から見た最新のフルバックア
ップ + それ以降のバイナリーログ
(*) あるいは、必要なバイナリーログが全て揃っていない場合
62/88
おしながき
バックアップのリストアの基本的な考え方1.
バックアップ手法のおさらい2.
複数のバックアップ手法を組み合わせて運用する3.
日々のバックアップにまつわるタスク4.
どこがどう壊れた時にどうリストアするか5.
ぼくがかんがえたさいきょうのサーバー1台で自作サービス
を作ってる時のちょっとのコストで実現するバックアップの
しくみ
※感じ方には個人差があります
6.
63/88
ぼくがかんがえた最強の略
ここまでの話を踏まえて
フルバックアップはほどほどで良い( リカバリーを速くす
る手段や方法 は話さないので)
バイナリーログのバックアップはリアルタイムで
それぞれマスターサーバーの外部に保管する
しかし色々マスター1台でコストかけずにパターンを考えて
思ったんだけど 1000円くらいで1インスタンス立ててバッ
クアップサーバー立てるのが最強だと思う
オンプレでもVM1台…作って…ほしい…‐
64/88
ほどほどのフルバックアップ
$ mysqldump --all-databases --single-transaction 
--routines --triggers --events 
--master-info=2 --hex-blob --quick
gtid_mode= ON の時は --master-data=2 は無くてもいい(あ
っても毒にはならない)
MySQL :: MySQL 5.6 リファレンスマニュアル :: 4.5.4
mysqldump — データベースバックアッププログラム
65/88
ほどほどのフルバックアップ
デイリーで mysqldump
--all-databases で全データベースのダンプを指定
--single-transaction (InnoDBのみの場合はこれでOK) ま
たは --lock-all-tables を指定しないとバックアップ中に更
新が走るとデータがズレる
--master-info=2 で mysqldump 実行時の SHOW MASTER
STATUS の値をダンプファイルに記録
スレーブから取る場合( SHOW SLAVE STATUS の Exec_Master_Log_Pos
とかを使いたい場合)は --dump-slave=2
‐
--triggers, --routines, --events を指定しないとトリガ
ー、ストアドルーチン、ストアドファンクション、イベント
スケジューラーのジョブの内容がバックアップされない
66/88
ほどほどのフルバックアップ
非UTF-8な環境の場合、 --hex-blob をつけておくとリスト
ア時に文字が壊れる心配がない
ただしダンプファイルのサイズは大きくなる‐
--quick は内部的なバッファの使い方のフラグ
ONにすると「クライアントのバッファを節約してサーバー側のリソ
ースがその分長く占有される」、OFFにすると「クライアントで全部
バッファしてサーバー側のリソースをさっさと解放する」
‐
バッチとかでも有効にするスイッチ‐
67/88
ほどほどのフルバックアップ
圧縮
gzipが嫌になるまではgzipでいいと思う‐
ダンプファイルがでかくなってきて(体感では > 100GB )圧縮率を稼
ぎたくなってきたらpzstd
‐
更に圧縮率を稼ぎたくなって、CPUが16スレッド以上とかあるなら
pbzip2
‐
帯域制限
pv が一番使いやすい‐
LAN内部で完結するなら特に使わなくてもいい‐
68/88
ほどほどのフルバックアップ
外部への書き込み
自作サーバーなら外付けHDDへのリダイレクトとかでもいい‐
一度ローカルのファイルシステムに吐き出してから転送した方が楽な
こともあるけど、マスターに容量の余裕がない場合はストリームしち
ゃうのがやっぱり楽
‐
個人的にはS3にのっけて、S3のライフサイクルで数日したら
Glacier、更に90日したら削除とかに任せるのが一番便利だった
mysqldump .. | aws s3 cp - s3://your_backet_name/prefix/filename.gz
‐
69/88
リアルタイムのバイナリーログバックアップ
これが考えどころ
たどり着いた結論だけ言うと mysqlbinlog --stop-never --
raw --read-from-remote-server を別サーバー(t2.microく
らいで十分だった)のdockerコンテナに入れて、それを10分
おきくらいにS3にアップロードしてる
自作サーバーとかでやるなら、 /var/lib/mysql とは 別の ディスク
に mysqlbinlog --stop-never --raw --read-from-remote-server で
書き込むかな
‐
70/88
リアルタイムのバイナリーログバックアップ
壊れたところ 最新のデータ 最新のバイナリーログ
マスター ないのでリストア binlog保管場所(*)
binlog保管場所 マスターにある マスター
マスターとbinlog保管場所
の二重障害
ないのでリストア 外部ストレージ
(*) mysqlbinlog はレプリケーションスレーブと同じコマンドで
マスターからバイナリーログを吸い上げる
71/88
リアルタイムのバイナリーログバックアップ
mysqlbinlog --read-from-remote-server --stop-never --raw 
--result-file=/path/to/binlog/directory 
--user=${REPLICATION SLAVE権限アカウント} 
--port=${MYSQL_TCP_PORT} 
--host=${MYSQL_HOST} 
${追尾を開始するバイナリーログファイル名}
72/88
リアルタイムのバイナリーログバックアップ
実運用にはこれをもうちょっとラップして、自動で ${追尾
を開始するバイナリーログファイル名} を計算したり、多少
通信が途切れてもリトライするようにはしてある
${追尾を開始するバイナリーログファイル名} はフルパス で
はなく ファイル名のみ。 CHANGE MASTER TO
master_log_file= '..' といっしょ。
--result-file でbinlogサーバー内の出力先ディレクトリー
(とか)を指定できる
MySQL :: MySQL 5.6 リファレンスマニュアル :: 4.6.8.3
バイナリログファイルのバックアップのための
mysqlbinlog の使用
73/88
リアルタイムのバイナリーログバックアップ
binlog保管場所から外部への転送はcronで非同期に。二重障
害時の最大ロスト幅がcronの間隔になる
Prodではそれ以外に log_slave_updates なバックアップサーバーが
いるし
‐
更にサービス用のスレーブも log_slave_updsates を入れているの
で、ほぼ間違いなくどこかに最新のバイナリーログは残る
‐
74/88
リアルタイムのバイナリーログバックアップ
完全に1台運用なら /var/lib/mysql のバイナリーログを直
接外部転送してもいいんじゃ? (別のディスクに一度書く
必要はないんじゃ?)
cronで回すと落ちた時にその間隔分(起動できなくなった時に)ロ
ストが確定するのでそれが許容できるなら全然アリだと思う
‐
この用途(1か所の故障でも最新のバイナリーログを失わない)に関
しては、RAID1とかでも期待した効果が得られると思う
ただし自力で上がってこなかったサーバーからディスクを引っこ抜いてデータが取
り出せる人に限る
‐
75/88
リストア
gtid_mode= OFF でポジションベースで指定する時
最新のデータまで進める場合には最後の --stop-position はつけな
い
‐
$ mysqld --initialize-insecure
$ mysql -uroot < /path/to/mysqldump.sql
$ head -30 /path/to/mysqldump.sql | grep CHANGE
-- CHANGE MASTER TO MASTER_LOG_FILE='mysql-bin.008547', MASTER_LO
G_POS=70540318;
$ mysqlbinlog --start-position=70540318 mysql-bin.008547 | mysql
-uroot
$ mysqlbinlog mysql-bin.008548 | mysql -uroot
$ mysqlbinlog mysql-bin.008549 | mysql -uroot
..
$ mysqlbinlog mysql-bin.008999 --stop-position=32764 | mysql -uro
ot
76/88
リストア
gtid_mode= ON の時
少し余裕を持った過去のぶんのバイナリーログから用意しておくこと‐
最新のデータまで進める場合には最後の --exclude-gtids はつけな
い
‐
$ mysqld --initialize-insecure
$ mysql -uroot < /path/to/mysqldump.sql
$ mysqlbinlog --exclude-gtids="419f4444-0571-11e8-b425-0201a32caf
75:5-9999999999999999" mysql-bin.* | mysql -uroot
77/88
mysqldumpからのリストアトラブルあるある
リストア後、ユーザーアカウントが作られていないような気
がする / ログインできない / SHOW GRANTS がおかしい
mysqldump からのリストアは mysql スキーマのテーブルを直接書き
換えることになるので、管理情報に一次的な不整合が発生します
‐
よくわからなければmysqldを再起動しましょう。 FLUSH
PRIVILEGES でもOK。
‐
78/88
mysqldumpからのリストアトラブルあるある
トリガーとかストアドがどっかいった
バックアップ取る時に --triggers とか --routines とか --events
忘れたでしょ!
‐
ついでに言うなら その時mysqldumpが標準エラー出力にワーニン
グ出してたでしょ!
‐
79/88
mysqldumpからのリストアトラブルあるある
5.7.18までの mysqldump は mysql.gtid_executed テーブル
をバックアップしてしまってリストア後に不正なGTIDを掴
まされる
5.7.19とそれ以降にアップデートしましょう。 mysqldump のバグな
のでクライアントだけアップデートしてもいいです。
‐
MySQL Bugs: #82848: Restarting a slave after seeding it with
a mysqldump loses it’s position
‐
爆発しろ‐
80/88
オススメのmysqldのオプション(1)
[mysqld]
# マスターはスレーブがいなくてもバイナリーログ出力オプション必
須。
# スレーブにも出力させておくとバイナリーログ回収先の選択肢が増え
る
log_bin
log_slave_updates
expire_logs_days = 7
# ファイルサイズは大きすぎない方が取り回しが楽
max_binlog_size = 256M
# 初心者向けにはバイナリーログはROWベースの方がオススメ
binlog_format = ROW
binlog_rows_query_log_events
# binlog_row_image = minimal ### ファイルサイズを抑えたい場合、お
好みで
81/88
オススメのmysqldのオプション(2)
[mysqld]
# GTIDは有効に
enforce_gtid_consistency
gtid_mode = ON
# 信頼性を犠牲にして性能を稼ぐオプションは使わない
innodb_flush_log_at_trx_commit = 1
sync_binlog = 1
# レプリケーションセーフな設定(`*_unsafe_*` とか入ってるオプショ
ンに手を出してはいけない)
sysdate_is_now
MySQL :: MySQL 5.6 リファレンスマニュアル :: 5.1.4 サ
ーバーシステム変数
82/88
バックアップFAQ
バイナリーログはローテーションさせてからバックアップ(特に
OSコマンドで複製する時)するべきなのでは
最後のイベントが書きかけだった場合、単に mysqlbinlog でデコードする
時にそのイベントが無視されるだけなので特に気にする必要はないです
83/88
バックアップFAQ
コールドバックアップの時、バイナリーログのポジションってど
うやって記録するの
マスターから取る(ことがあるとは思えないけど)なら「次回起動時にバ
イナリーログが必ずスイッチする」ので取ったバックアップが抱えている
バイナリーログの次のファイルのposition=1から、スレーブから取るなら
relay-log.info または mysql.slave_relay_log_info テーブルの
Master_log_pos から拾い上げます
84/88
Questions
コンテナーでバイナリーログのストリームバックアップしてるっ
て話だったけど、ストリームがちゃんと動いてるかどうかの監視
ってどうしてるの?
プロセスが止まってないかと、コンテナを再起動すると最後に掴んでいた
バイナリーログの先頭から取り直すようにしてあるのでそれでリトライさ
せて代わりにしてます
85/88
Questions
ストリームでコピーしたバイナリーログが正しくコピーできてる
かどうかの検証ってどうするの?
バイナリーログはイベント単位でチェックサムを持っているので、壊れて
いるところは mysqlbinlog コマンドに食わせた時にエラーになります。
バイナリーログがスイッチしたら mysqldump かけてベリファイしてから圧
縮してます。
86/88
Questions
物理バックアップが正しく取れているかどうかってどうやって判
断するの?
InnoDBのブロック破損はそのブロックに触って初めてわかるので、コマン
ドレベルからの判断は難しいですが、代わりにバックアップサーバーで定
期的に OPTIMIZE TABLE をかける(バックアップの断片化の解消と、
OPTIMIZE TABLE は全レコードを舐めるので壊れたブロックがあればそこで
検出できる)とか、他のところでは物理バックアップの他にたまに
mysqldump (論理バックアップなので全レコードを舐める)を取るとかで担
保しています。
87/88
Thank
you!!
88/88

サーバーが完膚なきまでに死んでもMySQLのデータを失わないための表技