いよいよ来ましたレプリケーション。本番環境のMongoDBでは障害に対応できるよう複数DBサーバ構成にする。これによりデータ損失やサービス停止を防ぐ。そして負荷分散にもつながるレプリケーションについて学んでいこう。 MongoDB では複数のMongoDBをまとめてReplica Set と呼ぶ。
Hello, ReplicaSet
ローカル環境で Replica Set 構成を作ってみる。 MongoDB 2.4.7
mac 環境では /data/db/
と /usr/local/var/log/mongodb/
のパーミッションを変えてmongod実行ユーザが書き込めるようにしておく。
ちなみに、MongoDB2.4 からは ReplSetTest
コマンドを有効にするには、 mongod
コマンドに --setParameter enableTestCommands=1
を付与する必要があるらしい。ということで、デーモンで既に起動していた mongod を一旦ストップし、このパラメータを付けた状態で再起動した。
ちなみにMac でinit.d的なのは、/Library/LaunchDaemons
以下にあった。ここのxmlを読み込んで起動時にmongodが起動されている状態になっていた模様。
以下Mac の場合の手順
sudo launchctl stop org.mongo.mongod sudo /usr/local/Cellar/mongodb/2.4.7/mongod --dbpath /usr/local/var/mongodb/ --logpath /usr/local/var/log/mongodb/mongodb.log --config /usr/local/etc/mongod.conf --setParameter enableTestCommands=1
mongod が起動したのでいよいよReplica Set を組む。
mongo --nodb replicaSet = new ReplSetTest({"nodes" : 3}) replicaSet.startSet() replicaSet.initiate() { "replSetInitiate" : { "_id" : "testReplSet", "members" : [ { "_id" : 0, "host" : "DevApp.local:31000" }, { "_id" : 1, "host" : "DevApp.local:31001" }, { "_id" : 2, "host" : "DevApp.local:31002" } ] } }
他のターミナルで ps aux | grep mongo
すると
/usr/local/Cellar/mongodb/2.4.7/mongod --oplogSize 40 --port 31000 /usr/local/Cellar/mongodb/2.4.7/mongod --oplogSize 40 --port 31001 /usr/local/Cellar/mongodb/2.4.7/mongod --oplogSize 40 --port 31002
とあるのでどうやらローカルで3台構成になった模様だ。試しに繋いでみる
mongo MongoDB shell version: 2.4.7 connecting to: test > conn1 = new Mongo('localhost:31000') connection to localhost:31000 > t = conn1.getDB('test') test > t.isMaster() { "setName" : "testReplSet", "ismaster" : true, "secondary" : false, "hosts" : [ "DevApp.local:31000", "DevApp.local:31002", "DevApp.local:31001" ], "primary" : "DevApp.local:31000", "me" : "DevApp.local:31000", "maxBsonObjectSize" : 16777216, "maxMessageSizeBytes" : 48000000, "localTime" : ISODate("2014-01-18T05:00:13.905Z"), "ok" : 1 }
ふむ。今回のReplica Setでは31000がmasterとして認識しているようだ。これでデータを入れると、他の salve にデータがレプリケーションされているはずだ。
> for(i = 0; i < 100; i++) { t.coll.insert({count: i}); } > t.coll.count() 100 > conn2 = new Mongo('localhost:31001') connection to localhost:31001 > t2 = conn2.getDB('test') test > t2.coll.find() error: { "$err" : "not master and slaveOk=false", "code" : 13435 }
これは slave の古いデータの読み込みを防ぐためにデフォルトでSlaveへのリクエストを拒否しているために起きているもの。これをOKにするには setSlaveOk()
を実行する。
> conn2.setSlaveOk() > t2.coll.find() { "_id" : ObjectId("52da0c23baa041eafb7c9878"), "count" : 0 } { "_id" : ObjectId("52da0c23baa041eafb7c9879"), "count" : 1 } { "_id" : ObjectId("52da0c23baa041eafb7c987a"), "count" : 2 } { "_id" : ObjectId("52da0c23baa041eafb7c987b"), "count" : 3 } { "_id" : ObjectId("52da0c23baa041eafb7c987c"), "count" : 4 } { "_id" : ObjectId("52da0c23baa041eafb7c987d"), "count" : 5 }
おお〜!2台目にデータがレプリケーションされていることを確認できた! slave にデータをinsertすることはできないので注意。slave は全ての書き込みを拒否する。さて、こっからがレプリケーションの本領発揮。 master をシャットダウンしてみよう。
t.adminCommand({'shutdown': 1}) conn3 = new Mongo('localhost:31002') t3 = conn3.getDB('test') t3.isMaster() { "setName" : "testReplSet", "ismaster" : true, ..... }
今回は3台目がmasterとして役割が切り替わった。これが自動フェイルオーバというものになる。 ちなみにparimary と master そして slave と secondary は同じ意味。
Replica Set を停止するには rs.stopSet()
でOK.
学び
- クライアントは1台のmongodのときと同じオペレーションでprimaryへコマンドを送ることができる。
- client は slave への書き込みはできない。
- デフォルトではクライアントはslaveの読み込みはできない。 setSlaveOk() することでslaveの読み込みが可能になる。
複数サーバ構成にしても同じことだ。
今回はここまで。続きは細かい設定やサーバ構成について書いてまとめてみる予定。