0

非常に高い書き込みスループットと妥当な読み取りスループットを必要とするデータベースをモデル化しようとしています。「イベント」データをデータベースに追加するシステムの分散セットがあります。

現在、イベントレコードのIDはGuidです。GUIDはランダムに分布しているため、最近のデータがディスクに分散し、ページングの問題が発生する可能性があるため、GUIDは優れたインデックスを作成する傾向がないことを読んでいます。

したがって、検証したい最初の仮定は次のとおりです。自動番号のようなものなど、適切なバランスの取れたツリーを作成する_idを選択したくないと仮定しています。最近の2つのイベントは基本的にディスク上で隣り合っているため、これは有益です。これは正しい仮定ですか?

(1)が正しいと仮定して、私はそのようなIDを生成するための最良の方法を見つけようとしています。MongoがObjectIdをネイティブにサポートしていることは知っています。これは、データをMongoに結び付けても問題ないアプリケーションに便利ですが、私のアプリケーションはそうではありません。データを生成するシステムは複数あるため、mongoはサーバー側で自動番号をサポートしていないため、「自動番号」フィールドのシミュレーションは少し問題があります。そのため、プロデューサーはIDを割り当てる必要があります。他のシステムが何をしているのかわからない。

これを解決するために、私が検討しているのは、_idフィールドを{localId、producerId}の複合キーにすることです。ここで、local idは、producerIdによって一意になるため、プロデューサーが生成できる自動番号です。ProducerIdは、プロデューサー間でネゴシエートして、一意のIDを考え出すことができるものです。

次の質問です。すべてのプロデューサーから最新のデータを取得することが目標の場合、localIdは右向きで、producerIdは小さなクラスターになるため、{localId、producerId}を優先キーの順序にする必要があります。最近の2つのイベントは互いにローカルのままであることが望ましいでしょう。その順序を逆にすると、ツリーが最終的にどのように見えるかについての私の推論は次のようになります。

               root
        /        |           \
       p0        p1          p2
       /         |            \
     e0..n      e0..n        e0..n

ここで、p#はプロデューサーID、e#はイベントです。これは、インデックスをデータのp#クラスターに断片化するようであり、新しいイベントは必ずしも互いに隣接しているとは限りません。優先順位に関する私の仮定は、代わりに次のようになります(確認してください)。

               root
      /          |          \
     e0          e1         e2
     /            |           \
  p0..n         p0..n        p0..n

最近の出来事を互いに近づけているようです。(MongoがインデックスにBツリーを使用していることは知っていますが、ここではビジュアルを単純化しようとしています)。

私が見ることができる{localId、producerId}の唯一の注意点は、ユーザーによる一般的なクエリは、プロデューサーごとに最新のイベントをリストすることであり、{producerId、localId}は実際にははるかにうまく処理できるということです。このクエリを{localId、producerId}で機能させるには、ドキュメントのフィールドとしてproducerIdも追加し、インデックスを作成する必要があると考えています。

ここでの私の質問が実際に何であるかを明確にするために、私がこの問題について正しく考えているかどうか、またはこれにアプローチするための明らかにより良い方法があるかどうかを知りたいです。

ありがとう

4

1 に答える 1

1

質問に答えるには:次のような複合語:{a、b}は、bでクエリしてから、aで並べ替えると、スキャッタークエリで終了します。ただし、ソートにはインデックスを使用します。

ObjectIdの代わりにDocumentを使用する場合、_idはインデックス付けされますが、使用されませんが、複合インデックスではありません。

例:

コレクション'a'にこのドキュメントがあり、追加のインデックスがない場合:

{ "_id" : { "e" : 1, "p" : 1 } }
{ "_id" : { "e" : 1, "p" : 2 } }
{ "_id" : { "e" : 2, "p" : 1 } }
{ "_id" : { "e" : 1, "p" : 3 } }
{ "_id" : { "e" : 2, "p" : 3 } }
{ "_id" : { "e" : 2, "p" : 2 } }
{ "_id" : { "e" : 3, "p" : 1 } }
{ "_id" : { "e" : 3, "p" : 2 } }
{ "_id" : { "e" : 3, "p" : 3 } }

このようなクエリ:

db.a.find({'_id.p' : 2}).sort({'_id.e' : 1}).explain()

インデックスを使用しません:

{
    "cursor" : "BasicCursor",
    "nscanned" : 9,
    "nscannedObjects" : 9,
    "n" : 3,
    "scanAndOrder" : true,
    "millis" : 0,
    "nYields" : 0,
    "nChunkSkips" : 0,
    "isMultiKey" : false,
    "indexOnly" : false,
    "indexBounds" : {   
    }
}

ドキュメントにインデックスが付けられているからです。

次のようなインデックスを作成する場合:

db.a.ensureIndex({'_id.e' : 1, '_id.p' : 1})

その後、再度クエリを実行します。

db.a.find({'_id.p' : 2}).sort({'_id.e' : 1}).explain()

{
    "cursor" : "BtreeCursor _id.e_1__id.p_1",
    "nscanned" : 9,
    "nscannedObjects" : 3,
    "n" : 3,
    "millis" : 0,
    "nYields" : 0,
    "nChunkSkips" : 0,
    "isMultiKey" : false,
    "indexOnly" : false,
    "indexBounds" : {
        "_id.e" : [
            [
                {
                    "$minElement" : 1
                },
                {
                    "$maxElement" : 1
                }
            ]
        ],
        "_id.p" : [
            [
                2,
                2
            ]
        ]
    }
}

並べ替えのためにインデックス(nscanned:9)を照会し、オブジェクト:3をフェッチします。これは、_idで並べ替えるよりも優れています(nscannedおよびnscannedObjectsは9になります)。

ドキュメント.explain()

したがって、書き込みスループットが高い場合(1秒あたり15kを超える書き込み)、おそらくシャードになります。オプションが設定されている場合、両方のインデックスは一意性を保証します。ただし、直接クエリに役立つのは複合シャードキーのみであり、スキャッターギャザーはありません。

シャードキーとして({'_ id.e':1、'_id.p':1})を使用すると、すべての "_id.e"クエリが直接ルーティングされますが、 "_ id.p"('e'なし)クエリはルーティングされません。これらのクエリはすべてのホストに送信され、そこでインデックスルックアップで終了しますが、同様に高速になる可能性があります(ネットワークなどによって異なります)。これらのクエリを「p」でクラスター化する場合は、次のように複合キーの最初の部分として「_id.p」を配置する必要があります。

{'_id.p' : 1, '_id.e' : 1}

したがって、すべての「p」クエリは直接クエリです。しかし、はい、これはクラスター全体に最近のイベントを分散させます。したがって、時間ベースのキーを使用する個別のインデックスは、これらのスキャッタークエリを高速化する可能性があります。

いくつかのサンプルデータを生成し、開発システムで2つのシャードを使用するセットアップでそれを試して、シャードキーとインデックスを選択するために.explain()を使用します。

于 2012-12-12T15:01:11.503 に答える