ReplicaSetで遊ぶ

MongoDBのSharding機能で遊ぶ - hachiのブログで、Shardingを試したので、今度はRelipcaSetで遊んでみました。
構成は以下のようにしました。Shardingのときとポート番号などを変えているのはわかりやすくするためです。
f:id:hs_hachi:20120305235935p:plain
図を見れば一目瞭然ですが、こんな変な構成は実際には組みません。実際は各ShardにReplicaSetを組んでいきます。
今回はSharding部分の説明は省いていきます。

mongodの設定

mongod --shardsvr --dbpath /data/mongod1_r0 --logpath /data/mongod1_r0/log/mongod1_r0.log --port 27030 --fork --nojournal --replSet rs
mongod --shardsvr --dbpath /data/mongod1_r1 --logpath /data/mongod1_r1/log/mongod1_r1.log --port 27031 --fork --nojournal --replSet rs
mongod --shardsvr --dbpath /data/mongod1_r2 --logpath /data/mongod1_r2/log/mongod1_r2.log --port 27032 --fork --nojournal --replSet rs

ここでのポイントは最後の"--replSet rs"です。これでこれら3つが同じReplicaSetということを示してます。ログファイルを見ると

Sun Mar  4 15:48:57 [rsStart] replSet info you may need to run replSetInitiate -- rs.initiate() in the shell -- if that is not already done
Sun Mar  4 15:49:07 [rsStart] replSet can't get local.system.replset config from self or any seed (EMPTYCONFIG)

と出てきます。初期化しなさいということなので、初期化しましょう。

ReplicaSetの初期化

mongo localhost:27030
use admin
var config = {_id: 'rs', members: [ {_id: 0, host: 'localhost:27030'}, {_id: 1, host: 'localhost:27031'}, {_id: 2, host: 'localhost:27032'}]  };
rs.initiate(config); 

_idはいつもMongoが勝手に設定してくれるのですが、ここでは自分で設定します。レプリカ名と対象のHost名を引数にしてinitiateへ渡します。
rs.status()を実行すると、RelipcaSetの状態が表示されます。とりあえず、stateStrを注目すると1つはPRIMARY、残り2つがSECONDARYになっていくのがわかります。順調に進んでいくログを見るのは楽しいですね。

rs.status()
{
     "set" : "rs",
     "date" : ISODate("2012-03-04T15:58:18Z"),
     "myState" : 2,
     "members" : [
          {
               "_id" : 0,
               "name" : "localhost:27030",
               "health" : 1,
               "state" : 2,
               "stateStr" : "SECONDARY",
               "optime" : {
                    "t" : 1330876687000,
                    "i" : 1
               },
               "optimeDate" : ISODate("2012-03-04T15:58:07Z"),
               "self" : true
          },
          {
               "_id" : 1,
               "name" : "localhost:27031",
               "health" : 1,
               "state" : 5,
               "stateStr" : "STARTUP2",
               "uptime" : 10,
               "optime" : {
                    "t" : 0,
                    "i" : 0
               },
               "optimeDate" : ISODate("1970-01-01T00:00:00Z"),
               "lastHeartbeat" : ISODate("2012-03-04T15:58:16Z"),
               "pingMs" : 20,
               "errmsg" : "initial sync need a member to be primary or secondary to do our initial sync"
          },
          {
               "_id" : 2,
               "name" : "localhost:27032",
               "health" : 1,
               "state" : 5,
               "stateStr" : "STARTUP2",
               "uptime" : 10,
               "optime" : {
                    "t" : 0,
                    "i" : 0
               },
               "optimeDate" : ISODate("1970-01-01T00:00:00Z"),
               "lastHeartbeat" : ISODate("2012-03-04T15:58:16Z"),
               "pingMs" : 0,
               "errmsg" : "initial sync need a member to be primary or secondary to do our initial sync"
          }
     ],
     "ok" : 1
}

