MongoDB レプリカセットは、1 つのプライマリーノードと複数のセカンダリーノードで構成されます。すべての書き込みはプライマリーノードで行われ、セカンダリーノードはデータを複製して同一のデータセットを維持し、高可用性を提供します。
次の図は MongoDB の公式ドキュメントからのもので、1 つのプライマリーノードと 2 つのセカンダリーノードを持つ典型的なレプリカセットを示しています。
プライマリー選出
レプリカセットは replSetInitiate または rs.initiate() を使用して初期化されます。 初期化後、メンバーはハートビートを交換してプライマリを選出します。 過半数の票を獲得したノードがプライマリになり、残りのノードはセカンダリになります。
レプリカセットの初期化
config = {
_id : "my_replica_set",
members : [
{_id : 0, host : "rs1.example.net:27017"},
{_id : 1, host : "rs2.example.net:27017"},
{_id : 2, host : "rs3.example.net:27017"},
]
}
rs.initiate(config)
「過半数」の定義
N 個の投票メンバーがいる場合、過半数は N を 2 で割った値 (小数点以下切り捨て) に 1 を加えた数となります。アクティブなメンバーが過半数に満たない場合、レプリカセットはプライマリーを選出できず、読み取り専用になります。
|
投票メンバー数 |
過半数 |
許容障害数 |
|
1 |
1 |
0 |
|
2 |
2 |
0 |
|
3 |
2 |
1 |
|
4 |
3 |
1 |
|
5 |
3 |
2 |
|
6 |
4 |
2 |
|
7 |
4 |
3 |
メンバーは奇数にすることを推奨します。3 ノードと 4 ノードのレプリカセットはどちらも許容障害数が 1 ですが、4 ノードの方がより信頼性の高いデータストレージを提供します。
特殊なセカンダリーノード
デフォルトでは、セカンダリーは選出に参加し、プライマリーになることができ、プライマリーからデータを同期して同一のデータセットを維持します。
セカンダリーは読み取りリクエストを処理して、読み取りキャパシティを増やすことができます。MongoDB は、さまざまなシナリオに対応するために、いくつかの特殊なタイプのセカンダリーをサポートしています。
-
アービター
アービターは選出で投票のみを行います。プライマリーになることはできず、データも保存しません。
2 ノードのレプリカセットでは、どちらかのノードがダウンすると、プライマリーを選出できません。アービターを追加すると、データ保持ノードが 1 つ利用できなくても、選出を成功させることができます。
アービターは軽量 (データストレージなし) であるため、偶数個のメンバーで構成されるレプリカセットに最適です。
-
Priority0
優先度が 0 のノードは、プライマリーとして選出されません。
たとえば、マルチデータセンター構成では、データセンター B のメンバーの優先度を 0 に設定して、プライマリーが常にデータセンター A にとどまるようにします。
説明ノードの過半数がデータセンター A に存在する必要があります。そうでない場合、ネットワークパーティションの発生時にプライマリーを選出できません。
-
Vote 0
MongoDB 3.0 では、レプリカセットは最大 50 のメンバーをサポートしますが、投票できるのは 7 つまでです。投票権のないメンバー (Vote 0) は votes プロパティを 0 に設定する必要があります。
-
隠れメンバー
隠れメンバーは優先度が 0 で、ドライバーからは見えません。
隠れメンバーはクライアントのリクエストを処理しないため、データバックアップやオフラインコンピューティングに最適です。
-
遅延メンバー
遅延メンバーは隠れメンバーの一種で、そのデータは secondaryDelaySecs プロパティで設定された期間 (たとえば 1 時間) だけプライマリーより遅延します。
遅延メンバーを使用することで、誤ったデータがプライマリーに書き込まれた場合にポイントインタイムリカバリが可能になります。
プライマリーの再選出
初期化後、次のようなシナリオでプライマリーの再選出が行われます。
-
レプリカセットの再設定
再選出は、セカンダリーがプライマリーのダウンを検出したとき、またはプライマリーが自発的に降格したときにトリガーされます。結果は、ハートビート、優先度、および最新の optime に依存します。
-
ノードの優先度
ノードは、最も優先度の高い候補に投票します。優先度 0 のノードは、選出を開始しません。プライマリーは、自身より優先度が高く、かつデータ遅延が 10 秒未満のセカンダリーを検出すると、そのセカンダリーに役割を譲るために降格します。
-
Optime
最新の optime (最新の oplog エントリのタイムスタンプ) を持つノードのみがプライマリーとして選出されます。
-
-
ネットワークパーティション
ノードは、投票ノードの過半数に接続している場合にのみプライマリーになることができます。プライマリーが過半数との接続を失った場合、セカンダリーに降格します。ネットワークパーティションの発生中、複数のプライマリーが一時的に共存する可能性があります。書き込み確認を majority に設定することで、1 つのプライマリーのみが書き込みを正常に完了できるように保証します。
データ同期
プライマリーからセカンダリーへのデータ同期には oplog が使用されます。プライマリーでの各書き込みは、local.oplog.rs コレクションにエントリを作成します。セカンダリーは、新しい oplog エントリを継続的に取得して適用します。
local.oplog.rs コレクションはキャップ付きコレクションです。サイズ制限に達すると、最も古いエントリが削除されます。oplog エントリはべき等です。つまり、セカンダリーで複数回適用される可能性があるため、操作を再適用しても同じ結果が生成されます。
oplog エントリのフォーマットは次のとおりです。
{
"ts" : Timestamp(1446011584, 2),
"h" : NumberLong("1687359108795812092"),
"v" : 2,
"op" : "i",
"ns" : "test.nosql",
"o" : { "_id" : ObjectId("563062c0b085733f34ab4129"), "name" : "mongodb", "score" : "100" }
}
フィールド:
-
ts:操作時間。現在の UNIX タイムスタンプにカウンターを加えたものです。カウンターは 1 秒ごとにリセットされます。
-
h:操作のグローバルに一意な識別子。
-
v:oplog のバージョン情報。
-
op:操作の種類。有効な値は次のとおりです。
-
i:挿入操作。
-
u:更新操作。
-
d:削除操作。
-
c:createDatabase や dropDatabase などのコマンドの実行。
-
n:ヌル操作。特別な目的で使用されます。
-
-
ns:操作対象のコレクション。
-
o:操作の内容。
-
o2:操作のクエリ条件。このフィールドは更新操作にのみ含まれます。
セカンダリは、最初の参加時に初期同期 (init sync) を実行し、プライマリまたはより最新のセカンダリから完全なデータセットをコピーします。その後、プライマリノードの local.oplog.rs コレクションから tailable cursor を使用して、新しい oplog エントリを継続的にフェッチし、適用します。
init sync プロセスは次のとおりです。
-
T1 で、セカンダリは
listDatabases、listCollections、およびcloneCollectionを使用して、プライマリから (localを除く) すべてのデータベースをコピーします。すべての操作は T2 で完了するものとします。 -
セカンダリーは、T1 から T2 の間に生成されたすべての oplog エントリを適用します。一部は手順 1 と重複する可能性がありますが、oplog エントリはべき等であるため、再適用は安全です。
-
セカンダリーは、プライマリーのインデックス設定に基づいてインデックスを作成します。各コレクションの
_idインデックスは、手順 1 ですでに作成されています。説明oplog のサイズは、データベースのサイズと書き込み量に基づいて決定してください。oplog が大きすぎるとストレージ領域が無駄になり、小さすぎると
init syncが完了しない可能性があります。これは、データベースが大きい場合に、oplog が T1 から T2 までのすべてのエントリを保持できず、同期が失敗する可能性があるためです。
レプリカセット設定の変更
メンバーを追加または削除したり、priority、vote、hidden、delayed などのプロパティを変更したりするには、 replSetReconfig または rs.reconfig() を使用します。
たとえば、2 番目のメンバーの優先度を 2 に設定するには、次のようにします。
cfg = rs.conf();
cfg.members[1].priority = 2;
rs.reconfig(cfg);
エラー処理 (ロールバック)
プライマリーが未同期のデータを保持したままダウンし、古いプライマリーが再接続する前に新しいプライマリーで書き込みが発生した場合、古いプライマリーは未同期の操作をロールバックして、新しいプライマリーのデータセットと一致させます。
ロールバックされたデータはロールバックディレクトリに保存され、管理者は必要に応じて mongorestore を使用して復元できます。
読み取り設定と書き込み懸念
-
読み取り設定
デフォルトでは、すべての読み取りはプライマリーで行われます。他のノードに読み取りをルーティングするには、ドライバーで読み取り設定を構成します。
-
primary:デフォルトのモード。すべての読み取りはプライマリーで行われます。
-
primaryPreferred:プライマリーから読み取ります。プライマリーにアクセスできない場合は、セカンダリーにフォールバックします。
-
secondary:すべての読み取りはセカンダリーで行われます。
-
secondaryPreferred:セカンダリーから読み取ります。すべてのセカンダリーにアクセスできない場合は、プライマリーにフォールバックします。
-
nearest:
pingレイテンシーによって決定される、到達可能な最も近いノードから読み取ります。
-
-
書き込み懸念
デフォルトでは、プライマリーは書き込みが完了した後に応答を返します。書き込み成功のルールを定義するには、ドライバーでWrite Concernを構成します。
次の例では、5 秒以内にノードの過半数で書き込みが成功する必要があります。
db.products.insert( { item: "envelopes", qty : 100, type: "Clasp" }, { writeConcern: { w: "majority", wtimeout: 5000 } } )上記の方法は単一のリクエストに適用されます。レプリカセット全体にデフォルトの書き込み懸念を設定するには、次のようにします。
cfg = rs.conf() cfg.settings = {} cfg.settings.getLastErrorDefaults = { w: "majority", wtimeout: 5000 } rs.reconfig(cfg)