matsukaz's blog

Agile, node.js, ruby, AWS, cocos2d-xなどなどいろいろやってます

MongoDBでReplicaSetにメンバーを追加/削除する方法と、その注意点

MongoDBで、ReplicaSetにメンバーを追加/削除する方法は、Adding a New Set Memberにある通りです。

rs.add("node1:27018");

で追加したり、

rs.remove("node1:27018");

で削除したり。

追加する際は

  1. 既存データを物理的にコピーしてきて戻す方法
  2. カラの状態から同期を取る方法

があります。
1000万件程度であれば、1.でも数分で終わる模様。数億件以上ある場合は、2.で戻した方が作業が早いかもしれません。

注意点1

その際に気をつける点は、local.*のファイルはコピーしてこないこと。local以外のコレクションに対応するファイルだけコピーしてきて、あとは起動後にrs.add()すればOKです。local.*がいると、データの整合が取れなくなって「still initiating」って状態で止まってしまいます。これやっちゃってハマりました・・・orz
ちなみに、その場合でも一度サーバを止めてlocal.*ファイルを全部削除して起動し直せばOKみたいです。

注意点2

あともう一つ注意する点は、メンバーのvotesの数の合計が偶数にならないこと。MongoDB sharding and replication with (only) 3 servers - Part 2: One Replica-Setに以下のように説明されています。

The members of a set will elect their master just as well as we do with our politicians. The master has to get more than half of the available votes. Problem: Imagine a set with four members. What happens if two vote for node A to get master and the two others vote for node B?
Neither A nor B will get master, because no node got more than half of the available votes.
Therefore the number of votes in a replica set has to be odd-numbered. You can reach this by easily add one more node as a new slave to the set - simultaneously increasing database availability. But if you don't have the resources for an additional node, there are two other possibilities:

1. Add an arbiter, which is a mongod process and a member of the set, but only exists to solve majority problems (http://www.mongodb.org/display/DOCS/Adding+an+Arbiter)
2. Change the number of votes the members have, so the number is unequal.

PRIMARYになるためには、全体のvotes数の過半数より多い票を獲得しなければならないため、votes数が偶数になってしまうと、残りのvotes数が半数になった時点でPRIMARYがいなくなってしまいます。例えば、

ノード名 votes
node1 1
node2 1
node3 1
node4 1

となると、上記のうち2台が死んだ時点でvotesの数が過半数の2となってしまい、PRIMARYがいなくなってしまいます。PRIMARYがいなくなるということは、ReplicaSetの構成変更も出来なくなるので、死んだ2台のうちいずれかを起動するまでは何も出来ません。

ノード名 votes
node1 2
node2 1
node3 1
node4 1

であれば、仮にnode3/node4が死んでも、残りのvotes数は3なので、node1/node2のいずれかはPRIMARYでいられます(node1が死んでしまうと、あと1台しか死ぬことは出来ませんが)。この辺はArbiterで調整したり。

自分は、

ノード名 votes
node1 1
node2 1
node3 1

に対して

ノード名 votes
node1 1
node2 1
node3 1
node4 1
node5 1
node6 1

と新たに3つのノードを追加したところ、追加した3つが前述した理由で起動できない状態となっていたため、既存ノードからPRIMARYがいなくなってしまいました。

ノード名 votes State
node1 1 SECONDARY
node2 1 SECONDARY
node3 1 SECONDARY
node4 1 still initiating
node5 1 still initiating
node6 1 still initiating

追加した3つのノードを起動することも出来ず、PRIMARYがいないのでrs.remove()でノードを消すこともできず、完全に詰んだ状態に・・・orz
local.*を消せばよかったみたいですが、このときには気付かなかったので、node1/node2/node3を取得していたバックアップから復元して対応しました。