sql >> データベース >  >> NoSQL >> MongoDB

MongoDBを使用してGUIDベースのシャードキーをプログラムで事前分割する方法

    初期データサイズ(120GB)と、MongoDBのデフォルトの最大チャンクサイズは64MBであることがわかっています。 64MBを120GBに分割すると、1920になります。これが、最初に検討する必要のあるチャンクの最小数です。たまたま2048は16の累乗を2で割ったものであり、GUID(シャードキー)が16進数ベースであることを考えると、1920よりもはるかに扱いやすい数値です(以下を参照)。

    注: この事前分割は、前に実行する必要があります。 すべてのデータがコレクションに追加されます。データを含むコレクションでenableSharding()コマンドを使用すると、MongoDBはデータ自体を分割し、チャンクが既に存在するときにこれを実行します。これにより、チャンクの分散がかなり奇妙になる可能性があるため、注意してください。

    この回答の目的のために、データベースがusersと呼ばれると仮定しましょう。 コレクションはuserInfoと呼ばれます 。また、GUIDが_idに書き込まれると仮定しましょう。 分野。これらのパラメータを使用して、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つ行う必要があります。インポート中は、バランサーと自動分割をオフにします。

    >
    • すべてのバランシングを無効にするには、mongosから次のコマンドを実行します:sh.stopBalancer()
    • 他のバランシング操作を実行したままにする場合は、特定のコレクションを無効にすることができます。上記の名前空間を例として使用します:sh.disableBalancing("users.userInfo")
    • 読み込み中に自動分割をオフにするには、各mongosを再起動する必要があります --noAutoSplitを使用してデータをロードするために使用します オプション。

    インポートが完了したら、必要に応じて手順を逆に実行します(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 } } ); 
    } 
    

    これが今の考え方です-すべてshard0001に4つのチャンクがあります:

    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つを移動します。これはプログラムで行うことができますが、チャンクが最初に存在する場所、シャードにどのように名前を付けたかなどによって異なります。このマニュアルは今のところ残しておきます。それほど面倒ではありません。3moveChunk コマンド:

    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    
    

    そして、あなたはそれを持っています-バランサーを待つ必要はありません、配布はすでに均等です。




    1. mongodbで重複するURLを検索する

    2. django:redis:CommandError:サーバーの実行に必要なASGI_APPLICATIONを設定していません

    3. MongoDBがシェルでデータベースを作成しない

    4. データの複数のバージョンをRedisキャッシュに保存する