MySQLでスレーブを複数台並べている。
負荷増加やサーバ障害で新規スレーブを追加したい。
で、構築したばかりのMySQLスレーブをいきなりサービスに突っ込むとどうなるか。
それなりの規模のサービスであれば、buffer_poolが空っぽのDBだとIOがつまって応答遅延が発生します。
というわけでサービス投入前にbuffer_poolのウォームアップが必要で、方法としてはいくつか考えられます。
- クエリを実行して主要テーブルのIndexをロードする
- 低い振り分け比率でサービスに投入してしばらく待つ
- tcpdump + (mk|pt)-query-digest等で既存スレーブのクエリを流し込む
基本的にサービス投入前の完璧なウォームアップは無理ゲーなので、
普段は主要テーブルのIndexを読み込んでから、低い振り分け比率でサービス投入、ディスクIO見ながら徐々に振り分け比率を上げていく、ということをしていました。
しかししばらく前にとあるエンジニアから『稼働中DBでdumpしたib_buffer_poolを新規DBでloadする』というかなりびっくりなウォームアップ手順を聞きました。
どういうことかというと、MySQL5.6から使える以下の設定はよく知られていて、
innodb_buffer_pool_load_at_startup = 1 innodb_buffer_pool_dump_at_shutdown = 1 #innodb_buffer_pool_dump_pct = 100
この設定があるとMySQL停止時にバッファプールの状態をダンプし、起動時にそれを読み込むことでバッファプールの状態を復旧出来ます。
このダンプファイル(ib_buffer_pool)は稼働中のDBでも取得可能なので、それを新規構築したDBで読み込めばバッファプールを本番稼働中の既存DBと同じ状態に出来ると。
これはなかなか怪しい手順です。
ダンプファイルに入っているのは、主キー値などではなくtablespace ID and page ID
で、細かいことよくわかってないですが取得したDBとは別のDBでそれを使うのはかなり乱暴な気はします。
ただ今自分が主に使っている環境では、バックアップDBがあって、新規スレーブのデータは全てこのDBのEBSのスナップショットから作っています。
つまり元データは完全に同じもので、その後の更新クエリも同じもの。
なんかいけるような気もする・・・
まぁとりあえず試してみよう、ということでやってみました。
ウォームアップ手順
稼働中のスレーブでdumpを取る。
> SET GLOBAL innodb_buffer_pool_dump_now = 1;
以下のコマンドを実行してcompletedになってれば完了。
これはあっという間に完了して、サービス影響は全く無さそう。
ダンプファイルもすごく小さいです。
> SHOW STATUS LIKE 'innodb_buffer_pool_dump_status'; +--------------------------------+--------------------------------------------------+ | Variable_name | Value | +--------------------------------+--------------------------------------------------+ | Innodb_buffer_pool_dump_status | Buffer pool(s) dump completed at 150723 15:22:44 | +--------------------------------+--------------------------------------------------+
出力されたダンプファイルを新規DBにコピーする。
$ ls -lh /var/lib/mysql/ib_buffer_pool -rw-rw---- 1 mysql mysql 28M Jul 23 15:22 /var/lib/mysql/ib_buffer_pool
innodb_buffer_pool_load_at_startup =1が設定されていれば、mysqlを起動するだけでダンプファイルの読み込みが始まります。
# service mysql start
既にmysqlが起動済みであれば以下のコマンドでloadを開始してもOK。
※別のloadが走ってたらinnodb_buffer_pool_load_abort=1で止めてからのほうがいいかも。
> SET GLOBAL innodb_buffer_pool_load_now = 1;
以下のコマンドでcompletedになれば完了。
> SHOW STATUS LIKE 'innodb_buffer_pool_load_status'; +--------------------------------+---------------------------+ | Variable_name | Value | +--------------------------------+---------------------------+ | Innodb_buffer_pool_load_status | Loaded 1537/2679699 pages | +--------------------------------+---------------------------+ > SHOW STATUS LIKE 'innodb_buffer_pool_load_status'; +--------------------------------+--------------------------------------------------+ | Variable_name | Value | +--------------------------------+--------------------------------------------------+ | Innodb_buffer_pool_load_status | Buffer pool(s) load completed at 150723 18:51:30 | +--------------------------------+--------------------------------------------------+
検証
とある本番環境でざっくり効果を検証してみました。
どうせ環境依存がデカイので細かいことは置いといて、1回ずつだけウォームアップ時間、サンプルクエリの実行時間を測定しました。
- r3.2xlarge (EBS最適化オプションつけ忘れてた)
- MySQL 5.6
- buffer_pool_size = 45G
- mysql用EBS : gp2 600G。全部同じスナップショットから作成
サンプルクエリは本番サーバでtcpdumpしてとった50万行のSELECTです。
$ egrep '^SELECT' /tmp/select.sql | wc -l 504316
実行時間は単純に1並列で流し込んで測定しました。
$ time mysql -uxxx -p'xxxx' db_name < /tmp/select.sql > /dev/null
比較として、いつもやってる主要テーブルのIndexの読み込みウォームアップも合わせて実施します。
$ innodb-warmer -u xxx -p 'xxxxx' -d db_name -t comments,articles,...
innodb-warmerコマンドは@songmuさんのMySQL::Warmerを@sgwr_dtsさんがrubyで書きなおしたものです。公開されてたっけな・・・
結果
warmup済みのEBSで検証してみるとこんな感じ。
ウォームアップ | サンプルクエリ実行 | |
---|---|---|
既存稼働中サーバを振り分け外して | - | 2m36s |
ウォームアップ無し | 0 | 8m42s |
innodb-warmerで主要テーブルを温める | 66m14s | 4m15s |
別サーバからbuffer_poolをdumpしてload | 33m と少々(dump&scp) | 2m38s |
別サーバからbuffer_poolをdumpしてloadした場合のウォームアップ時間は30分強で、検証クエリの実行時間は本番稼働中のサーバと同程度に速かったです。
どちらもIndex読み込み方式に比べるとかなり優秀。
wamup無しのEBSだとどうなるか
ウォームアップ | サンプルクエリ実行 | |
---|---|---|
innodb-warmerで主要テーブルを温める | 4h9m | 24m41s |
別サーバからbuffer_poolをdumpしてload | 7h37m | 2m49s |
ウォームアップの時間がものすごくかかる・・・
この状態のDBを実際にサービスに投入してみます。
レプリケート取りつつほんの少しクエリ流したところから、50%->100%と振り分けに入れていくと、
読み込みのIOPSはこうなる。
青がサービス稼働中の既存DB、赤が『innodb-warmerで主要テーブルを温める』のやつ、緑が『別サーバからbuffer_poolをdumpしてload』。
緑のディスク読み込みが短時間で本番DBと同程度になるのに対し、赤はだいぶ時間がかかっています。
んー、こっちの結果はEBSのウォームアップはたいへんだなという意味しかないかな。
EBSのウォームアップ、もうちょっとなんとかなんないかなぁ・・・
まとめ
検証した条件では、ib_buffer_poolを別サーバから持ってきてloadするのはInnnoDBのウォームアップ手順としてなかなか優秀なように見えるので、状況に応じて使っていこうと思います。
ただこの方法が汎用的に使えるのかというと疑問があるので、ツッコミや別の検証結果があればぜひ見たいです。