シャーディングは、分散環境内の複数の MongoDB インスタンスにわたって、大規模なデータ セットを小さなデータ セットのチャンクに分割するプロセスです。

シャーディングとは何ですか?
MongoDB シャーディングは、単一サーバーに大量のデータを保存するのではなく、多数のサーバー間で大量のデータを保存するためのスケーラブルなソリューションを提供します。
実際には、指数関数的に増加するデータを 1 台のマシンに保存することは現実的ではありません。単一サーバーに保存されている大量のデータをクエリすると、リソース使用率が高くなり、満足のいく読み取りおよび書き込みスループットが得られない可能性があります。
基本的に、システムで増大するデータを処理するために存在するスケーリング方法には 2 種類あります。
- 垂直
- 水平
垂直スケーリングは 、より強力なプロセッサを追加したり、RAM をアップグレードしたり、システムにディスク領域を追加したりすることで、単一サーバーのパフォーマンスを向上させます。ただし、既存のテクノロジーとハードウェア構成を使用した実際の使用例では、垂直スケーリングを適用する可能性のある影響があります。
水平スケーリングは サーバーを追加して機能し、複数のサーバーに負荷を分散します。各マシンはデータセット全体のサブセットを処理するため、ハイエンドのハードウェアを導入するよりも効率が高く、コスト効率の高いソリューションが提供されます。ただし、多数のサーバーを備えた複雑なインフラストラクチャの追加のメンテナンスが必要になります。
Mongo DB シャーディングは、水平スケーリング技術に基づいて機能します。

コンポーネントのシャーディング
MongoDB でシャーディングを実現するには、次のコンポーネントが必要です。
Shard は 、元のデータのサブセットを処理する Mongo インスタンスです。シャードはレプリカ セットにデプロイする必要があります。
Mongos は Mongo インスタンスであり、クライアント アプリケーションとシャード クラスター間のインターフェイスとして機能します。シャードへのクエリルーターとして機能します。
Config Server は、 クラスターのメタデータ情報と構成の詳細を保存する Mongo インスタンスです。 MongoDB では、構成サーバーをレプリカ セットとしてデプロイする必要があります。

シャーディングアーキテクチャ
MongoDB クラスターは、多数のレプリカ セットで構成されます。
各レプリカ セットは、少なくとも 3 つ以上の mongo インスタンスで構成されます。シャードクラスターは複数の mongo シャードインスタンスで構成され、各シャードインスタンスはシャードレプリカセット内で動作します。アプリケーションは Mongos と対話し、 Mongos は シャード と通信します。したがって、シャーディングでは、アプリケーションがシャード ノードと直接対話することはありません。クエリ ルーターは、シャード キーに基づいてデータのサブセットをシャード ノード間で分散します。

