2010/11/15

replicationしてるMySQLのslave増設手順



こんにちは、hiroshiです。おひさしぶりですね。
stoneが書いたhadoopの記事が打ち合わせとかで「見ましたよ。評判ですよ。」とか言われてジェラシーいっぱいです。
僕もがんばります。目指せホッテントり!

といっても、僕だと書けることに限界があるので、今日は半定常作業のMySQLの増設作業について書こうと思います。

下図のように、master1台←slave2台がLVS+keepalivedで負荷分散構成されているDBがあるとします。

図1.master-slave構成


この構成の組み方にしようかと思ったのですが、これはググったらいっぱいあったのでホッテントリは狙えないと思ってやめました。

なので、今回のテーマは「このテーブルはwriteは余裕だけどreadがきつくなってきたからslaveを増設しなければ!」となった場合のslaveを増設する手順について書いてみます。
下図のslaveCを追加するぞ!の場合です。

図2. slave Cを追加するぞ


※今回はengine=innoDBの前提で書きます。
※各種userとかIPとかその辺はそこそこ適当にかいてます。

大まかな手順は以下です。

  1. コピー元となるslaveを1台LVSから切り離す。
  2. slave→masterのreplicationを止める。
  3. dumpを取る。
  4. dumpファイルを移動させる。
  5. dumpファイルをインポートする
  6. LVSに参加させる

こんな感じです。

1. コピー元となるslaveを1台LVSから切り離す。


忙しいDBでいきなり切り離すと、sleepのプロセスが大量発生するので、weightを1にしてから外すことにしています。

keepalived.conf
# slave A
real_server     192.168.0.101 3306 {
 weight  1 ←1にする。(通常10にしてます)
 inhibit_on_failure
  TCP_CHECK {
   connect_port    3306
   connect_timeout 3
  }
}

# slave B
real_server     192.168.0.100 3306 {
 weight  10
 inhibit_on_failure
 TCP_CHECK {
  connect_port    3306
  connect_timeout 3
 }
}


weight1にして
svc -h /servie/keepalive


# slave A
#real_server     192.168.0.101 3306 {
#    weight  1 ←1にする。(通常10にしてます)
#    inhibit_on_failure
#    TCP_CHECK {
#        connect_port    3306
#        connect_timeout 3
#    }
#}

# slave B
real_server     192.168.0.100 3306 {
 weight  10
 inhibit_on_failure
 TCP_CHECK {
  connect_port    3306
  connect_timeout 3
 }
}


コメントアウトして
svc -h /servie/keepalive


で、LVSからの切り離しは完了です。

2. slave→masterのreplicationを止める。


slave A上のmysqlでshow processlistするなどしてアクセスされなくなったのをやわらかく確認します。
小心者のぼくは最終的にはtcpdumpとかでmysqlのパケットが流れてこないかを確認するのですが、この時点ではまだreplicationしたままなのでバリバリmysqlのパケットが流れています。

mysql> show slave status\G
~中略~
Master_SSL_Cert:
Master_SSL_Cipher:
Master_SSL_Key:
Seconds_Behind_Master: 0
1 row in set (0.00 sec)


mysql> stop slave;
~中略~
Master_Host: master
Master_User: hoge
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mysql-bin.000049
Read_Master_Log_Pos: 760141636
~中略~
Master_SSL_Cert:
Master_SSL_Cipher:
Master_SSL_Key:
Seconds_Behind_Master: NULL
1 row in set (0.00 sec)

ということで、この時点でreplicationが止まりました。
Seconds_Behind_Master: NULL
がreplicationが止まった合図です。

で、
Master_Log_File: mysql-bin.000049
Read_Master_Log_Pos: 760141636
の部分をメモっておきます。

これからdumpをとるのですが、そのデータはmasterのbinログファイルが「mysql-bin.000049」でそのファイルの位置が「760141636」の状態のものだという意味です。]
部分的なコピペでもいいんですが、だいたいこんな感じshow slave status自体をファイルに落としておいておくといいかもです。

mysql> \T slave_status.txt
mysql> show slave status \G
mysql> \t

で、ここでtcpdump -i eth0 port 3306とかやってパケットが流れないのを確認すると精神衛生上いいので僕はそうしてます。

3. dumpを取る。


容量のでかいDBの場合、dumpに半日以上かかることもざらなので、screenを使います。screenじゃなくてもnohup使うでもなんでもいいんですが、不慮のネットワーク切断に備えた何かをやっておいたほうが賢明です。

$ screen

$ mysqldump -u root -p database_stage > dump_database_stage.sql
$ mysqldump -u root -p database_release > dump_database_release.sql
※テスト環境用のDBもあるので、一緒にダンプします。

