Doctrine2 でレプリケーションする(マスタースレーブ構成)

Doctrine2 を使うようになって、開発スピード上ったなと思う反面、思い通りにいかないことがちょいちょいあって、結局 createNativeQuery() しちゃった方が痒いところに手が届くじゃんなんてことにもなっているわけだけど、いかんせん Doctrine2 の日本語の情報が少ない。なので、自分が悩んだこととかを記事にしてるんだけど、今回はマスター/スレーブをどうするのか?という話。

以前のプロジェクトでは Ethna の Ethna_Backend::getDB メソッドを使っていてマスター/スレーブ構成にもしててそこそこなアクセスを捌いてたりしてたんだけど、新しいプロジェクトでは Doctrine2 を使おう、で小さく始めるからとりあえずレプリケーションについては後回しにしてきた。インフラさんには一応いつでも使えるようにとマスター/スレーブを用意してもらってたんだけど、なかなか使う機会がなくて。

そもそも使い方がわからない、、、。他のタスクもあるのでとりあえず手が空いたらちょっと調べてみるって感じだったんだけどググっても英語の記事ばかり。片手間では深くは追えなかった。

そうこうしているうちにそのときはきた。どうもそれなりに大量データを扱う処理がべらぼうに時間がかかってしまっている。これまでもメモリリークの問題とかあったけど今回はちょっと様子が違う。その処理中に table lock が極端に増えている。これはちょっとデータベースへの負荷を下げた方がいいんじゃないかということでついにレプリケーション導入とあいなったわけです。

- - -

で、Doctrine2 じゃない方の情報とかに騙されながらようやくマスター/スレーブ構成が可能になりました。Doctrine/DBAL/Connections/MasterSlaveConnection.php のコメントにあるとおりに記述したらなんなくマスター/スレーブ構成になっちゃいましたよwネットの情報ってなんだよって思ったねwで、やっぱソース読もうよってw

これは最初の設定。データベースは1台だけ。細かいところは端折ってあります。

bootstrap.php

use Doctrine\ORM\Tools\Setup;
use Doctrine\ORM\EntityManager;

$config = Setup::createYAMLMetadataConfiguration(
array(__DIR__."/yaml"), $isDevMode = true
);

$conn = array(
'driver' => 'pdo_mysql',
'user' => 'ユーザー',
'password' => 'パスワード',
'host' => 'ホスト'
'dbname' => 'データベース名',
);

$EntityManager = \Doctrine\ORM\EntityManager::create($conn, $config);


Doctrine/DBAL/Connections/MasterSlaveConnection.php にはこんなコメントが書いてあるんだよね。


* @example
*
* $conn = DriverManager::getConnection(array(
* 'wrapperClass' => 'Doctrine\DBAL\Connections\MasterSlaveConnection',
* 'driver' => 'pdo_mysql',
* 'master' => array('user' => '', 'password' => '', 'host' => '', 'dbname' => ''),
* 'slaves' => array(
* array('user' => 'slave1', 'password', 'host' => '', 'dbname' => ''),
* array('user' => 'slave2', 'password', 'host' => '', 'dbname' => ''),
* )
* ));


これを参考にこんな感じで設定してみたらマスター/スレーブできた。スレーブは配列で複数追加できてどのスレーブ使うかは Doctrine がよしなにしてくれるらしい。気が利くやつだわ。


$conn = array(
'wrapperClass' => 'Doctrine\DBAL\Connections\MasterSlaveConnection',
'driver' => 'pdo_mysql',
'master' => array(
'user' => 'ユーザー',
'password' => 'パスワード',
'host' => 'マスターのホスト'
'dbname' => 'データベース名',
),
'slaves' => array(
array(
'user' => 'ユーザー',
'password' => 'パスワード',
'host' => 'スレーブ1のホスト',
'dbname' => 'データベース名',
),
array(
'user' => 'ユーザー',
'password' => 'パスワード',
'host' => 'スレーブ2のホスト',
'dbname' => 'データベース名',
),
 :
)
);


これだけで無条件に参照系はスレーブ、更新系はマスターになった。Doctrine がよしなにしてくれるから「SELECT だからスレーブ、UPDATE はマスター、あ、スレーブに INSERT しちゃった」なんてことにはならない。ただ、これだとレプリケーションで遅延が発生してしまうとまずいんだよな。例えば商品の在庫数とかはマスターを参照しないといけない。この件についてはこれからなんとかする。

今回の件に関する情報が少ないってことは、みんな MasterSlaveConnection.php のコメントに気付いてさらっとマスター/スレーブしちゃってるからなのかな?出来ない出来ないってひといっぱいいたようだけど。

かわのくんとは

Web系IT企業でプログラミングやマネジメントをしています。趣味で音楽を少々。

Youtubeでライブ動画配信中

Ustreamでライブ動画配信中

スマートフォン向けにPCサイトを自動変換(コンバート)する『CONV2SP』 CSS作成支援ツール『CSSツクール』