シャーディングの実装
シャーディングするには以下の手順に従ってください
ステップ1
- レプリカ セットで 構成サーバー を起動し、それらの間のレプリケーションを有効にします。
mongod --configsvr --port 27019 --replSet rs0 --dbpath C:\data\data1 --bind_ip localhost
mongod --configsvr --port 27018 --replSet rs0 --dbpath C:\data\data2 --bind_ip localhost
mongod --configsvr --port 27017 --replSet rs0 --dbpath C:\data\data3 --bind_ip localhost
ステップ2
- いずれかの構成サーバー上でレプリカ セットを初期化します。
rs.initiate( { _id : "rs0", configsvr: true, members: [ { _id: 0, host: "IP:27017" }, { _id: 1, host: "IP:27018" }, { _id: 2, host: "IP:27019" } ] })
rs.initiate( { _id : "rs0", configsvr: true, members: [ { _id: 0, host: "IP:27017" }, { _id: 1, host: "IP:27018" }, { _id: 2, host: "IP:27019" } ] })
{
"ok" : 1,
"$gleStats" : {
"lastOpTime" : Timestamp(1593569257, 1),
"electionId" : ObjectId("000000000000000000000000")
},
"lastCommittedOpTime" : Timestamp(0, 0),
"$clusterTime" : {
"clusterTime" : Timestamp(1593569257, 1),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
},
"operationTime" : Timestamp(1593569257, 1)
}
ステップ3
- レプリカ セットで サーバーのシャーディング を開始し、サーバー間のレプリケーションを有効にします。
mongod --shardsvr --port 27020 --replSet rs1 --dbpath C:\data\data4 --bind_ip localhost
mongod --shardsvr --port 27021 --replSet rs1 --dbpath C:\data\data5 --bind_ip localhost
mongod --shardsvr --port 27022 --replSet rs1 --dbpath C:\data\data6 --bind_ip localhost
MongoDB は最初のシャーディング サーバーをプライマリとして初期化し、プライマリ シャーディング サーバーを移動するには、 movePrimary メソッドを使用します。
ステップ4
- シャード化されたサーバーの 1 つでレプリカ セットを初期化します。
rs.initiate( { _id : "rs0", members: [ { _id: 0, host: "IP:27020" }, { _id: 1, host: "IP:27021" }, { _id: 2, host: "IP:27022" } ] })
rs.initiate( { _id : "rs0", members: [ { _id: 0, host: "IP:27020" }, { _id: 1, host: "IP:27021" }, { _id: 2, host: "IP:27022" } ] })
{
"ok" : 1,
"$clusterTime" : {
"clusterTime" : Timestamp(1593569748, 1),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
},
"operationTime" : Timestamp(1593569748, 1)
}
ステップ5
- シャードクラスターの mangos を開始する
mongos --port 40000 --configdb rs0/localhost:27019,localhost:27018, localhost:27017
ステップ6
- mongoルートサーバー に接続する
mongo --port 40000
- 次に、シャーディング サーバーを追加します。
sh.addShard( "rs1/localhost:27020,localhost:27021,localhost:27022")
sh.addShard( "rs1/localhost:27020,localhost:27021,localhost:27022")
{
"shardAdded" : "rs1",
"ok" : 1,
"operationTime" : Timestamp(1593570212, 2),
"$clusterTime" : {
"clusterTime" : Timestamp(1593570212, 2),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
}
ステップ7
- mongo シェルでは、DB とコレクションのシャーディングを有効にします。
- DBでシャーディングを有効にする
sh.enableSharding("geekFlareDB")
sh.enableSharding("geekFlareDB")
{
"ok" : 1,
"operationTime" : Timestamp(1591630612, 1),
"$clusterTime" : {
"clusterTime" : Timestamp(1591630612, 1),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
}
ステップ8
- シャードするには、コレクションのシャード キー (この記事で後述) が必要です。
構文
:
sh.shardCollection("dbName.collectionName", { "key" : 1 } )<br>
sh.shardCollection("geekFlareDB.geekFlareCollection", { "key" : 1 } )
{
"collectionsharded" : "geekFlareDB.geekFlareCollection",
"collectionUUID" : UUID("0d024925-e46c-472a-bf1a-13a8967e97c1"),
"ok" : 1,
"operationTime" : Timestamp(1593570389, 3),
"$clusterTime" : {
"clusterTime" : Timestamp(1593570389, 3),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
}
コレクションが存在しない場合は、次のように作成して ください 。
db.createCollection("geekFlareCollection")
{
"ok" : 1,
"operationTime" : Timestamp(1593570344, 4),
"$clusterTime" : {
"clusterTime" : Timestamp(1593570344, 5),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
}
ステップ9
コレクションにデータを挿入します。 Mongo ログが増加し始め、バランサーが動作し、シャード間でデータのバランスをとろうとしていることを示します。
ステップ10
最後のステップは、シャーディングのステータスを確認することです。ステータスは、Mongos ルート ノードで以下のコマンドを実行することで確認できます。
シャーディングステータス
mongo ルート ノードで以下のコマンドを実行して、シャーディング ステータスを確認します。
sh.status()
mongos> sh.status()
--- Sharding Status ---
sharding version: {
"_id" : 1,
"minCompatibleVersion" : 5,
"currentVersion" : 6,
"clusterId" : ObjectId("5ede66c22c3262378c706d21")
}
shards:
{ "_id" : "rs1", "host" : "rs1/localhost:27020,localhost:27021,localhost:27022", "state" : 1 }
active mongoses:
"4.2.7" : 1
autosplit:
Currently enabled: yes
balancer:
Currently enabled: yes
Currently running: no
Failed balancer rounds in last 5 attempts: 5
Last reported error: Could not find host matching read preference { mode: "primary" } for set rs1
Time of Reported error: Tue Jun 09 2020 15:25:03 GMT+0530 (India Standard Time)
Migration Results for the last 24 hours:
No recent migrations
databases:
{ "_id" : "config", "primary" : "config", "partitioned" : true }
config.system.sessions
shard key: { "_id" : 1 }
unique: false
balancing: true
chunks:
rs1 1024
too many chunks to print, use verbose if you want to force print
{ "_id" : "geekFlareDB", "primary" : "rs1", "partitioned" : true, "version" : { "uuid" : UUID("a770da01-1900-401e-9f34-35ce595a5d54"), "lastMod" : 1 } }
geekFlareDB.geekFlareCol
shard key: { "key" : 1 }
unique: false
balancing: true
chunks:
rs1 1
{ "key" : { "$minKey" : 1 } } -->> { "key" : { "$maxKey" : 1 } } on : rs1 Timestamp(1, 0)
geekFlareDB.geekFlareCollection
shard key: { "product" : 1 }
unique: false
balancing: true
chunks:
rs1 1
{ "product" : { "$minKey" : 1 } } -->> { "product" : { "$maxKey" : 1 } } on : rs1 Timestamp(1, 0)
{ "_id" : "test", "primary" : "rs1", "partitioned" : false, "version" : { "uuid" : UUID("fbc00f03-b5b5-4d13-9d09-259d7fdb7289"), "lastMod" : 1 } }
mongos>

データ配信
Mongos ルーターは、シャード キーに基づいてシャード間で負荷を分散し、データを均等に分散します。バランサーが作動します。
シャード間でデータを分散するための主要なコンポーネントは次のとおりです。
-
バランサーは、
シャード化されたノード間でデータのサブセットのバランスを取る役割を果たします。 Mongos サーバーがシャード間で負荷の分散を開始すると、バランサーが実行されます。開始すると、Balancer はデータをより均等に分散しました。バランサーの状態を確認するには
<strong>sh.status()</strong>
、sh.getBalancerState()
、または<code class="language-markup">sh.isBalancerRunning()
実行します。
mongos> sh.isBalancerRunning()
true
mongos>
または
mongos> sh.getBalancerState()
true
mongos>
データを挿入した後、特定のシャードの一部のチャンクを移動していることなどを示す Mongos デーモンのアクティビティに気づくことができます。つまり、バランサーがシャード間でデータのバランスをとろうと動作中です。バランサーを実行すると、パフォーマンスの問題が発生する可能性があります。したがって、特定の バランサー ウィンドウ 内でバランサーを実行することをお勧めします。
mongos> sh.status()
--- Sharding Status ---
sharding version: {
"_id" : 1,
"minCompatibleVersion" : 5,
"currentVersion" : 6,
"clusterId" : ObjectId("5efbeff98a8bbb2d27231674")
}
shards:
{ "_id" : "rs1", "host" : "rs1/127.0.0.1:27020,127.0.0.1:27021,127.0.0.1:27022", "state" : 1 }
{ "_id" : "rs2", "host" : "rs2/127.0.0.1:27023,127.0.0.1:27024,127.0.0.1:27025", "state" : 1 }
active mongoses:
"4.2.7" : 1
autosplit:
Currently enabled: yes
balancer:
Currently enabled: yes
Currently running: yes
Failed balancer rounds in last 5 attempts: 5
Last reported error: Could not find host matching read preference { mode: "primary" } for set rs2
Time of Reported error: Wed Jul 01 2020 14:39:59 GMT+0530 (India Standard Time)
Migration Results for the last 24 hours:
1024 : Success
databases:
{ "_id" : "config", "primary" : "config", "partitioned" : true }
config.system.sessions
shard key: { "_id" : 1 }
unique: false
balancing: true
chunks:
rs2 1024
too many chunks to print, use verbose if you want to force print
{ "_id" : "geekFlareDB", "primary" : "rs2", "partitioned" : true, "version" : { "uuid" : UUID("a8b8dc5c-85b0-4481-bda1-00e53f6f35cd"), "lastMod" : 1 } }
geekFlareDB.geekFlareCollection
shard key: { "key" : 1 }
unique: false
balancing: true
chunks:
rs2 1
{ "key" : { "$minKey" : 1 } } -->> { "key" : { "$maxKey" : 1 } } on : rs2 Timestamp(1, 0)
{ "_id" : "test", "primary" : "rs2", "partitioned" : false, "version" : { "uuid" : UUID("a28d7504-1596-460e-9e09-0bdc6450028f"), "lastMod" : 1 } }
mongos>
- シャード キーは、 シャード コレクションのドキュメントをシャード間で分散するためのロジックを決定します。シャード キーは、挿入されるコレクションのすべてのドキュメントに存在する必要があるインデックス付きフィールドまたはインデックス付き複合フィールドにすることができます。データはチャンクに分割され、各チャンクは範囲ベースのシャード キーに関連付けられます。範囲クエリに基づいて、ルーターはどのシャードにチャンクを保存するかを決定します。
シャード キーは、 次の 5 つのプロパティを考慮して選択できます。
- カーディナリティ
- 書き込み分散
- 読み取り分布
- ターゲティングを読む
- 地域性を読み取る
理想的なシャード キーにより、MongoDB はすべてのシャード間で負荷を均等に分散します。適切なシャード キーを選択することは非常に重要です。
シャードノードの削除
クラスターからシャードを削除する前に、ユーザーは残りのシャードにデータが安全に移行されることを確認する必要があります。 MongoDB は、必要なシャード ノードを削除する前に、他のシャード ノードへのデータの安全なドレインを処理します。
以下のコマンドを実行して、必要なシャードを削除します。
ステップ1
まず、削除するシャードのホスト名を決定する必要があります。以下のコマンドは、クラスター内に存在するすべてのシャードとシャードの状態をリストします。
db.adminCommand( { listShards: 1 } )
mongos> db.adminCommand( { listShards: 1 } )
{
"shards" : [
{
"_id" : "rs1",
"host" : "rs1/127.0.0.1:27020,127.0.0.1:27021,127.0.0.1:27022",
"state" : 1
},
{
"_id" : "rs2",
"host" : "rs2/127.0.0.1:27023,127.0.0.1:27024,127.0.0.1:27025",
"state" : 1
}
],
"ok" : 1,
"operationTime" : Timestamp(1593572866, 15),
"$clusterTime" : {
"clusterTime" : Timestamp(1593572866, 15),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
}
ステップ2
以下のコマンドを発行して、クラスターから必要なシャードを削除します。発行されると、バランサーはドレイン シャード ノードからのチャンクの削除を処理し、残りのシャード ノード間での残りのチャンクの分散のバランスをとります。
db.adminCommand( { removeShard: "shardedReplicaNodes" } )
mongos> db.adminCommand( { removeShard: "rs1/127.0.0.1:27020,127.0.0.1:27021,127.0.0.1:27022" } )
{
"msg" : "draining started successfully",
"state" : "started",
"shard" : "rs1",
"note" : "you need to drop or movePrimary these databases",
"dbsToMove" : [ ],
"ok" : 1,
"operationTime" : Timestamp(1593572385, 2),
"$clusterTime" : {
"clusterTime" : Timestamp(1593572385, 2),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
}
ステップ3
ドレインシャードのステータスを確認するには、同じコマンドを再度実行します。
db.adminCommand( { removeShard: "rs1/127.0.0.1:27020,127.0.0.1:27021,127.0.0.1:27022" } )
データの排出が完了するまで待つ必要があります。 msg フィールド と state フィールドには、次のようにデータの排出が完了したかどうかが表示されます。
"msg" : "draining ongoing",
"state" : "ongoing",
コマンド
sh.status()
を使用してステータスを確認することもできます。削除されたシャード ノードは出力に反映されません。ただし、ドレインが進行中の場合、シャード ノードのドレイン ステータスは true になります。
ステップ4
必要なシャードが完全に削除されるまで、上記と同じコマンドを使用してドレインのステータスを確認し続けます。
完了すると、コマンドの出力には完了したというメッセージと状態が反映されます。
"msg" : "removeshard completed successfully",
"state" : "completed",
"shard" : "rs1",
"ok" : 1,
ステップ5
最後に、クラスター内の残りのシャードを確認する必要があります。ステータスを確認するには、
<strong>sh.status()</strong>
または
<strong>db.adminCommand( { listShards: 1 } )</strong>
を入力します。
mongos> db.adminCommand( { listShards: 1 } )
{
"shards" : [
{
"_id" : "rs2",
"host" : "rs2/127.0.0.1:27023,127.0.0.1:27024,127.0.0.1:27025",
"state" : 1
}
],
"ok" : 1,
"operationTime" : Timestamp(1593575215, 3),
"$clusterTime" : {
"clusterTime" : Timestamp(1593575215, 3),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
}
ここでは、削除されたシャードがシャードのリストに存在していないことがわかります。
レプリケーションに対するシャーディングの利点
- レプリケーションでは、プライマリ ノードがすべての書き込み操作を処理しますが、セカンダリ サーバーはバックアップ コピーを維持したり、読み取り専用操作を実行したりする必要があります。ただし、レプリカ セットを使用したシャーディングでは、負荷が多数のサーバー間で分散されます。
- 1 つのレプリカ セットは 12 ノードに制限されていますが、シャードの数には制限はありません。
- レプリケーションには、大規模なデータセットを処理するためのハイエンド ハードウェアまたはバーティクル スケーリングが必要ですが、シャーディングでサーバーを追加する場合と比較してコストが高すぎます。
- レプリケーションでは、スレーブ/セカンダリ サーバーを追加することで読み取りパフォーマンスを向上できますが、シャーディングでは、シャード ノードを追加することで読み取りと書き込みの両方のパフォーマンスが向上します。
シャーディングの制限
- シャードクラスターは、一意のインデックスに完全なシャードキーがプレフィックスとして付けられるまで、シャード全体にわたる一意のインデックス作成をサポートしません。
- 1 つまたは複数のドキュメントに対するシャード コレクションのすべての更新操作には、クエリにシャード キーまたは _id フィールドが含まれている必要があります。
- コレクションのサイズが指定されたしきい値を超えない場合、コレクションをシャーディングできます。このしきい値は、すべてのシャード キーの平均サイズと構成されたチャンク サイズに基づいて推定できます。
- シャーディングは、最大コレクション サイズまたは分割数の運用制限で構成されます。
- 間違ったシャード キーを選択すると、パフォーマンスに影響が生じます。
結論
MongoDB は、パフォーマンスを損なうことなく大規模なデータベースを実装するための組み込みシャーディングを提供します。上記が MongoDB シャーディングのセットアップに役立つことを願っています。次に、一般的に使用される MongoDB コマンドのいくつかに慣れるとよいでしょう。