これでひたすら待ちます。Zabbixのロードアベレージとかがグイーンとあがるので、それが下がったら「終わったかな」と思って見に行きます。dumpが終わったら通知メールを送るようなスクリプトを書いてもいいんですが、必要に思ったことがないので今はそういうのはないです。

ちなみに、だいたいの場合において、このdumpをとっているスキに増設先のmysqlの構築を済ませておきます。

dumpが終わったらreplicationを再開させます。

mysql> start slave;

replicationをとめてた時間などにもよりますが、

mysql> show slave status\G
~中略~
Seconds_Behind_Master: 1348615

と、こんな感じになっているので、0になるのを待ちます。これも見つめていてもなかなか終わらないので、Zabbixを利用します。Seconds_Behind_Masterの値は、常時監視項目としてZabbixで監視+閾値でアラートを設定しているので、これを利用してreplicationが追いつくタイミングを知ることもできます。

ここでちょっとワンポイントなのですが、replicationが追いついた後、

mysql> select count(*) from table;
をします。
innodb限定の手順なのですが、これでinnodb_buffer_poolにデータを乗せようとしています。これをやらないでいきなりユーザーからのアクセスにさらすと、処理がおっつかずにreplicationのディレイが発生しちゃったりします。

ところで、上述のとおり、slave増設はslave1台をサービスから切り離してdumpを取る、という作業手順になります。
ですが、増設のきっかけは「負荷が厳しいから」だけではなくて「slaveが1台故障したから」というケースもあります。
故障のケースでmaster1台←slave2台の構成だと、故障後はmaster1台←slave1台という構成と同義になり、そこからslaveを切り離してしまうとサービスに利用できるslaveがなくなってしまいます。最悪負荷の低い深夜帯にmasterにreadも担当させてやるか、サービスを止めるかなどするしかなくなってしまうので、そこそこ途方にくれます。

ですので、slaveは3台が基本です。
masterがblackholeの場合を考慮すると「実体のあるDB3台が基本」という言い方もあります。

このことはid:naoyaさんの著書「大規模サービス技術入門」にも書いてあったのですが、僕たちは、実際に途方にくれた後にこの著書に出会い、「もっと早くこの本に出会えていれば」と悔やみました。

4. dumpファイルを移動させる。


sftpでもscpでもいいです。増設先のサーバに移動させるだけです。
ただ、うちではサーバ間のinとoutのトラフィックをZabbixで監視してます。で、特に何もしないとscpでも全力でファイル転送されるため、監視の閾値を超えてしまい、アラートメールが飛んできてしまいます。
そうすると、周りに冷たい目で見られるのでZabbixのトラフィック監視をOffにしておくことを忘れてはいけません。
この移動もファイルの容量次第ではscreenするなりしておきます。

5. dumpファイルをインポートする


$ mysql -u root -p database_stage < dump_database_stage.sql
$ mysql -u root -p database_release < dump_database_release.sql
これも相当時間がかかるケースがあるのでscreenなりをしておきましょう。 インポートが終わったら、replication設定をします。
mysql> change master to master_host='マスターホスト名', master_user='repl', master_password='repl', master_log_file='ログファイル名', master_log_pos=ポジション;
この「ログファイル名」と「ポジション」に2でメモったMaster_Log_FileとRead_Master_Log_Posを入れます。 その後は
mysql> start slave;
して、Seconds_Behind_Masterが0になるのを待ってselect count(*)やっての手順は先ほどまでと同じです。

6. LVSに参加させる


これで最後です。 keepalived.conf
# slave A
real_server     192.168.0.101 3306 {
 weight  10
 inhibit_on_failure
 TCP_CHECK {
  connect_port    3306
  connect_timeout 3
 }
}

# slave B
real_server     192.168.0.100 3306 {
 weight  10
 inhibit_on_failure
 TCP_CHECK {
  connect_port    3306
  connect_timeout 3
 }
}

# slave C
real_server     192.168.0.99 3306 {
 weight  1 ←1からはじめる
 inhibit_on_failure
 TCP_CHECK {
  connect_port    3306
  connect_timeout 3
 }
}
新しく追加したDBのweightは1からはじめます。何かしくじってたときのダメージを低くするためです。アプリのエラーログを監視しながらこの作業は行い、エラーがでれば即戻します。各種グラフを見ながらweightを10にまで持っていけたら作業完了です。 以上が単純なslaveの増設方法です。

これ、役に立つと思うのでブックマークよろしくおねがいします!

では、また会いましょう。

次回予告:「1つのDBに複数テーブルが入っている場合において、一部のテーブルだけ別DBに移動させるの術の巻」