PIYO - Tech & Life -

Railsでデータベースのレプリケーションをしたいとき

データベースの負荷分散の方法の1つレプリケーションという方法があります。マスターDBへの変更をスレーブDBに同期するようにしておいて、書き込みのクエリはマスターDBへ、読み取りのクエリはスレーブDBへと振り分けることで負荷を分散しようというやり方です。

最初にこの方法を知った時は最強じゃんとか思ったんですが、そんな魔法のようなものではなくよく考えてうまく使う必要があります。例えばマスターの更新がスレーブに反映されるのにはタイムラグがあるので、マスターを更新した直後にスレーブを参照してデータがない、とか。まあとにかく色々工夫が必要です。

と、ここまで書いておいてなんですが、今日の記事ではレプリケーションの実践について書くわけではありません。もっとライトな「Railsでデータベースの接続先を変えるためのgemを紹介する」という話です。

いくつかのgemがありましたが、僕が選んだのはシンプルに使えそうだったpostamtというgemです。

sauspiel/postamt

有名なのはこっちかも?使った当時は導入に手間どってしまったのでパスしました。

tchandy/octopus

postamtの導入

インストール

# Gemfile
gem 'postamt'
$ bundle install

database.ymlの設定

database.ymlファイルにスレーブの情報を追加します。スレーブは複数でもよいです。。データベースの名前やユーザー名などを一緒にしておけばhostを書き換えるだけでOKです。

development:
  adapter: mysql2
  host: db-master
  database: hoge
  user: hoge
  password: hoge
  pool: 5
  timeout: 5000
  slave1:
    host: db-slave1
  slave2:
    host: db-slave2

ローカルでの動作確認でレプリケーション構成を作るのが面倒なときは、参照するDBは同じにしたまま読み取り専用のユーザーを作っておくことでシミュレート可能です。

postamtの使い方

アクション単位で分ける

READMEから転載w

class UserController < ApplicationController
  use_db_connection :slave, for: ['User'], only: [:search]

  def search
    # SELECTs here are sent to slave
    # User#save and User.create would be sent to master anyways.
    # Everything in a transaction block too.
    @users = User.where(...) # sent to slave
    @something_else = SomethingElse.first # sent to master
  end

  def create
    @user = User.new(params[:user])
    @user.save! # sent to master
  end

  def invoice
    transaction do
      @user = User.where(...) # sent to master
      @invoices = Invoice.create(...) # sent to master
    end
  end
end

モデルのデフォルトを指定する

class User < ActiveRecord::Base
  # ...
  self.default_connection = :slave1
end

User.where(...) # slave1が使われる

DBを指定して実行する

Postamt.on(:master) do
  User.where(...) # masterが使われる
end