--- layout: old_post title: Twitterのトラブルから見る、DB分割でスケーラブルなRailsサイト構築 permalink: /tatsuya/show/311-twitter-db-rails ---

スケーラブルWebサイト

最近、2.0な方々の間でTwitterが話題になってる。で、そのTwitter自体も面白いんだけど、TwitterについてDHHがブログを書いてRailsでの大規模サイト構築が話題になってるのが面白い。

Twitter trouble (Loud Thinking - DHH)
まずTwitterの高負荷について言及、Twitterは11,000リクエスト/秒 の高負荷で問題となっているらしい。 そしてスケーラビリティの鍵はDB分割だ、と言っている。Railsは基本一つのDBを見るのでスケーラビリティの問題になる (確かにWebサーバはロードバランサがあればいくらでもスケールするしね、Sessionの共有だけ気を付ければ)

 ↓

Dr Nic » Magic Multi-Connections: A “facility in Rails to talk to more than one database at a time”
タイムリーにRailsで複数DBを使い分けるためのライブラリがリリースされる、magic_multi_connections というらしい。

 ↓

Scaling to multiple databases with Rails (Loud Thinking) magic_multi_connectionsへのDHHの言及。

ということでmagic_multi_connectionsを触ってみる、以下のように使用

#gem からインストール
gem install magic_multi_connections

#config/database.ymlを編集
development:
  database: hoge_master
  timeout: 5000
  host: localhost

development_slave1:
  database: hoge_slave1
  timeout: 5000
  host: localhost

development_slave2:
  database: hoge_slave2
  timeout: 5000
  host: localhost

DB設定準備完了、本来の意図からするとhostが全部同じなのは意味がないので複数サーバになると思う、次にconfig/environment.rbを編集

require 'magic_multi_connections'
connection_names = ActiveRecord::Base.configurations.keys.select do |name|
  name =~ /^#{ENV['RAILS_ENV']}_slave/
end
@@connection_pool = connection_names.map do |connection_name|
  Object.class_eval <<-EOS
    module #{connection_name.camelize}
      establish_connection :#{connection_name}
    end
  EOS
  connection_name.camelize.constantize
end

development*slaveのDBを探してきて、module作成。で作成したモジュール名を付加してアクセス

ruby script/console
>>Hoge.find(1)                      #hoge_master DB を検索
>>DevelopmentSlave1::Hoge.find(1)   #hoge_slave1 DB を検索
>>DevelopmentSlave2::Hoge.find(1)   #hoge_slave2 DB を検索

とアクセスするDBを使い分けられる。ついでにこれでランダムにアクセスも可能

ruby script/console
>> def conn
>>   @@connection_pool[rand(@@connection_pool.size)]
>> end
>> conn::Hoge.name
=> "DevelopmentSlave2::Person"
>> conn::Hoge.name
=> "DevelopmentSlave1::Person"

配列に入ってるのか。。一通り触ってみると確かにDB振り替えられてるみたい。
ということで明示的にDBを切り替えるには良いじゃないか、これを利用してSelectだけSlaveを選択するModelを組めば良いかも。。と思ってた矢先に今度は別のPluginがリリースされた!
これは acts_as_readonlyable という名前の通りの動作を行なう。DBのアクセスでSELECTのみReadOnlyDBへアクセスするらしい

Revolution On Rails: [PLUGIN RELEASE] ActsAsReadonlyable

ということでこっちも触ってみる、もう疲れたのでざっくりと

script/plugin install svn://rubyforge.org/var/svn/acts-as-with-ro/trunk/vendor/plugins/acts_as_readonlyable

#config/database.yml############
development:
  adapter: mysql
  database: hoge_master
  host: localhost

  read_only:
    adapter: mysql
    database: hoge_slave
    host: localhost

#app/model/book.rb##############
class Book < ActiveRecord::Base
  acts_as_readonlyable :read_only
end

#準備完了#######################
ruby script/console
>>book = Book.find(1)  #hoge_slaveへアクセス
=> #<Book:0x457683c .........
>> book.title = "hoge2"
=> "hoge2"
>> book.save #hoge_masterへアクセス

SELECT (COUNT) はread_onlyで指定したDBへアクセス、その他更新系は全てmasterへアクセスする。Master>SlaveでレプリケーションしているMySQLを想定してるんだろうか。ふーむ、使い分けの問題だろうけど自分はacts_as_readonlyableのが好きかな、スマートだしソースも判りやすい。magic_multi_connectionの方はソースいまいち良く判んないや。 しいて言うなら

という雰囲気か。でもmagic_multi_connectionの作者もreadonlyableを褒めてるみたいなので
Dr Nic » “Reads -> slaves, writes -> master” plugin
良い影響を与え合っていくと良いなと思う(・し・)

さあ、結局この先に続くのはmixiやMySpaceなんかの大規模サイトがやっているFederateDBの道だと思う (DB一つ10万人としてUserIDを10万で割ってDB名を出す、みたいな)、冒頭で画像を張ったオライリーのスケーラブルWebサイトでも「横のDB分割」って紹介されてるけど、結局DBをスケーラブルにするのって気が狂うくらい大変だと思う。そこでFadarate、これならMySQLでも適用可能、でもアプリケーション側が汚くなる諸刃の刃・・・、そこを上手くRubyの柔軟性で品良くクラスライブラリに閉じ込められれば結構良いと思う。名前は勿論 acts_as_federate で4649。

(ちなみにOracleのRACを使えば楽できそうだけど、前仕事で使ってたOracleDBは、言うこと聞かなくてインスタンスが突然落ちる、とかあったしライセンス料バカ高いし・・・・むむむむむ)

追記 : Matz日記にも Twitter>DHH>magic multi connectionの話が言及されてた http://www.rubyist.net/~matz/20070416.html#p06

さらに追記 : はてブコメントによると「11,000じゃなくて600リクエスト/秒とtwitterの中の人が言っている。」?
確かにscaling twitterていうTwitterのプレゼンテーションでは"600 request per second"って書いてあるね。失礼、そうなのか;)
ちなみに "180 Rails Instances (Mongrel) / 1 Database Server (MySQL) + 1 Slave" と書いてあるね、DBの中の人大変そう