l'essentiel est invisible pour les yeux

Wednesday, March 15, 2006

21歳になりました

3月12日で21歳になりました。

21 * 365 = 7635

20歳の時期なんてあっという間にすぎ、ついこの間「今日から20歳になった」といってた気がする。
うるう年除くとわずか7635日ぐらいしか過ごしてないことに素で驚きました。

Le Sud Hayakawa

夜は、ここで彼女にご馳走になりました。場所は京都三条御幸町です。
なんとも贅沢な空間使いで、スペースの割りにテーブルが少なくゆったりできます。

エスカルゴがウマイ!
サザエみたいでおいしかった。

ワインをボトル2本(赤・白一本ずつ。名前忘れた。) 開け、お勧めのブランデーをオーダし・・・とまぁよく飲んだのでウェイターさんがチーズだり、ドライフルーツなりいろいろとサービスしてくれました。

かなり満腹だったのですが、三条のちんゆうの看板を見つけ、
ちんゆうにかけこみラーメンを完食。
翌日、暴飲暴食のため胃が痛くなる。。このへんが21歳なんだろう(笑

3年間使っていた財布から、
BOTTEGA VENETTAのイントレチャート(編みこみ)の財布に買い換えました。

BOTTEGAに爬虫類の絵が描いていて、鼻緒?の部分がわに革になっているサンダルがめちゃめちゃカッコよかった・・。でも、かかとの減りが早くすぐ靴をだめにしてしまうたちので、もって3ヶ月。
と考えると、5万のサンダルは買えなかった(笑

男のステータス的に見られるのが、
・時計
・ペン
・財布
の三つのアイテムだそう。

BOTTEGAもイントレチャートが特徴のため、見たらそれとすぐそれとわかるらしい。
時計はなかなか手がでませんね。。フランクミューラーのロングアイランドカッコヨス。
ペン?確かにペンはかっこいいよ。カードのサインのときにマイペンてかっこいいよ。
・・ジャケットに内ポケットついてないねんけど。

優秀な人ほど、それほど外見に気を使っていない人が多いのがこの業界。
自分の中の興味をすべてコンピュータに注ぎ込むため、他に興味を持たないからだろうな。
そういう方向を目指すのではなく、目指せ、ちょいわるデベロッパ。

18日まで赤倉温泉でボード。
18日夜は大阪でm-flow, sold outらの集まるライブに参加。これ楽しみ。
19日から末までまた、東京でUIEのお仕事。

Tuesday, March 14, 2006

セッションをMySQLのオンメモリストレージに載せるための考察

MySQL5.0.3からVARCHARのMAXが65535byteまで増えたという情報を入手。

http://dev.mysql.com/doc/refman/5.0/en/char.html
http://dev.mysql.com/doc/refman/5.0/en/news-5-0-3.html

この二つの記事中の以下の情報をマージすると・・・

MEMORY (HEAP) can have VARCHAR() fields.
The length can be specified as a value from 0 to 255 before MySQL 5.0.3, and 0 to 65,535 in 5.0.3 and later versions.


HEAPエンジンでは、VARCHARが使えるようになりましたよ。
んでもって、MySQL5.0.3からVARCHAR型は、最大65,535byteまで格納できますよと。

で、実際に次のようなクエリを発行。

CREATE TABLE sessions_heap (`id` int(11) DEFAULT NULL auto_increment PRIMARY KEY, `session_id` varchar(255), `data` varchar(65500), `updated_at` datetime)engine=heap;

ERROR 1163 (42000): The used table type doesn't support BLOB/TEXT columns


さてさて、雲行きが怪しくなってきました。
同じクエリをengine=MyISAMとかにすればクエリは問題なく実行できます。
どうやらVARCHARのMAX以外の制限に依存していそうです。

で次は、VARCHARのサイズを減らして実行。

CREATE TABLE sessions_heap (`id` int(11) DEFAULT NULL auto_increment PRIMARY KEY, `session_id` varchar(255), `data` varchar(20000), `updated_at` datetime)engine=heap;

Query OK, 0 rows affected (0.01 sec)


どうやら、VARCHARは確かにサポートしている様子。
65500だとTEXT/BLOB型だと見なされてエラーになる。

そこで、VARCHARの限界値を探ってみた。

CREATE TABLE sessions_heap (`id` int(11) DEFAULT NULL auto_increment PRIMARY KEY, `session_id` varchar(255), `data` varchar(21800), `updated_at` datetime)engine=heap;

ERROR 1118 (42000): Row size too large. The maximum row size for the used table type, not counting BLOBs, is 65535. You have to change some columns to TEXT or BLOBs


先程と結果が変わった。
なるほど。HEAPエンジンの一行あたりのサイズの制限に引っ掛かった様子。もういちど、

http://dev.mysql.com/doc/refman/5.0/en/char.html
もう一度読み返してみることに。

The length can be specified as a value from 0 to 255 before MySQL 5.0.3, and 0 to 65,535 in 5.0.3 and later versions. (The maximum effective length of a VARCHAR in MySQL 5.0.3 and later is determined by the maximum row size and the character set used. The maximum length overall is 65,532 bytes.)


あ~あ。完全に読み飛ばしていた。
VARCHARの制限の前に、一行あたりの最大サイズの制限に引っ掛かる様子。HEAPエンジンは内部では、BTREEインデクスやHASHと同じ形なので制限が厳しい様子。

ちなみに、VARCHAR(21000)なら問題なく実行できる。
21000byte -> 21k
セッションデータ入れるには、ちょい厳しいか・・。

そしてtmpfsの話も少しだけ。
これもスレーブ上のMyISAMのテーブルを設置する以外は使い道がなさそう。セッションテーブルをtmpfsで運用するためには、障害時のことを考えて何かしら対策を打っておかないとデータが逝ってしまう。

セッションテーブルのレプリケーション先は、InnoDB on ext3にしておいてディスクに残すとかかな。

なかなかうまくいかない。。

Thursday, March 09, 2006

[Rails plugin] Rails session with MySQL MEMORY storage

--------------------------------------------------------------
今日の午前。

RailsのSession DataをInnoDB&MEMORYストレージ両方に書き込むプラグイン。

参照は、sessions_heapテーブルに。
更新は、sessionsとsessions_heapに。

使い方は、enviroment.rbの編集と、rake create_sessions_tableのみ。
さらに、DBに保存してしまえばWEB負荷分散時の心配もいらず。
美しい。今から作ろう。

セッションデータは、厳密な同期が求められる?
からマスタに問い合わせた方がいいのかなぁ。
--------------------------------------------------------------

今日の午後。

死亡_ト ̄|...........○

7.4. HEAP テーブル

プラグインの開発が万事うまくいき、セッションテーブル作成用のtaskも書き、
さぁLet's rake.

[furutani@furutani]/site/sandbox% rake create_sessions_table_with_heap
(in /site/sandbox)
rake aborted!
Mysql::Error: #42000The used table type doesn't support BLOB/TEXT columns: CREATE TABLE sessions_heap (`id` int(11) DEFAULT NULL auto_increment PRIMARY KEY, `session_id` varchar(255), `data` text, `updated_at` datetime) ENGINE=HEAP

(See full trace by running task with --trace)
[furutani@furutani]/site/sandbox%


はいはい、そうですか。知らなかった。。
今日の一日は一体・・・。

tmpfsにon diskのテーブル載せるよりもHEAPの方が2倍近く速いって言ってる人がいたのに。
名案募集中です。

Wednesday, March 08, 2006

ActiveRecord Cluster (複数データベースを扱うプラグイン)

Active Record Cluster

ttp://rakuto.nobody.jp/src/active_record_cluster.zip

直接リンクだと403になります。
右クリックでURLをコピーして直接URLバーに入力してください。

を作りました。

で、これは何かって言うと

DBコネクションをクラスタ単位で扱うためのプラグイン。
「DB一台じゃクエリ捌くの無理だろ」って時に使います。

だけど、ActiveRecordは複数DBを扱うのに向いていないんですよ。
というかRails作者のDavid自身が一つのDBでARを使うことを強くお勧めしています。

そのため内部では、クラス名をハッシュキーとスレッドIDをキーとしてコネクションアダプタ(ActiveRecord::ConnectionAdapter)をキャッシュしています。

ActiveRecord Clusterでは、クラスタ毎に
「更新系リクエストのためのコネクションアダプタ」
「参照系リクエストのためのコネクションアダプタ」
を同時に管理できるように、キャッシュ機構を設けました。

個人用途ではあまり意味無さげ。
社内ではSVNレポジトリ立てたんだけど、外部に公開できるサーバ持って無いので、
ダウンロードして手動で/path/to/rails-app/vendor/plugins以下に配置します。

---------
更新履歴
---------
2005/02/16
スケジューリングアルゴリズムモジュールを切り離し、
コネクションのキャッシュを一つのメソッドで一元管理するようにしてDRY(Don't repeat your self).

2005/02/17
○コネクションの冗長化を実装しました。
(参照系リクエストの場合にスレーブ障害が起きていて接続できない場合、スレーブを接続対象から切り離し他のスレーブに接続を試みる。全てのスレーブがダウンしている場合は、マスタに対して接続を試みる。)

○ClusterモジュールをMixinして拡張を行う方法に変更。
(以前はActiveRecord::Baseに特異クラスを定義していた。)

2005/03/07
○Active Record Clusterの#delete or #delete_all呼び出しの際にコネクションをマスタに付け替える実装を付け加えました。
○バグ修正

ソースコード: Active Record Cluster

READMEを引用
== Welcome to Active Record Cluster

Active Record Clusterは、Active Recordを拡張するプラグインです。
一つマスタと複数台のスレーブを一つのクラスタとして扱い、
クラスタに対してコネクションを獲得することが出来るように拡張しています。

設定したクラスタへのコネクションで更新系クエリは全てマスタに対して
獲得され、参照系のクエリはスレーブ間で分散されます。

スレーブ間での負荷分散のアルゴリズムは現在のところラウンドロビンスケジューリングのみをサポートしています。

冗長化機能について (参照系のリクエストのみ)
接続先のホストに障害が発生していて接続できない場合、他のスレーブに対して
障害が起きているホストをクラスタから切り離し、他のスレーブに対して接続を試みます。
もし、全てのスレーブがダウンしていた場合は、マスタに対してコネクションを確立します。

Author:: rakuto (http://rakuto.blogspot.com/)
Copyright 2006 rakuto (http://rakuto.blogspot.com/)
License:: MIT

== Getting start
1. 始めにclusters.ymlというファイルを編集する必要があります。このファイルにはDBのクラスタ情報を書いておきます。書けたら#{RAILS_ROOT}/config/clusters.ymlにデプロイでOKです.

2. 次にclusters.ymlのクラスタ設定ファイル中に設定したホスト情報の接続情報を#{RAILS_ROOT}/config/database.ymlに書きます。文法はRailsのdatabse.ymlと同じです。

3. 使用するApplicationController(デフォルトなら#{RAILS_ROOT}/app/application.rb)中にクラスタ情報の初期化命令をbefore_filter(ActionController::Filters::ClassMethods)のコールバックメソッドとして指定します。

class ApplicationController < ActionController::Base
before_filter :init_cluster

def init_cluster
ActiveRecord::Base.initialize_clusters
end
end

こんな感じです。

4. 後は、各モデルで接続先のクラスタを指定します。クラスタの指定にはspecify_clusterメソッドを使用します。

class Hoges < ActiveRecord::Base
specify_cluster :hoge_cluster01
end

ID等の条件で一つのテーブルを複数ホストに分割したい場合は、コールバックメソッドを定義する。コールバックメソッドでは、接続先のクラスタ名を返す。

class Hoges < ActiveRecord::Base
specify_cluster_condition :condition

def conditon
if self.id %lt; 1000000
'hoge_cluster01'
else
'hoge_cluster02'
end
end
end

ですがこの機能はまだテストしていません。
次のバージョンで動作するようにします。はい。


== config file format

1. clusters.yml
次のようなフォーマットになります。
スレーブは省略できますが、マスタは省略できません。(そんな構成はないので。)
ルートの要素は、#{RAILS_ENV}の値にしてください。
ここに書いたホストへの接続情報をdatabase.ymlに指定します。

production:
hoge_cluster01:
master: master-db001
slaves:
- slave-db001
- slave-db002
- slave-db003

2. database.yml
指定するスペックはRailsと同じです。

master-db001:
host: master-db001
user: david
database: transaction-db

5. TODO
ID等の条件による接続先クラスタの切り替え
UnitTestの作成.



Tags:  

Monday, March 06, 2006

[Vol.3] RailsとMySQLによる大規模サイト構築実験 

MySQL + Railsで大規模サイト構築実験もいよいよ大詰めです。

前回までは、
DB : Active-Active
LB : Active-Passive

構成を想定して構築してきました。

また、レプリケーションの遅れによる更新の衝突を避けるための解をいくつか紹介しました。

だがActive-Active構成にするコストが大きい!!
「そもそもActive-Active構成で組む必要があるのかどうか?」といった疑問が沸いてきました。

調査を進めたところ、DBのActive-Active構成にするコストに対して、メリットが少ないことが判明。

[Active-Active構成を組むメリット]
瞬間的なコネクション増への対応が可能。
コネクションの負荷分散のみ可能。(I/Oの負荷分散はできない)

[Active-Active構成を組むデメリット]
更新系クエリの負荷分散は不可。
レプリケーション遅延による更新の衝突の問題。
厳密な同期を要するアプリでは、どちらのDBにもレコードを問い合わせる必要がある。
レプリケーション遅延の度合いを監視する必要がある。

以上を考慮した結果、
DBもActive-Passive構成で組むという結論に至ることになりました。

[MySQLをActive-Standby構成で組む]
MySQLをActive-Passive構成で組むために、ldirectordを少しハック的に使います。

2台の実MasterサーバとVIPを
real server1: 192.168.10.219
real server2: 192.168.10.230
VIP : 192.168.10.226

と定義する。

ldirectord.cfの設定で、通常なら
virtual=192.168.10.226:mysql
real=192.168.10.219:mysql gate 10
real=192.168.10.230:mysql gate 10
fallback=127.0.0.1:mysql gate

とするところを

virtual=192.168.10.226:mysql
real=192.168.10.219:mysql gate 10
fallback=192.168.10.230:mysql gate 10

とする。

このマジックは、fallbackサーバにスタンバイサーバを指定することによりActive-Passive構成を実現します。

※fallbackサーバは、全ての実サーバに接続できないときに接続するサーバです。通常はローカルホストを指定します。


これでActiveのMySQLが落ちた場合にスタンバイ側にコネクションが確立されるようになり、高可用性を実現できました。

WARNING!!!!
一つ注意するポイントがあります。この問題でまる2日ハマりました。
LBを配置しているホストにDBもWEBも配置している場合、WEBからDBへのリクエストはLBを経由して、DBにフォワードされます。この構成では、ダイレクトルーティング使用時、リクエストのフォワーディングに失敗します。

上の状況、つまりロードバランサー(Linux directord)がゲートウェイになりうる時は、パケットの送信元がVIPになるが、LB自体のインタフェースにもVIPが設定されているため偽装さ れたパケットとみなさ、ldirectordによって廃棄されるという罠があります。

詳しくは、
http://ultramonkey.jp/papers/lvs_tutorial/html/

-- 引用 --
しかし、場合によっては(Linux Directorが、本当に実サーバのネットワークのゲートウェイである場合など)、実サーバからの返信パケットをLinux Director経由でルーティングすることが望ましいこともあります。このようなパケットの送信元アドレスはVIPになります。ただし、このVIPは Linux Directorのインターフェイスに属しているので、Linux Directorはパケットが偽造されていると見なしてこれを破棄してしまいます。

この問題を解決する方法はいくつかあります。最もよいのは、Julian Anastasov提供のカーネル・パッチを適用することです。このパッチは、インターフェイスごとにパケット破棄の動作を無効にすることができる procエントリを追加します。このパッチはhttp://www.ssi.bg/~ja/#lvsgwから入手できます。



[更新系の負荷分散]
更新系の負荷分散は、Vol1でも書いたとおり、クラスタ自体のパーティションにより実現します。クラスタのパーティションには二つあります。

・役割に基づくパーティション
・データに基づくパーティション

現在のバージョンのActive Record Clusterでは、役割に基づくパーティションのみサポートしています。


これでMySQL+オープンソースプロダクトを用いたMySQLクラスタの実現に成功しました。
Active-Recordと組み合わせれば、大規模サイトの構築も簡単にできてしまいます。
レプリケーション遅延による自動増分キーの衝突も発生しません。

・・・すばらしい(笑

[Vol.2] RailsとMySQLによる大規模サイト構築実験

vol2でも引き続き、負荷分散・高可用性を備えるDBとWEBアプリケーションフレームワーク(以下AF)のセットアップを目指します。



デュアルマスタ構成に複数台のスレーブがぶら下がっています。
もう少し、詳しく今考えているアーキテクトを見ていきたいと思います。

最小構成: マスタ2台, スレーブ1台

最低3台のDBを用意します。
なぜ3台か?スレーブを追加する際には、マスタのスナップショット取得のためにマスタへの更新を停止する必要があります。データ量が増えると、マスタを停止してスナップショットを取得するには、長時間を要するため、これは現実的な解ではない。

そこで、スレーブをマスタのスナップショットとして考え、スレーブへのレプリケーションを一時停止し、スレーブのスナップショットを新規に用意したスレーブにコピーしレプリケーションを設定する。

スレーブが1台しか存在しない場合でのスレーブ停止時は、参照系のクエリは、全てマスタに向けられるようにActive Record Clusterを実装しています。

また、ロードバランサー自体も冗長化され2台のマスタ上にActive-Passive構成で配置されています。

デュアルマスタ構成構築に当たっての注意点
デュアルマスタは、すべての問題を万事解決する銀の弾丸では無いということを理解しておかなければなりません。
具体的には、次のような問題を念頭に入れておくべきです。

・更新がリアルタイムで反映されるわけではない

つまり、レプリケーション遅延がクリティカルになるようなサービスには向かないということです。でも、現状のBtoCサービスを見る限りおそらくこれは、クリティカルな問題ではないと思ってます。

レプリケーション遅延時間の許容値
レプリケーション遅延時間の許容値を設定するのは非常に難しいです。
また、レプリケーションの遅れは定期的に監視しておくべきです。

・レプリケーション遅延時間の監視

最新のタイムスタンプのみを格納するテーブルを一つ用意し、このテーブルに対して一定の間隔でマスタがレコードを挿入するようにする。そして監視プロセスが一定間隔置きでこのレコードを読み出し→タイムスタンプを調べる。

→Nagios経由で監視するのがベター。

レプリケーション遅延が大きくなってきたときの解決策。
レプリケーション遅延が許容できなくなるぐらい大きくなったときは、もう一台クラスタを用意してテーブルを分割→負荷分散する。

[vol.1]で、次のような考察点が上がりました。
考察1 - 参照系クエリもLBで振り分けるようにするか
考察2 - マスタ間でのレプリケーション遅延により発生する、
      AUTO_INCREMENTシーケンスの衝突問題を回避するための対策
考察3 - テーブル設計の工夫

以上の考察点をもう少しブラッシュアップ。


考察1
参照系のクエリをAFで振り分けるのか?LBで振り分けるのか?

LBをセットアップするのが面倒。
AF側でリクエストの負荷分散を管理する方がよさそうである。


考察2
デュアルマスタのアーキテクトを選択することにより、AUTO_INCREMENTカラムのシーケンスの衝突が発生する問題である。レプリケーションが完了する前に各マスタがINSERTクエリを受け付けシーケンスを割り当ててしまい、レプリケーション時にキーが衝突し一意制約に反するため、レプリケーションに失敗し同期できなくなる。

現在の解決方法は二つである。
・ID値として採用しているAUTO_INCREMENT属性をやめ、MD5ハッシュを使用する。
→レプリケーション遅延によるキーの衝突はなくなりそう。

・外部のライブラリを使用し、グローバルな固有識別子(GUID)を使用する。

しかし、どちらも現実的な解ではなさげ。

もう少し、ハックなアプローチを紹介。
1. マルチパートキー
2. AUTO_INCREMENTフィールドの拡張

マルチパートキー
一つのプライマリキーを用いるのではなく、AUTO_INCREMENTなプライマリキーとMySQLで定義するserver_idのマルチパートキーをプライマリキーとして使用する。

では、INSERTのケースの場合どのようにserver-idを知ることが出来るだろうか?
MySQLでは、SERVER_ID()関数を使って自身のサーバIDを取得することが出来る。
例えば、

INSERT INTO drecoms values (SERVER_ID(), 'naito', 'naito@drecom.co.jp');

しかし、この方法には大きな問題点がある。。
→マルチパート自動増分キーはMyISAMしか使えない。
→外部キーの設定が難しくなる。

マスタはInnoDBをストレージエンジンとして使用するだろうから、これは却下。
また、AF側で決めうちでサーバIDを指定する方法がありますが、LBを通しているため、どこのマスタにデータが格納されるかわからないので、実は決めうちでserver-idを指定できない罠。

→よって却下

AUTO_INCREMENTカラムの拡張
IDカラムをINTEGER型ではなく、BIGINTを使用し、64bitに拡張。
上位32bitにサーバIDを格納し、下位32bitにIDを格納。
マルチパートキーを一つのキーで実現します。

このアプローチが一番現実的。
AR側に拡張が必要になるかもしれない。

問題点
→レコード件数が膨大になったときに、DISK容量が爆発する。
→ID値が広く分散する。


考察3
異なるクラスタ間でのテーブルのJOINには(現状)対応できないため、JOINが発生しないようにテーブルを分散できるよう設計する必要がある。


現状の進捗
現在、デュアルマスタ&スレーブ一台のクラスタの構築を完了済み。
Active Record Cluster経由でクエリが正常に分散されることも確認済み。

障害時の動作検証
Heartbeat障害&ホスト障害→Activeに発生した場合はフォールバックされることを確認。
MySQLプロセス障害→LVSハッシュテーブル・Ldirectordともに正常動作しているように見受けられるが、なぜかリクエストがルーティングされない。

→デバッグ中。

TODO
・デュアルマスタ構成はシンプルなマスタスレーブ構成に比べて少し複雑。
障害別対応をきちっと決めておく必要がある。

・MySQLの設定をデフォルトで使用しているケースが見受けれる。
例えば、スレーブが一台死んだだけで、リソースはあまっているのにMaxClientの制限に引っ掛かって接続できないようでは意味が無い。パラメータの設定基準を明確にしドキュメント化する。

[Vol.1] RailsとMySQLによる大規模サイト構築実験

大規模サイト構築のための土台を作っていきます。
ASP事業に力を注入するとなると、24H7D動作し続ける安定したサービスのためのインフラがまうます必須になるはずです。

アーキテクト
WEBサーバ 何でもいい。
WEBアプリケーションフレームワーク Ruby on Rails
DB MYSQL

で実験していきます。

とりあえず、必要そうなもの。
1. WEB 負荷分散 ・冗長化
2. DB 負荷分散
3. DB 冗長化
4. Railsの拡張(DBへのコネクション周り)

まず1番。
ロードバランサー使えばできるし現在のドリコムでも、DUOBLOG APIやCMS ASPはクリアしてます。DNSラウンドロビンは、障害検知が不可能なのでNGです。

次は、2番。
クエリは、参照系クエリと更新系クエリに分類されます。
今ドリコムでDBへのクエリを負荷分散させているプロジェクトは(僕の知っているところは)ありません。

次は、3番。
DB自体の冗長化です。
MySQLを使用しているのでDUOBLOG 検索APIがDBの冗長化をサポートしています。
単純なマスタ・スレーブ構成を構築し、Mojavi側でコネクションの切り替え(マスタが死んでいる際はスレーブに接続)を実装しました。

でも、これって完全には冗長化されていませんね。現状参照系クエリの冗長化しか行っていません。通常マスタが死んだ場合はスレーブに引き継ぐことになりますが、フェールバックは手動になります。

次は、4番。
Active Record Clusterをこのために作りました。


やることイパーイなのですが、このシリーズではMySQL自体の冗長化と負荷分散とRails側の拡張を実験していきます。

アーキテクト
次のようなデュアルマスタ構成に複数代のスレーブがぶら下がっている、クラスタがたくさんあると考えてください。



更新系のクエリはLB(負荷分散装置)が振り分け、参照系のクエリについてはRails側で振り分けます。
(参照系クエリもLBで振り分けるようにするか- 考察1)

一つ注意が必要なのは、更新系のクエリは図に示しているLB(ロードバランサ)で分散されるわけではありません。ここでLBをはさんでいるのはあくまでも 冗長性のためです。マスタ間でレプリケーションが行われるため、リクエストの受け付けの負荷分散が出来るだけで本当の意味での負荷分散が出来るわけではあ りません。

(マスタ間でのレプリケーション遅延により発生する、AUTO_INCREMENTシーケンスの衝突による問題を回避するための対策 - 考察2)

では、更新系の負荷分散はどうするか?
このようなクラスタを複数用意します。テーブルによって所属するクラスタ自体を分けてしまいます

(テーブル設計の工夫 - 考察3)

考察と書いたところは、考える必要があるポイントです。
まずは、マスタへのクエリの分散を行うためのLBを構築したいと思います。
Heartbeat + Ldirectordを使用して構築します。

技術的には全く難しいことはしてないんですけど、セットアップが若干めんどくさい。。
レイヤ4スイッチングを使用し、MySQLへクエリを発行し正常に結果を取得できなかったら、DBサーバをハッシュテーブルからはずすだけです。(正確にはWeightを0に設定しリクエストが振り分けられなくする。)

LBはActive-passive構成で組み、DBはActive-Activeで組みます。

セットアップ手順

1. カーネルの再構築
ARP問題を回避するためにパッチを当てたカーネルが必要です。
linux-2.6.11を使用します。最新のlinu-2.6.16でやると、カネパになりました。

http://kernel.org/
ここからカーネルをwget.

# tar jxvf kernel-x.x.x.tar.bz2
# cd kernel-x.x.x

http://www.ssi.bg/~ja/#hidden
ここからカーネルのバージョンにあったパッチをwget.

# patch -p1 < style="font-weight: bold;">2. ネットワーク設定
IPフォワーディングとか隠れデバイスを有効にするための設定を行います。
ARP問題を解決するために、ループバックインタフェースを隠します。
言葉の意味がわからなければ気にしないでください。

/etc/sysctl.conf
を編集します。
次のように設定します。
した二つは、設定に無いので新たに追加します。

net.ipv4.ip_forward = 1

# 隠れデバイスの設定を有効にする
net.ipv4.conf.all.hidden = 1
# ループバックインタフェースを隠す
net.ipv4.conf.lo.hidden = 1

設定が完了すれば、
# /sbin/sysctl -p
で設定が反映されていることを確認します。


3. Heartbeatのインストール
Heartbeatはソースからインストールします。v2.xを使用します。
依存関係が複雑なので必要なプロダクトを先にインスコします。
インスコの順番は次のようにすればまず、間違いはないはず。

1. libgpg-error
2. libgcrypt
3. gnutls
4. libnet
5. heartbeat

Heartbeat以外は
$ ./configure
$ make
$ sudo make install

Heartbeatのみ
$ ./ConfigureMe configure
$ make
$ sudo make install

です。
もし、エラーが出る場合は足りないプロダクトをインスコしてください。


4. ldirectordのインストール

$ cvs -d:pserver:guest@cvs.linux-ha.org:/home/cvs/linux-ha login
$ cvs -z3 -d:pserver:guest@cvs.linux-ha.org:/home/cvs/linux-ha co linux-ha/ldirectord/

login時に要求されるパスワードは、"guest"です。
で完了です。


5. デプロイ
プログラム・設定ファイル類をコピーします。

# cp ldirectord /etc/ha.d/resource.d/
# chmod +x /etc/ha.d/resource.d/ldirectord
# cp ldirectord.cf /etc/ha.d/
# cp /usr/share/doc/heartbeat-2.0.3/authkeys /etc/ha.d/
chmod 600 /etc/ha.d/authkeys

6. 設定
Heartbeatは、この記事あたりを見れば設定できるはず。

Ldirecotordの設定が多少異なります。

以下は、/etc/ha.d/ldirectord.cfの設定サンプル。
192.168.10.226がVIPで、realが実サーバ(マスタ).
requestには実行するSQLクエリ、serviceにはmysql、login, passwd, databaseをそれぞれ指定します。サービスにDBを指定した場合は、recieveディレクティブは使用できません。

virtual=192.168.10.226:3306
real=192.168.10.219:3306 gate 1
real=192.168.10.230:3306 gate 1
fallback=127.0.0.1:3306 gate
service=mysql
request="SHOW VARIABLES;"
# receive="Test Page"
# virtualhost=some.domain.com.au
scheduler=rr
#persistent=600
#netmask=255.255.255.255
protocol=tcp
login="heartbeat"
database="test"
passwd="hogera"

大抵一発では通りません。
デバックしましょう。
ldirectodのデバックは簡単です。-dオプションをつけて起動すると、デバックモードとして起動し、デーモンになりません。

$ /etc/ha.d/resoruce.d/ldirectord /etc/ha.d/ldirectord.cf

出力されたメッセージを見ましょう。
PerlのMySQLモジュールが無いと出ていないですか?
出ている場合は、インスコします。

http://search.cpan.org/dist/DBD-mysql/lib/DBD/mysql.pm
をwget.

$ perl Makefile.PL
$ make
$ sudo make install

もう一度実行します。
モジュールDBDも無いといわれた場合は、CPANからインスコします。

ここからレプリケーションを組んでスレーブ遅延による問題を回避し、
考察点としてあげたあたりをvol2で追求します。

テーブル設計工夫にあたり、Rails側の拡張ももう少し必要になるかもしれませんね。