PRIMARY> rs.status()
{
     "set" : "rs",
     "date" : ISODate("2012-03-04T15:58:30Z"),
     "myState" : 1,
     "members" : [
          {
               "_id" : 0,
               "name" : "localhost:27030",
               "health" : 1,
               "state" : 1,
               "stateStr" : "PRIMARY",
               "optime" : {
                    "t" : 1330876687000,
                    "i" : 1
               },
               "optimeDate" : ISODate("2012-03-04T15:58:07Z"),
               "self" : true
          },
          {
               "_id" : 1,
               "name" : "localhost:27031",
               "health" : 1,
               "state" : 3,
               "stateStr" : "RECOVERING",
               "uptime" : 22,
               "optime" : {
                    "t" : 0,
                    "i" : 0
               },
               "optimeDate" : ISODate("1970-01-01T00:00:00Z"),
               "lastHeartbeat" : ISODate("2012-03-04T15:58:28Z"),
               "pingMs" : 4,
               "errmsg" : "initial sync need a member to be primary or secondary to do our initial sync"
          },
          {
               "_id" : 2,
               "name" : "localhost:27032",
               "health" : 1,
               "state" : 3,
               "stateStr" : "RECOVERING",
               "uptime" : 22,
               "optime" : {
                    "t" : 0,
                    "i" : 0
               },
               "optimeDate" : ISODate("1970-01-01T00:00:00Z"),
               "lastHeartbeat" : ISODate("2012-03-04T15:58:28Z"),
               "pingMs" : 0,
               "errmsg" : "initial sync need a member to be primary or secondary to do our initial sync"
          }
     ],
     "ok" : 1
}
PRIMARY> rs.status()
{
     "set" : "rs",
     "date" : ISODate("2012-03-04T15:58:50Z"),
     "myState" : 1,
     "members" : [
          {
               "_id" : 0,
               "name" : "localhost:27030",
               "health" : 1,
               "state" : 1,
               "stateStr" : "PRIMARY",
               "optime" : {
                    "t" : 1330876687000,
                    "i" : 1
               },
               "optimeDate" : ISODate("2012-03-04T15:58:07Z"),
               "self" : true
          },
          {
               "_id" : 1,
               "name" : "localhost:27031",
               "health" : 1,
               "state" : 2,
               "stateStr" : "SECONDARY",
               "uptime" : 42,
               "optime" : {
                    "t" : 1330876687000,
                    "i" : 1
               },
               "optimeDate" : ISODate("2012-03-04T15:58:07Z"),
               "lastHeartbeat" : ISODate("2012-03-04T15:58:48Z"),
               "pingMs" : 0
          },
          {
               "_id" : 2,
               "name" : "localhost:27032",
               "health" : 1,
               "state" : 2,
               "stateStr" : "SECONDARY",
               "uptime" : 42,
               "optime" : {
                    "t" : 1330876687000,
                    "i" : 1
               },
               "optimeDate" : ISODate("2012-03-04T15:58:07Z"),
               "lastHeartbeat" : ISODate("2012-03-04T15:58:48Z"),
               "pingMs" : 0
          }
     ],
     "ok" : 1
}

Shardingの設定
少しだけShardingの設定がかわります。mongosに接続後

mongos> db.runCommand({'addShard': 'rs/localhost:27030,localhost:27031,localhost:27032'})
{ "shardAdded" : "rs", "ok" : 1 }
mongos> db.runCommand({'addShard': 'localhost:27040'})
{ "shardAdded" : "shard0000", "ok" : 1 }
mongos> db.runCommand({'addShard': 'localhost:27050'})

として、最初のshardを追加するときに"レプリカ名/ホスト名1,ホスト名,…"とします。Shardの設定を見ると

mongos> db.runCommand( { listshards : 1 } ); 
{
     "shards" : [
          {
               "_id" : "rs",
               "host" : "rs/localhost:27030,localhost:27032,localhost:27031"
          },
          {
               "_id" : "shard0000",
               "host" : "localhost:27040"
          },
          {
               "_id" : "shard0001",
               "host" : "localhost:27050"
          }
     ],
     "ok" : 1
}

で、OKです。
これで、PRIMARYのプロセスをkillすると、自動的にSECONDARYに切り替わります。

Mon Mar  5 16:08:29 [rsHealthPoll] replSet info localhost:27030 is down (or slow to respond): DBClientBase::findN: transport error: localhost:27030 query: { replSetHeartbeat: "rs", v: 1, pv: 1, checkEmpty: false, from: "localhost:27031" }
Mon Mar  5 16:08:29 [rsHealthPoll] replSet member localhost:27030 is now in state DOWN
Mon Mar  5 16:08:30 [rsMgr] replSet info electSelf 1
Mon Mar  5 16:08:30 [rsMgr] replSet PRIMARY

落ちてる間にデータを更新してみましょう。

mongo localhost:27021
MongoDB shell version: 2.0.2
connecting to: localhost:27021/test
mongos> var user = db.user.findOne({userId:1})
mongos> user
null

お、ひっかからない。PRIMARYを見に行ってみよう。

PRIMARY> var user = db.user.findOne({userId:1})
PRIMARY> user
{
	"_id" : ObjectId("4f54e670cc67d707e0df245e"),
	"userId" : 1,
	"name" : "userName11",
	"createAt" : 1330964080067
}

あるね。再度、mongosから見に行ってみよう

mongos> var user = db.user.findOne({userId:1})
mongos> user
{
	"_id" : ObjectId("4f54e670cc67d707e0df245e"),
	"userId" : 1,
	"name" : "userName11",
	"createAt" : 1330964080067
}

あれ?あるなぁ…データの同期中だったのかな?ちなみにSECONDARYから見るとどうなるのかな?

SECONDARY> use blog
switched to db blog
SECONDARY> var user = db.user.findOne({userId:1})
Mon Mar  5 16:22:38 uncaught exception: error { "$err" : "not master and slaveok=false", "code" : 13435 }

ここでslaveが見れないのはslave側でデータの取得を許可していないためです。rs.slaveOkを実行するとslaveから実行できます。
とりあえず基本的な設定はこのへんで終了。

参考:
Replica Sets - MongoDB
Replica Set Tutorial - Docs-Japanese - 10gen Confluence