初期データ サイズ (120GB) はわかっており、MongoDB のデフォルトの最大チャンク サイズは 64MBです。64MB を 120GB に分割すると、1920 になります。これが、最初に調べる必要があるチャンクの最小数です。たまたま 2048 は 16 のべき乗を 2 で割ったものであり、GUID (シャード キー) が 16 進数ベースであることを考えると、1920 よりもはるかに扱いやすい数値です (以下を参照)。
注:この事前分割は、データをコレクションに追加する前に行う必要があります。データを含むコレクションで enableSharding() コマンドを使用すると、MongoDB はデータ自体を分割し、チャンクが既に存在するときにこれを実行することになります。これにより、非常に奇妙なチャンク分散が発生する可能性があるため、注意してください。
この回答の目的のために、データベースが呼び出されusers
、コレクションが呼び出されると仮定しましょうuserInfo
。_id
また、GUID がフィールドに書き込まれると仮定しましょう。これらのパラメーターを使用して、に接続しmongos
、次のコマンドを実行します。
// first switch to the users DB
use users;
// now enable sharding for the users DB
sh.enableSharding("users");
// enable sharding on the relevant collection
sh.shardCollection("users.userInfo", {"_id" : 1});
// finally, disable the balancer (see below for options on a per-collection basis)
// this prevents migrations from kicking off and interfering with the splits by competing for meta data locks
sh.stopBalancer();
ここで、上記の計算に従って、GUID 範囲を 2048 チャンクに分割する必要があります。そのためには、少なくとも 3 桁の 16 進数 (16 ^ 3 = 4096) が必要であり、それらを範囲の最上位桁 (つまり、左端の 3 桁) に配置します。繰り返しますが、これはmongos
シェルから実行する必要があります
// Simply use a for loop for each digit
for ( var x=0; x < 16; x++ ){
for( var y=0; y<16; y++ ) {
// for the innermost loop we will increment by 2 to get 2048 total iterations
// make this z++ for 4096 - that would give ~30MB chunks based on the original figures
for ( var z=0; z<16; z+=2 ) {
// now construct the GUID with zeroes for padding - handily the toString method takes an argument to specify the base
var prefix = "" + x.toString(16) + y.toString(16) + z.toString(16) + "00000000000000000000000000000";
// finally, use the split command to create the appropriate chunk
db.adminCommand( { split : "users.userInfo" , middle : { _id : prefix } } );
}
}
}
それが完了したら、sh.status()
ヘルパーを使用してプレイの状態を確認しましょう。
mongos> sh.status()
--- Sharding Status ---
sharding version: {
"_id" : 1,
"version" : 3,
"minCompatibleVersion" : 3,
"currentVersion" : 4,
"clusterId" : ObjectId("527056b8f6985e1bcce4c4cb")
}
shards:
{ "_id" : "shard0000", "host" : "localhost:30000" }
{ "_id" : "shard0001", "host" : "localhost:30001" }
{ "_id" : "shard0002", "host" : "localhost:30002" }
{ "_id" : "shard0003", "host" : "localhost:30003" }
databases:
{ "_id" : "admin", "partitioned" : false, "primary" : "config" }
{ "_id" : "users", "partitioned" : true, "primary" : "shard0001" }
users.userInfo
shard key: { "_id" : 1 }
chunks:
shard0001 2049
too many chunks to print, use verbose if you want to force print
2048 個のチャンク (および最小/最大チャンクのおかげで 1 つ余分に) がありますが、バランサーがオフになっているため、それらはすべて元のシャードに残っています。それでは、バランサーを再度有効にしましょう。
sh.startBalancer();
これはすぐにバランスを取り始め、すべてのチャンクが空であるため比較的高速ですが、まだ少し時間がかかります (他のコレクションからの移行と競合している場合は、はるかに遅くなります)。しばらくしてからsh.status()
再度実行すると、2048 個のチャンクがすべて 4 つのシャードに分割され、初期データ ロードの準備が整います。
mongos> sh.status()
--- Sharding Status ---
sharding version: {
"_id" : 1,
"version" : 3,
"minCompatibleVersion" : 3,
"currentVersion" : 4,
"clusterId" : ObjectId("527056b8f6985e1bcce4c4cb")
}
shards:
{ "_id" : "shard0000", "host" : "localhost:30000" }
{ "_id" : "shard0001", "host" : "localhost:30001" }
{ "_id" : "shard0002", "host" : "localhost:30002" }
{ "_id" : "shard0003", "host" : "localhost:30003" }
databases:
{ "_id" : "admin", "partitioned" : false, "primary" : "config" }
{ "_id" : "users", "partitioned" : true, "primary" : "shard0001" }
users.userInfo
shard key: { "_id" : 1 }
chunks:
shard0000 512
shard0002 512
shard0003 512
shard0001 513
too many chunks to print, use verbose if you want to force print
{ "_id" : "test", "partitioned" : false, "primary" : "shard0002" }
これでデータの読み込みを開始する準備が整いましたが、データの読み込みが完了するまで分割や移行が発生しないことを完全に保証するには、もう 1 つ行う必要があります。インポート中はバランサーと自動分割をオフにします。
インポートが完了したら、必要に応じて手順を逆にして ( sh.startBalancer()
、 、なしsh.enableBalancing("users.userInfo")
で を再起動)、すべてを既定の設定に戻します。mongos
--noAutoSplit
**
更新: 速度の最適化
**
急いでいない場合は、上記の方法で問題ありません。現状では、これをテストするとわかるように、バランサーはそれほど高速ではありません - 空のチャンクでも。したがって、作成するチャンクの数を増やすと、バランスをとるのに時間がかかります。展開によって異なりますが、2048 個のチャンクのバランスを完了するのに 30 分以上かかることがわかりました。
テストや比較的静かなクラスターでは問題ないかもしれませんが、バランサーをオフにして他の更新を必要としないことは、忙しいクラスターでは確実に行うのがはるかに難しくなります. では、どうすれば高速化できるのでしょうか。
答えは、早い段階でいくつかの手動の移動を行い、チャンクがそれぞれのシャードに配置されたら分割することです。これは、特定のシャード キー (ランダムに分散された UUID など)、または特定のデータ アクセス パターンでのみ望ましいので、結果として不十分なデータ分散にならないように注意してください。
上記の例を使用すると、4 つのシャードがあるため、すべての分割を行ってからバランスを取るのではなく、代わりに 4 つに分割します。次に、手動で移動して各シャードに 1 つのチャンクを配置し、最後にそれらのチャンクを必要な数に分割します。
上記の例の範囲は次のようになります。
$min --> "40000000000000000000000000000000"
"40000000000000000000000000000000" --> "80000000000000000000000000000000"
"80000000000000000000000000000000" --> "c0000000000000000000000000000000"
"c0000000000000000000000000000000" --> $max
これらを作成するのは 4 つのコマンドだけですが、それがあるので、上記のループを単純化/変更された形式で再利用してみませんか?
for ( var x=4; x < 16; x+=4){
var prefix = "" + x.toString(16) + "0000000000000000000000000000000";
db.adminCommand( { split : "users.userInfo" , middle : { _id : prefix } } );
}
これが現在の thinks の外観です。4 つのチャンクがあり、すべて shard0001 にあります。
mongos> sh.status()
--- Sharding Status ---
sharding version: {
"_id" : 1,
"version" : 4,
"minCompatibleVersion" : 4,
"currentVersion" : 5,
"clusterId" : ObjectId("53467e59aea36af7b82a75c1")
}
shards:
{ "_id" : "shard0000", "host" : "localhost:30000" }
{ "_id" : "shard0001", "host" : "localhost:30001" }
{ "_id" : "shard0002", "host" : "localhost:30002" }
{ "_id" : "shard0003", "host" : "localhost:30003" }
databases:
{ "_id" : "admin", "partitioned" : false, "primary" : "config" }
{ "_id" : "test", "partitioned" : false, "primary" : "shard0001" }
{ "_id" : "users", "partitioned" : true, "primary" : "shard0001" }
users.userInfo
shard key: { "_id" : 1 }
chunks:
shard0001 4
{ "_id" : { "$minKey" : 1 } } -->> { "_id" : "40000000000000000000000000000000" } on : shard0001 Timestamp(1, 1)
{ "_id" : "40000000000000000000000000000000" } -->> { "_id" : "80000000000000000000000000000000" } on : shard0001 Timestamp(1, 3)
{ "_id" : "80000000000000000000000000000000" } -->> { "_id" : "c0000000000000000000000000000000" } on : shard0001 Timestamp(1, 5)
{ "_id" : "c0000000000000000000000000000000" } -->> { "_id" : { "$maxKey" : 1 } } on : shard0001 Timestamp(1, 6)
$min
チャンクはそのままにして、残りの 3 つを移動します。これはプログラムで行うことができますが、チャンクが最初に存在する場所、シャードにどのように名前を付けたかなどによって異なりmoveChunk
ます.
mongos> sh.moveChunk("users.userInfo", {"_id" : "40000000000000000000000000000000"}, "shard0000")
{ "millis" : 1091, "ok" : 1 }
mongos> sh.moveChunk("users.userInfo", {"_id" : "80000000000000000000000000000000"}, "shard0002")
{ "millis" : 1078, "ok" : 1 }
mongos> sh.moveChunk("users.userInfo", {"_id" : "c0000000000000000000000000000000"}, "shard0003")
{ "millis" : 1083, "ok" : 1 }
再確認して、チャンクが期待する場所にあることを確認しましょう。
mongos> sh.status()
--- Sharding Status ---
sharding version: {
"_id" : 1,
"version" : 4,
"minCompatibleVersion" : 4,
"currentVersion" : 5,
"clusterId" : ObjectId("53467e59aea36af7b82a75c1")
}
shards:
{ "_id" : "shard0000", "host" : "localhost:30000" }
{ "_id" : "shard0001", "host" : "localhost:30001" }
{ "_id" : "shard0002", "host" : "localhost:30002" }
{ "_id" : "shard0003", "host" : "localhost:30003" }
databases:
{ "_id" : "admin", "partitioned" : false, "primary" : "config" }
{ "_id" : "test", "partitioned" : false, "primary" : "shard0001" }
{ "_id" : "users", "partitioned" : true, "primary" : "shard0001" }
users.userInfo
shard key: { "_id" : 1 }
chunks:
shard0001 1
shard0000 1
shard0002 1
shard0003 1
{ "_id" : { "$minKey" : 1 } } -->> { "_id" : "40000000000000000000000000000000" } on : shard0001 Timestamp(4, 1)
{ "_id" : "40000000000000000000000000000000" } -->> { "_id" : "80000000000000000000000000000000" } on : shard0000 Timestamp(2, 0)
{ "_id" : "80000000000000000000000000000000" } -->> { "_id" : "c0000000000000000000000000000000" } on : shard0002 Timestamp(3, 0)
{ "_id" : "c0000000000000000000000000000000" } -->> { "_id" : { "$maxKey" : 1 } } on : shard0003 Timestamp(4, 0)
これは上記で提案した範囲と一致するため、すべて問題ないようです。上記の元のループを実行して、各シャードでそれらを「その場で」分割すると、ループが終了するとすぐにバランスの取れた分散が得られます。もう1つsh.status()
確認する必要があります。
mongos> for ( var x=0; x < 16; x++ ){
... for( var y=0; y<16; y++ ) {
... // for the innermost loop we will increment by 2 to get 2048 total iterations
... // make this z++ for 4096 - that would give ~30MB chunks based on the original figures
... for ( var z=0; z<16; z+=2 ) {
... // now construct the GUID with zeroes for padding - handily the toString method takes an argument to specify the base
... var prefix = "" + x.toString(16) + y.toString(16) + z.toString(16) + "00000000000000000000000000000";
... // finally, use the split command to create the appropriate chunk
... db.adminCommand( { split : "users.userInfo" , middle : { _id : prefix } } );
... }
... }
... }
{ "ok" : 1 }
mongos> sh.status()
--- Sharding Status ---
sharding version: {
"_id" : 1,
"version" : 4,
"minCompatibleVersion" : 4,
"currentVersion" : 5,
"clusterId" : ObjectId("53467e59aea36af7b82a75c1")
}
shards:
{ "_id" : "shard0000", "host" : "localhost:30000" }
{ "_id" : "shard0001", "host" : "localhost:30001" }
{ "_id" : "shard0002", "host" : "localhost:30002" }
{ "_id" : "shard0003", "host" : "localhost:30003" }
databases:
{ "_id" : "admin", "partitioned" : false, "primary" : "config" }
{ "_id" : "test", "partitioned" : false, "primary" : "shard0001" }
{ "_id" : "users", "partitioned" : true, "primary" : "shard0001" }
users.userInfo
shard key: { "_id" : 1 }
chunks:
shard0001 513
shard0000 512
shard0002 512
shard0003 512
too many chunks to print, use verbose if you want to force print
これで、バランサーを待つ必要はありません。分散はすでに均一になっています。