アクトインディ開発者ブログ

子供とお出かけ情報「いこーよ」を運営する、アクトインディ株式会社の開発者ブログです

Rails での MySQL マスタースレーブ構成

いこーよ の GW の負荷対策として MySQLレプリケーションを使いマスタースレーブ構成にしてみました。

一番悩んだのがマスタースレーブ構成のためにどのライブリを使うか。 次のような理由から seamless_database_poolフォークして使うことにしました。

  • アプリ起動時にスレーブが落ちていても動く。 ただし、この場合は途中からスレーブが動きだしてもスレーブにはつながらない。
  • アプリ起動中にスレーブが落ちても動く。
  • 途中でスレーブが復帰すればまたスレーブにつながるようになる。
  • マスターをスレーブに含めることも、含めないこともできる。
  • マスター、および各スレーブの接続ウエイト指定ができる。

フォークする理由は次のとおりです。

  • geokit-rails が UnsupportedAdapter 例外を投げることの対策。
  • 毎回セッション使わないようにする。 Rails 3 Slave Databases: Compare Octopus to SDP を参照。
  • デバッグ時にマスターにつないでいるのか、スレーブにつないでいるのかわからないので、接続先をログ出力する。

以下、セットアップ手順を書きていきます。

マスターとスレーブ両方にレプリケーション用のアカウントを作成します。

mysql -uroot

GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO repl@'%' IDENTIFIED BY 'password';

AWS のセキュリティグループがあるので repl@'%' でよし。

マスターの my.cnf を編集します。

sudo vi /etc/mysql/my.cnf

[mysqld]
server-id              = 10
log_bin                = /var/log/mysql/mysql-bin.log

MySQL を再起動。 sudo service mysql restart

スレーブの my.cnf を編集します。

sudo vi /etc/mysql/my.cnf

server-id               = 11
log_bin                 = /var/log/mysql/mysql-bin.log
relay_log               = /var/log/mysql/mysql-relay-bin.log
log_slave_updates       = 1
read_only               = 1
slave_load_tmpdir       = /var/tmp

slave_load_tmpdir はマシンを再起動してもファイルが消えないディレクトリにしておかないと問題があるようです。

MySQL を再起動。 sudo service mysql restart

スレーブで次のようにしてマスターを指定します。

mysql -uroot

CHANGE MASTER TO MASTER_HOST='ec2-123-123-123-123.ap-northeast-1.compute.amazonaws.com', MASTER_USER='repl', MASTER_PASSWORD='password';
show slave status\G

マスターからデータをスレーブに投入します。

mysqldump -uroot --single-transaction --all-databases --master-data=1 | ssh -C deployer@ec2-54-248-109-31.ap-northeast-1.compute.amazonaws.com mysql -uroot

スレーブでレプリケーションを開始します。

mysql -uroot

start slave;
show slave status\G

Rails で seamless_database_pool を使うようにします。

application_controller.rb でデフォルトでマスターを使うようにします。

class ApplicationController < ActionController::Base
  include SeamlessDatabasePool::ControllerFilter
  use_database_pool :all => :master # 個別にスレーブを使うように指定する。

スレーブを参照したいコントローラで次のように書きます。 アクション毎に指定できます。

class FacilitiesController < ApplicationController
  use_database_pool [:index, :show, :map_xhr] => :persistent

application_controller.rb で全てスレーブ参照にしても更新系の SQL はちゃんとマスターにいくのですが、 レプリケーションの遅延があった場合よくあるアップデートして show にリダイレクで古い情報を表示してしまう問題と、 ごく一部のアクションが全クエリーのほとんどをしめるという理由からデフォルトマスターで、個々にスレーブを使うように指定する方針にしました。

おかげさまで、今回の GW の瞬間的なピークは New Relic で 3,092rpm を記録し、 Google Analytics リアルタイムのアクディブユーザで 1,900 を越えました。 AWS のオートスケールでアプリサーバも1台から5台まで自動的にスケールしました。

いこーよ のご利用ありがとうございました。

最後に、弊社ではシステムエンジニア、プログラマ、インフラエンジニアなどを募集しています。 おきがるにお問い合わせください。